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

📄 unawave.pas

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

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

	  unaWave.pas
	  Voice Communicator components version 2.5
	  PCM wave 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, 2003 Lake of Soft, Ltd
		     All rights reserved
	----------------------------------------------

	  created by:
		Lake, 25 Mar 2002

	  modified by:
		Lake, Mar-Dec 2002
		Lake, Jan-Oct 2003
		Lake, Aug 2004

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

{$I unaDef.inc}

unit
  unaWave;

interface

{DP:UNIT
  Contains set of routines designed to work with PCM wave data.
}

uses
  unaTypes
{$IFDEF __SYSUTILS_H_ }
  , Math
{$ENDIF}
  ;

type
  // --  --
  punaPCMFormat = ^unaPCMFormat;
  unaPCMFormat = packed record
    //
    pcmSamplesPerSecond: unsigned;	// sampling rate
    pcmBitsPerSample: unsigned;		// bits per sample
    pcmNumChannels: unsigned;		// number of channels
  end;

  // --  --
  punaWaveFormat = ^unaWaveFormat;
  unaWaveFormat = packed record
    //
    formatTag: unsigned;		// format tag id. could be WAVE_FORMAT_PCM or anything else
    formatOriginal: unaPCMFormat;	// original PCM format
    formatUserData: unsigned;		// any additional data
  end;

  // --  --
  punaPCMChunk = ^unaPCMChunk;
  unaPCMChunk = record
    //
    chunkFormat: unaPCMFormat;	// chunk format
    chunkDataLen: unsigned;	// length of actual data in chunk
    chunkBufSize: unsigned;	// size of chunk buffer in bytes
    chunkData: pointer;		// chunk data
  end;


{DP:METHOD
  Mixes buf1 and buf2 and stores the result in buf3 (buf3 could be one of buf1 or buf2).
  <BR />Returns number of samples mixed.

  <BR />bitsN (N = 1, 2 or 3) specifies the number or bits in sample:
  <UL>
     <LI>8 = 1 byte per sample. Every sample contains PCM value in the following format:</LI>
     <TABLE CELLSPACING="0" BORDER="1">
	<TR><TD>Value</TD><TD>PCM value</TD></TR>
	<TR><TD>0x00</TD><TD>-128</TD></TR>
	<TR><TD COLSPAN="2">..</TD></TR>
	<TR><TD>0x7F</TD><TD>-1</TD></TR>
	<TR><TD>0x80</TD><TD>0</TD></TR>
	<TR><TD COLSPAN="2">..</TD></TR>
	<TR><TD>0xFF</TD><TD>+127</TD></TR>
     </TABLE>
     <BR /><BR />
     <LI>16 = 2 bytes per sample. Every sample contains PCM value in the following format:</LI>
     <TABLE CELLSPACING="0" BORDER="1">
	<TR><TD>Value</TD><TD>PCM value</TD></TR>
	<TR><TD>0x0000</TD><TD>0</TD></TR>
	<TR><TD COLSPAN="2">..</TD></TR>
	<TR><TD>0x7FFF</TD><TD>+32767</TD></TR>
	<TR><TD>0x8000</TD><TD>-32768</TD></TR>
	<TR><TD COLSPAN="2">..</TD></TR>
	<TR><TD>0xFFFF</TD><TD>-1</TD></TR>
     </TABLE>
     <BR /><BR />
     <LI>32 = 4 bytes per sample, signed integer. Every sample contains PCM value in the following format:</LI>
     <TABLE CELLSPACING="0" BORDER="1">
	<TR><TD>Value</TD><TD>PCM value</TD></TR>
	<TR><TD>0x00000000</TD><TD>0</TD></TR>
	<TR><TD COLSPAN="2">..</TD></TR>
	<TR><TD>0x7FFFFFFF</TD><TD>+2147483647</TD></TR>
	<TR><TD>0x80000000</TD><TD>-2147483648</TD></TR>
	<TR><TD COLSPAN="2">..</TD></TR>
	<TR><TD>0xFFFFFFFF</TD><TD>-1</TD></TR>
     </TABLE>
     <BR /><BR />
  </UL>
  <BR />Size of buffer #N in bytes = (bitsN * samples * numChannels) shr 3
  <BR />Mixing is done by adding (doAdd is true) or subtracting (doAdd is false) samples with clipping.
}
function waveMix(buf1, buf2, buf3: pointer; samples: unsigned; bits1, bits2, bits3: unsigned; doAdd: bool = true; numChannels: unsigned = 1): unsigned; overload;
function waveMix(const chunk1, chunk2, chunk3: unaPCMChunk; doAdd: bool = true): unsigned; overload;

