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

📄 unawave.pas

📁 Voice Commnucation Components for Delphi
💻 PAS
📖 第 1 页 / 共 2 页
字号:
	pop	ecx	// restore ESP pointer
	//
	pop	ebx
	pop	edi
	pop	esi
end;

// --  --
function waveReplaceChannel(buf, source: pointer; samples: unsigned; bits: unsigned; numChannels: unsigned = 1; channel: unsigned = 0): unsigned; assembler;
asm
{
	IN:	EAX = buf
		EDX = source
		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	edi, eax	// EDI gets buf ptr (destination)
	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	esi, edx	// ESI gets source ptr (channel data)

	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	edi, 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		// EDX:EAX = 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	edi, edx	// move pointer to next sample for this channel
	loop	@loop

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

  @exit:
	pop	ecx	// restore ESP pointer
	//
	pop	ebx
	pop	edi
	pop	esi
end;

// --  --
function waveReverse(buf: pointer; samples: unsigned; bits: unsigned; numChannels: unsigned): unsigned; assembler;
asm
{
	IN:	EAX = buf
		EDX = samples
		ECX = bits
		[ebp + $08] = numChannels

	OUT:	EAX = num of samples processed
}
	push	esi
	push	edi
	push	ebx
	//

	mov	edi, eax	// EDI gets buf ptr (channel data)
	sub	eax, eax	// result := 0

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

	mov	esi, edi	// ESI gets buf ptr (channel data)

	mov	eax, ecx
	shr	eax, 3		// convert number of bits to number of bytes
	mov	bh, al		// will need this later
	mov	bl, byte ptr numChannels
	imul	bl		// AX = number of bytes in one sample
	cwde			// EAX <- AX

	mov	bl, cl		// BL get number of bits in sample
	mov	ecx, edx	// ECX = num of samples

	dec	edx		// need one sample less
	imul	edx		// EDX:EAX = number of bytes in chunk
	add	eax, edi	// get to the end of buffer (assuming EDX=0)

	mov	edi, eax	// EDI = end of buffer

	shr	ecx, 1		// need only one half of buffer
	cmp	ecx, 0
	je	@exit		// exit if there are no samples

	sub	eax, eax
	mov	al, bh
	cwde
	mov	edx, eax

	mov	bh, byte ptr numChannels

  @loop:
	push	ecx

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

	xchg	esi, edi
	sub	edi, edx	// shift back one value

	mov	al, bl
	call	loadSample
	call	saveSample

	xchg	esi, edi
	sub	edi, edx	// shift back one value

	mov	eax, ecx	// restore value
	call	saveSample	// EDI shifts to next value

	dec 	bh
	cmp 	bh, 0
	jne	@loop_channels	// repeat for all channels

	sub	eax, eax
	mov	al, dl
	mov	bh, byte ptr numChannels
	imul	bh
	cwde
	sub	edi, eax	// shift two samples back
	sub	edi, eax

	pop	ecx
	loop	@loop		// repear for half of samples

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

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

// --  --
function waveGetLogVolume(volume: int): unsigned; assembler;
asm
	mov	ecx, volume	// ECX = volume level, from 0 to 32768

	sub	eax, eax	// result := 0
	cmp	ecx, -1
	je	@done

	cmp	ecx, 32768
	mov	eax, 300
	jae	@done

	sub	eax, eax

	//
	cmp	ecx, 32
	jb	@done   	// volume is too low - assume 0

	fldlg2			// push log.10(2) constant

	push	ecx		// allocate 4 bytes on stack

	fild	dword ptr [esp]	// push volume on stack
	fwait
	mov	dword ptr [esp], 32
	fidiv	dword ptr [esp]	// ST(0) := volume / 32

	{ log.10(X) = log.2(X) * log.10(2) }

	fyl2x		// ST(1) <- ST(1) * log.2(ST(0))
			// and pops FPU stack
	mov	dword ptr [esp], 100
	fimul	dword ptr [esp]

	fistp 	dword ptr [esp]	// convert ST(0) into integer are pop FPU stack
	fwait

	pop	eax	// restore esp and get result
  @done:
end;

// --  --
function waveResample(bufSrc, bufDst: pointer; samples, numChannelsSrc, numChannelsDst, bitsSrc, bitsDst, rateSrc, rateDst: unsigned): unsigned;
const
  const_dstChannelMul = 100;
var
  step: double;
  next: double;
  u: unsigned;
  srcChannel: unsigned;
  dstChannelStep: unsigned;
  sample: unsigned;
begin
  if (
      (nil = bufSrc) or
      (nil = bufDst) or
      (1 > bitsSrc) or
      (1 > bitsDst) or
      (1 > numChannelsSrc) or
      (1 > numChannelsDst) or
      (1 > samples) or
      (1 > rateSrc) or
      (1 > rateDst)
     ) then
    // invalid params
    result := 0
  else begin
    //
    if (
	(rateDst = rateSrc) and
	(numChannelsSrc = numChannelsDst) and
	(bitsSrc = bitsDst)
       ) then begin
      //
      result := samples * numChannelsSrc * bitsSrc shr 3;
      if ((bufSrc <> bufDst) and (0 < result)) then
	move(bufSrc^, bufDst^, result);
      //
      exit;	// nothing to do
    end;
    //
    next := 0;
    step := rateDst / rateSrc;

    // numChannels conversions supported:
    //
    //	mono => any Number Of Channels
    //  stereo => mono; stereo; 4 channels; 6 channels; 8 channels ...
    //  3 channels => mono; 3 channels; 6 channels; 9 channels ...
    //  4 channels => mono; stereo; 4 channels; 8 channels; 12 channels ...
    //  5 channels => mono; 5 channels; 10 channels; 15 channels ...
    //  6 channels => mono; 3 channels; 6 channels; 12 channels ...
    //  ...
    //
    //  hope you got the idea: number of dst channels must divide on number of src channels without a remainder
    //
    //dstChannelMul := 100;
    dstChannelStep := (numChannelsDst * const_dstChannelMul) div numChannelsSrc;

    asm
	push	esi
	push	edi
	push	ebx

	//cld			// should be
	mov	esi, bufSrc	// set source pointer
	mov	edi, bufDst	// set dest pointer
	mov	ecx, samples	// set samples counter
	mov	bl, byte ptr bitsDst	// set dest sample size
	mov	bh, byte ptr bitsSrc	// set source sample size

	fld	next		// push next on FPU stack
	xor	edx, edx	// EDX is a sample counter in dest buffer

	// --------- loop ends here ------
	
  @loop:
	push	ecx	// save ECX

	fadd	step	// go to next sample
	fist	u	// round the floating point value to unsigned
	fwait		//

	cmp	edx, u		// do we need to store this source sample into dest buffer?
	jae	@nextSrcSample	// if no, skip this sample

	// -- store this sample

  @storeSample:
	// save ESI since it could be required to store same sample several times
	push	esi
	push	edx

	xor	eax, eax
	mov	srcChannel, eax		// zero source channel counter
	mov	sample, eax		// zero sample value
	mov	edx, eax		// zero number of source channels mixed so far
	mov	ecx, eax		// zero dest channel number

  @loopSrcChannels:
	mov	al, bh
	call	loadSample		// get source sample value into EAX
	inc	srcChannel		// inc the source channel counter
	add	sample, eax		// mix this sample
	inc	edx			// inc number of mixed channels

  //@storeDstChannel:
	add	ecx, dstChannelStep		// go to next channel in dest buffer
	cmp	ecx, const_dstChannelMul	// should we store the source channel?
	jb	@nextSrcChannel			// if no, go to next source channel

	mov	eax, sample	// prepare to store the sample
	cmp	edx, 1		// should we care about mixing?
	je	@skipSampleDiv	// if no, skip division

	push	ecx
	mov	ecx, edx
	cdq
	idiv	ecx		// divide mixed sample on number of channels
				// this way we should avoid increasing the volume level
	pop	ecx

  @skipSampleDiv:
	mov	edx, ecx	// set number of dest channels we must fill

	xor	ecx, ecx
	mov	sample, ecx	// zero sample value

	mov	ecx, eax	// save EAX value

  @loopDstChannel:
	mov	eax, ecx	// restore EAX value
	// BL must be set to bits number
	call 	saveSample	// store samples into dest buffer

	sub	edx, const_dstChannelMul	// go to next dest channel
	cmp	edx, const_dstChannelMul	// do we have more channels?
	jae	@loopDstChannel			// if yes, store the sample into next dest channel

	xor	ecx, ecx	// zero dest channel number
	mov	edx, ecx	// zero number of source channels mixed so far

  @nextSrcChannel:
	mov	eax, srcChannel		//
	cmp	eax, numChannelsSrc	// do we have more source channels?
	jb	@loopSrcChannels	// if yes, go to next source channel

	pop	edx
	// restore original ESI
	pop	esi

	inc	edx	       	// go to next dest sample
	cmp	edx, u		// do we have more dest samples to fill?
	jb	@storeSample	// if yes, save this source sample just one more time

  @nextSrcSample:
	// here we need to go to the next source sample
	mov	eax, numChannelsSrc	// assuming there are no more than $FF channels
	imul	bh			// byte ptr bitsSrc
	cwde				// EAX <- AX
	shr 	eax, 3
	add	esi, eax		// move source pointer to next sample

  //@goLoop:
	pop	ecx		// restore source samples counter
	loop	@loop		// and loop if there are more samples to handle

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

	fstp	next	// pop next from FPU stack

	mov	eax, edi
	sub	eax, bufDst
	mov	u, eax

	pop	ebx
	pop	edi
	pop	esi
    end;
    //
    result := u;
  end;
end;

// --  --
function waveResample(const bufSrc: unaPCMChunk; var bufDst: unaPCMChunk): unsigned;
var
  samples: unsigned;
begin
  samples := waveGetChunkCurSamplesCount(bufSrc);
  result := waveResample(bufSrc.chunkData, bufDst.chunkData, samples,
			 bufSrc.chunkFormat.pcmNumChannels,
			 bufDst.chunkFormat.pcmNumChannels,
			 bufSrc.chunkFormat.pcmBitsPerSample,
			 bufDst.chunkFormat.pcmBitsPerSample,
			 bufSrc.chunkFormat.pcmSamplesPerSecond,
			 bufDst.chunkFormat.pcmSamplesPerSecond);
  bufDst.chunkDataLen := result;
end;

// --  --
function waveGetChunkMaxSamplesCount(const chunk: unaPCMChunk): unsigned;
begin
  with chunk do begin
    //
    if ((0 < chunkFormat.pcmBitsPerSample) and (0 < chunkFormat.pcmNumChannels)) then
      result := (chunkBufSize shl 3) div (chunkFormat.pcmBitsPerSample * chunkFormat.pcmNumChannels)
    else
      result := 0;
    //
  end;    
end;

// --  --
function waveGetChunkCurSamplesCount(const chunk: unaPCMChunk): unsigned;
begin
  with chunk do begin
    //
    if ((0 < chunkFormat.pcmBitsPerSample) and (0 < chunkFormat.pcmNumChannels)) then
      result := (chunkDataLen shl 3) div (chunkFormat.pcmBitsPerSample * chunkFormat.pcmNumChannels)
    else
      result := 0;
    //
  end;    
end;

// --  --
function waveReallocateChunk(var chunk: unaPCMChunk; numSamples: unsigned): unsigned;
begin
  result := (numSamples * chunk.chunkFormat.pcmBitsPerSample * chunk.chunkFormat.pcmNumChannels) shr 3;
  chunk.chunkBufSize := result;
  chunk.chunkDataLen := 0;	// make sure buffer will not be abused
  mrealloc(chunk.chunkData, result);
end;

// --  --
function waveReadFromChunk(var chunk: unaPCMChunk; buf: pointer; size: unsigned; bufOffs: unsigned = 0): unsigned;
begin
  if ((nil <> buf) and (0 < size) and (size > bufOffs) and (0 < chunk.chunkDataLen)) then begin
    //
    result := min(chunk.chunkDataLen, size - bufOffs);
    if (0 < result) then begin
      //
      move(chunk.chunkData^, pChar(buf)[bufOffs], result);
      dec(chunk.chunkDataLen, result);
      //
      if (0 < chunk.chunkDataLen) then
	move(pChar(chunk.chunkData)[result], chunk.chunkData^, chunk.chunkDataLen);
    end;
  end
  else
    result := 0;
end;

// --  --
function waveWriteToChunk(var chunk: unaPCMChunk; buf: pointer; size: unsigned; bufOffs: unsigned = 0): unsigned;
begin
  if ((nil <> buf) and (0 < size) and (size > bufOffs) and (chunk.chunkBufSize > chunk.chunkDataLen)) then begin
    //
    result := min(chunk.chunkBufSize - chunk.chunkDataLen, size - bufOffs);
    if (0 < result) then begin
      move(pChar(buf)[bufOffs], pChar(chunk.chunkData)[chunk.chunkDataLen], result);
      inc(chunk.chunkDataLen, result);
    end;
  end
  else
    result := 0;
end;

// --  --
function waveFindNextGcd(rate1, rate2: unsigned; startFrom: unsigned): unsigned;
var
  d: unsigned;
  maximum: unsigned;
begin
  maximum := min(rate1, rate2);
  //
  d := startFrom - 1;
  repeat
    //
    inc(d);
    //
    result := gcd(d, rate1);
    if (result = d) then
      result := gcd(d, rate2);
    //  
  until ((d = result) or (d >= maximum));
  //
  result := d;
end;

// --  --
function waveModifyVolume(volume: unsigned; buf: pointer; samples: unsigned; bits: unsigned; numChannels: unsigned; channel: int): unsigned;
var
  thousand: unsigned;
begin
  result := 0;
  if ((0 < samples) and
      (0 < bits) and
      (0 < numChannels) and
      ((0 > channel) or (int(numChannels) > channel))) then begin
    //
    thousand := 1000;
    asm
	push	esi
	push	edi
	push	ebx

	mov	ecx, samples
	mov	esi, buf
	mov	edi, esi
	mov	bl, byte ptr bits	// dest buffer bits number
	mov	bh, bl

	mov	edx, 0	// channel number

  @loop:
	mov	al, bl
	call 	loadSample

	cmp     channel, -1
	je	@modify

	cmp	channel, edx
	je	@modify

	jmp	@nextChannel

  @modify:
	push	edx
	imul	volume
	idiv	thousand
	pop	edx

  @nextChannel:
	// BL must be set to bits number
	call	saveSample

	inc	edx
	cmp	edx, numChannels
	jb	@loop

	mov	edx, 0
	loop	@loop

	//  -- loop ends here --

	pop	ebx
	pop	edi
	pop	esi
    end;
    result := samples;
  end;
end;

// --  --
function waveModifyVolume(volume: unsigned; const chunk: unaPCMChunk; channel: int): unsigned;
var
  samples: unsigned;
begin
  samples := waveGetChunkCurSamplesCount(chunk);
  //
  if (0 < samples) then
    result := waveModifyVolume(volume, chunk.chunkData, samples, chunk.chunkFormat.pcmBitsPerSample, chunk.chunkFormat.pcmNumChannels, channel)
  else
    result := 0;
end;


end.

⌨️ 快捷键说明

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