📄 unawave.pas
字号:
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 + -