{DP:METHOD
  Calculates volume. Calculation is done by adding squares of samples and then dividing result on number of samples and applying square root.
  <BR />buf is a pointer to PCM samples
  <BR />samples is a number of samples.
  <BR />bits should have the value as described in waveMix() routine
  <BR />For multi-channel streams specify the channel number you wish to get the power of and total number of channels in the stream.
  <BR />Size of buffer can be calculated as (samples * bits * numChannels) shr 3
  <BR />Resulting value is from 0 (silence) to 32768 (loudest possible sound).
  <BR />Assumption sound has "natural" source is in effect. That means you can get useless results for signals, that are not sound by nature (constant non-zero signal for example).
}
function waveGetVolume(buf: pointer; samples: unsigned; bits: unsigned; numChannels: unsigned = 1; channel: unsigned = 0; deltaMethod: bool = false): unsigned;

{DP:METHOD
  Returns Logarithmic volume.
  <BR />Input range is from 0 to 32768.
  <BR />Result range is from 0 to 300.
}
function waveGetLogVolume(volume: int): unsigned;

{DP:METHOD
  Extracts specified channel from PCM chunk.
  <BR />dest must be large enough to store required data (one channel).
  <BR />channel specifies which channel to extract (0, 1, 2... )
  <BR />Returns number of samples stored in dest buffer.
}
function waveExtractChannel(buf, dest: pointer; samples: unsigned; bits: unsigned; numChannels: unsigned = 1; channel: unsigned = 0): unsigned;

{DP:METHOD
  Replaces PCM data for specified channel with new one. Old data for this channel will be lost.
  <BR />channel specifies which channel to replace (0, 1, 2... )
  <BR />Returns number of samples stored in dest buffer.
}
function waveReplaceChannel(buf, source: pointer; samples: unsigned; bits: unsigned; numChannels: unsigned = 1; channel: unsigned = 0): unsigned; assembler;

{DP:METHOD
  Reverses PCM samples in wave chunk.
  <BR />bits specifies number of bits in sample.
  <BR />numChannels specifies number of channels in stream.
  <BR />Returns number of samples processed.
}
function waveReverse(buf: pointer; samples: unsigned; bits: unsigned; numChannels: unsigned): unsigned; assembler;

{DP:METHOD
  Changes PCM stream characteristics.
  <BR />Returns number of bytes produced by the function.
}
function waveResample(bufSrc, bufDst: pointer; samples, numChannelsSrc, numChannelsDst, bitsSrc, bitsDst, rateSrc, rateDst: unsigned): unsigned; overload;
{DP:METHOD
  Changes PCM stream characteristics.
  <BR />Returns number of bytes produced by the function.
}
function waveResample(const bufSrc: unaPCMChunk; var bufDst: unaPCMChunk): unsigned; overload;

{DP:METHOD
  Returns number of full samples which can be stored in given PCM chunk.
}
function waveGetChunkMaxSamplesCount(const chunk: unaPCMChunk): unsigned;
{DP:METHOD
  Returns current number of full samples which is stored in given PCM chunk.
}
function waveGetChunkCurSamplesCount(const chunk: unaPCMChunk): unsigned;

{DP:METHOD
  Reallocates PCM chunk to hold required number of samples.
  <BR />NOTE: chunk.chunkData must be a valid pointer (or nil) before calling this function.
  <BR />Returns number of bytes allocated.
}
function waveReallocateChunk(var chunk: unaPCMChunk; numSamples: unsigned = 0): unsigned;

{DP:METHOD
  Reads bytes from PCM chunk.
}
function waveReadFromChunk(var chunk: unaPCMChunk; buf: pointer; size: unsigned; bufOffs: unsigned = 0): unsigned;
{DP:METHOD
  Writes bytes to PCM chunk.
}
function waveWriteToChunk(var chunk: unaPCMChunk; buf: pointer; size: unsigned; bufOffs: unsigned = 0): unsigned;

{DP:METHOD
  Returns next gcd value.
}
function waveFindNextGcd(rate1, rate2: unsigned; startFrom: unsigned): unsigned;

{DP:METHOD
<PRE>
  volume:
     100 =  10% original volume
     500 =  50% original volume
    1000 = 100% original volume
    2000 = 200% original volume (two times louder)
      and so on
</PRE>
NOTE: scale is linear, for better results you need to use log scale instead.
}
function waveModifyVolume(volume: unsigned; buf: pointer; samples: unsigned; bits: unsigned = 1; numChannels: unsigned = 1; channel: int = -1): unsigned; overload;
function waveModifyVolume(volume: unsigned; const chunk: unaPCMChunk; channel: int): unsigned; overload;


implementation


uses
  unaUtils;

// -- --
procedure loadSample(); assembler;
{
	IN:	AL	- number of bits in sample (8, 16 or 32)
		ESI	- memory buffer pointer

	OUT:    EAX	- sample value (always 32 bit signed integer)

	AFFECTS:
		EAX
		ESI
}
asm

	cmp	al, 16
	je	@load_A16

	cmp	al, 8
	je	@load_A8

	cmp	al, 4
	je	@load_A4

  //@load_A32:
	lodsd			// eax = signed integer
	jmp	@exit

  @load_A4:
  	// not supported yet	
	jmp	@exit

  @load_A8:
	lodsb
	and	eax, 0FFh
	sub	eax, 080h
	sal	eax, 8		// eax = signed integer
	jmp	@exit

  @load_A16:
	lodsw
	cwde			// eax = signed integer
  @exit:
end;

// -- --
procedure saveSample(); assembler;
{
	IN:	BL	- 8, 16 or 32 bits in destination sample
		EAX	- sample value to store (always 32 bit signed integer)
		EDI	- memory buffer pointer

	OUT:    none

	AFFECTS:
		EAX
		EDI
}
asm
	push	ebx

	cmp	bl, 8
	je	@store_8

	cmp	bl, 16
	je	@store_16

	// store as 32 bit value
  //@store_32:
	stosd
	jmp	@exit

	// store as 16 bit value
  @store_16:
	mov	ebx, eax
	add	ebx, 08000h
	test	ebx, 080000000h
	jz	@store_16_01

	mov	eax, 08000h
	jmp	@store_16_w

  @store_16_01:
	test	eax, 080000000h
	jnz	@store_16_w

	mov	ebx, eax
	sub	ebx, 08000h
	test	ebx, 080000000h
	jnz	@store_16_w

	mov	eax, 07FFFh

  @store_16_w:
	stosw
	jmp	@exit

	// store as 8 bit value
  @store_8:
	sar	eax, 8
	add	eax, 080h
	test	eax, 080000000h
	jz	@store_8_01

	mov	eax, 0
	jmp	@store_8_b

  @store_8_01:
	cmp	eax, 0100h
	jb	@store_8_b

	mov	eax, 0FFh

  @store_8_b:
	stosb

  @exit:
	pop	ebx
end;

// --  --
function waveMix(buf1, buf2, buf3: pointer; samples: unsigned; bits1, bits2, bits3: unsigned; doAdd: bool; numChannels: unsigned): unsigned;
begin
  asm
	push 	esi
	push	edi
	push	ebx

	mov	ecx, samples
	mov	eax, numChannels
	imul	ecx			// EDX:EAX = samples * numChannels
	mov	ecx, eax
	mov	result, ecx

	// check if we have something to do
	cmp	ecx, 0
	je	@exit

	cmp	buf1, 0
	je	@exit
	cmp	buf2, 0
	je	@exit
	cmp	buf3, 0
	je	@exit

	mov	edi, buf3
	mov	bl, byte ptr bits3	// dest buffer bits number
	mov	bh, byte ptr bits1	// src buffer bits number

  @loop:
	// -- load A operand
	mov	esi, buf1
	mov	al, bh
	call	loadSample
	mov	buf1, esi

	mov	edx, eax	// store A operand

	// -- load B operand
	mov	esi, buf2
	mov	al, byte ptr bits2
	call	loadSample
	mov	buf2, esi

	xchg	eax, edx	// restore A operand

	// -- mix eax and ebx values

	cmp	doAdd, 0
	je	@do_mix_sub

  @do_mix_add:
	add	eax, edx
	jmp	@after_mix

  @do_mix_sub:
	sub	eax, edx

  @after_mix:
	// BL must be set to bits number
	call	saveSample		// store the result (EAX)
	loop	@loop

	// ------- loop ends here ------

  @exit:
	pop	ebx
	pop	edi
	pop	esi
  end;
end;

// --  --
function waveMix(const chunk1, chunk2, chunk3: unaPCMChunk; doAdd: bool = true): unsigned;
var
  samples: unsigned;
begin
  samples := min(waveGetChunkCurSamplesCount(chunk1), waveGetChunkCurSamplesCount(chunk2));
  if (0 < samples) then
    result := waveMix(chunk1.chunkData, chunk2.chunkData, chunk3.chunkData, samples,
		      chunk1.chunkFormat.pcmBitsPerSample,
		      chunk2.chunkFormat.pcmBitsPerSample,
		      chunk3.chunkFormat.pcmBitsPerSample)
  else
    result := 0;
end;

// --  --
function waveGetVolume(buf: pointer; samples: unsigned; bits: unsigned; numChannels: unsigned; channel: unsigned; deltaMethod: bool): unsigned;
var
  w: int64;
begin
  w := 0;
  asm
	push	esi
	push	edi
	push	ebx

	mov	result, 0

	mov	ecx, samples
	cmp 	ecx, 0
	je	@exit		// exit if there are no samples

	mov	ebx, numChannels
	cmp 	ebx, 0
	je	@exit		// exit if there are 0 channels

	mov	eax, channel
	cmp	eax, ebx
	jae	@exit		// exit if (channel >= numChannels)

	// skip required number of channels
	mov	esi, buf
	imul	byte ptr bits	// AX = channel * bits
	cwde			// EAX <- AX
	shr	eax, 3		// convert to bytes
	add	esi, eax	// advance to the beginning of channel

	// calculate size of sample (minus one channel)
	mov	eax, ebx
	dec	eax		// this is required since ESI will be increased by one channel every load
	imul	byte ptr bits	// AX = numChannels * bits
	cwde			// EAX <- AX
	shr	eax, 3		// convert to bytes
	mov	ebx, eax	//

	xor	edx, edx	// delta = 0

  @loop:
	mov	al, byte ptr bits
	call	loadSample		// EAX gets 32 bits signed integer
					// ESI shifts to next value
	cmp	deltaMethod, 0
	je	@doit

	// -- calculate sample as delta

	mov	edi, eax	// save current sample

	cmp	edx, eax
	jle	@lower		// EDX is lower then EAX

	sub	edx, eax
	mov	eax, edx
	jmp	@doit

  @lower:
	sub	eax, edx

  @doit:
	imul	eax		// EDX:EAX = EAX * EAX
	add	dword ptr w, eax
	adc	dword ptr w[4], edx

	add	esi, ebx	// move pointer to next sample for this channel
	mov	edx, edi	// restore EDX (it must contain prev. sample value for delta method)

	loop	@loop

	// --- loop ends here ---------------

	fild	w			// put w into ST(0)
	fidiv	dword ptr samples	// divide w on N
	fsqrt				// get square root

	fistp	w			// put ST(0) into w and pop FPU stack
	fwait				//

	mov	eax, dword ptr w	// assume w is less than 0x100000000
	mov	result, eax		// range is from 0 to 32'768

  @exit:
	pop	ebx
	pop	edi
	pop	esi
  end;
end;

// --  --
function waveExtractChannel(buf, dest: pointer; samples: unsigned; bits: unsigned; numChannels: unsigned = 1; channel: unsigned = 0): unsigned; assembler;
asm
{
	IN:	EAX = buf
		EDX = dest
		ECX = samples
		[ebp + $10] = bits
		[ebp + $0C] = numChannels
		[ebp + $08] = channel

	OUT:	EAX = num of samples stored
}
	push	esi
	push	edi
	push	ebx
	//
	push	ecx	// save number of samples

	mov	esi, eax	// ESI gets source ptr
	sub	eax, eax	// result := 0

	cmp 	ecx, 0
	je	@exit		// exit if there are no samples

	mov	ebx, numChannels
	cmp 	ebx, 0
	je	@exit		// exit if there are no channels

	mov	edi, edx	// EDI gets dest buf ptr

	mov	edx, channel
	cmp	edx, ebx
	jae	@exit		// exit if (channel >= numChannels)

	// skip required number of channels
	mov	bl, byte ptr bits
	mov	eax, edx	// EAX = channel
	imul	bl		// AX = channel * bits
	cwde			// EAX <- AX
	shr	eax, 3		// convert to bytes
	add	esi, eax	// advance to the beginning of channel

  @cont1:
	// calculate size of sample (minus one channel)
	mov	eax, numChannels
	dec	eax		// this is required since ESI will be increased by one channel every load
	imul	bl		// AX = numChannels * bits
	cwde			// EAX <- AX
	shr	eax, 3		// convert to bytes
	mov	edx, eax	// EDX = size of sample in bytes (munis one channel)

  @loop:
	mov	al, bl
	call	loadSample	// EAX gets 32 bits signed integer
				// ESI shifts to next value

	call	saveSample	// sample is stored in dest
				// EDI shifts to next value

	add	esi, edx	// move pointer to next sample for this channel
	loop	@loop

	// --- loop ends here ---------------
	pop	eax	// EAX = number of samples
	push	eax

  @exit:

⌨️ 快捷键说明

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