📄 snd_mix.pas
字号:
{----------------------------------------------------------------------------}
{ }
{ File(s): snd_mix.c }
{ Content: Quake2\ref_soft\ sound structures and constants }
{ }
{ Initial conversion by : Skaljac Bojan (Skaljac@Italy.Com) }
{ Initial conversion on : 17-Feb-2002 }
{ }
{ This File contains part of convertion of Quake2 source to ObjectPascal. }
{ More information about this project can be found at: }
{ http://www.sulaco.co.za/quake2/ }
{ }
{ Copyright (C) 1997-2001 Id Software, Inc. }
{ }
{ This program is free software; you can redistribute it and/or }
{ modify it under the terms of the GNU General Public License }
{ as published by the Free Software Foundation; either version 2 }
{ of the License, or (at your option) any later version. }
{ }
{ This program is distributed in the hope that it will be useful, }
{ but WITHOUT ANY WARRANTY; without even the implied warranty of }
{ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. }
{ }
{ See the GNU General Public License for more details. }
{ }
{----------------------------------------------------------------------------}
{ Updated on : 03-jun-2002 }
{ Updated by : Juha Hartikainen (juha@linearteam.org) }
{ - Language fixes to make this compile}
{ }
{----------------------------------------------------------------------------}
{ * Still dependent (to compile correctly) on: }
{ }
{----------------------------------------------------------------------------}
{ * TODO: }
{ 1. Some test of couple functions in this unit }
{----------------------------------------------------------------------------}
// snd_mix.c -- portable code to mix sounds for snd_dma.c
unit snd_mix;
interface
uses
SysUtils,
snd_loc,
snd_dma;
const
PAINTBUFFER_SIZE = 2048;
type
TSmallIntArray = array[0..0] of SmallInt;
PSmallIntArray = ^TSmallIntArray;
TCardinalArray = array[0..0] of Cardinal;
PCardinalArray = ^TCardinalArray;
var
paintbuffer : array[0..PAINTBUFFER_SIZE-1] of portable_samplepair_t;
snd_scaletable : array[0..31, 0..255] of Integer;
snd_p : PIntegerArray;
snd_linear_count, snd_vol : Integer;
snd_out : PSmallIntArray; // short *name;
soundtime : Integer; // sample PAIRS
paintedtime : Integer; // sample PAIRS
procedure S_WriteLinearBlastStereo16 ;
procedure S_PaintChannelFrom8 (ch:channel_p;sc:sfxcache_p; count, offset:Integer);
procedure S_PaintChannelFrom16 (ch:channel_p;sc:sfxcache_p; count, offset:Integer);
procedure S_InitScaletable;
procedure S_PaintChannels(endtime : Integer);
implementation
uses
snd_mem;
//#if !(defined __linux__ && defined __i386__)
{$IFNDEF id386)}
procedure S_WriteLinearBlastStereo16 ;
var
i, val: Integer;
begin
i := 0;
while (i<snd_linear_count) do
begin
val := SmallInt(snd_p^[i] shr 8);
if (val > $7fff) then
snd_out^[i] := $7fff
else if (val < SmallInt($8000)) then
snd_out^[i] := SmallInt($8000)
else
snd_out^[i] := val;
val := SmallInt(snd_p^[i+1] shr 8);
if (val > $7fff) then
snd_out^[i+1] := $7fff
else if (val < SmallInt($8000)) then
snd_out^[i+1] := SmallInt($8000)
else
snd_out^[i+1] := val;
Inc(i,2);
end;
end;
{$ELSE}
//__declspec( naked ) procedure S_WriteLinearBlastStereo16 ;
procedure S_WriteLinearBlastStereo16;
asm
push edi
push ebx
mov ecx,ds:dword ptr[snd_linear_count]
mov ebx,ds:dword ptr[snd_p]
mov edi,ds:dword ptr[snd_out]
@LWLBLoopTop:
mov eax,ds:dword ptr[-8+ebx+ecx*4]
sar eax,8
cmp eax,07FFFh
jg @LClampHigh
cmp eax,0FFFF8000h
jnl @LClampDone
mov eax,0FFFF8000h
jmp @LClampDone
@LClampHigh:
mov eax,07FFFh
@LClampDone:
mov edx,ds:dword ptr[-4+ebx+ecx*4]
sar edx,8
cmp edx,07FFFh
jg @LClampHigh2
cmp edx,0FFFF8000h
jnl @LClampDone2
mov edx,0FFFF8000h
jmp @LClampDone2
@LClampHigh2:
mov edx,07FFFh
@LClampDone2:
shl edx,16
and eax,0FFFFh
or edx,eax
mov ds:dword ptr[-4+edi+ecx*2],edx
sub ecx,2
jnz @LWLBLoopTop
pop ebx
pop edi
ret
end;
{$ENDIF}
//#endif
//pbuf = ^LongWord;
procedure S_TransferStereo16 (pbuf : PCardinalArray; endtime : Integer);
var
lpos,
lpaintedtime: Integer;
begin
snd_p := @paintbuffer;
lpaintedtime := paintedtime;
while (lpaintedtime < endtime) do
begin
// handle recirculating buffer issues
lpos := lpaintedtime and ((dma.samples shr 1)-1);
snd_out := Pointer(Cardinal(pbuf) + (lpos shl 1)*SizeOf(SmallInt));
snd_linear_count := (dma.samples shr 1) - lpos;
if (lpaintedtime + snd_linear_count > endtime) then
snd_linear_count := endtime - lpaintedtime;
snd_linear_count := snd_linear_count shl 1;
// write a linear blast of samples
S_WriteLinearBlastStereo16 ();
snd_p := Pointer(Cardinal(snd_p) + snd_linear_count * SizeOf(Cardinal));
lpaintedtime := lpaintedtime + (snd_linear_count shr 1);
end;
end;
(*
===================
S_TransferPaintBuffer
===================
*)
procedure S_TransferPaintBuffer(endtime : Integer);
var
out_idx,
count,
out_mask,
step,
val: Integer;
p: PIntegerArray;
pbuf: PCardinalArray;
i: Integer;
out8 : PByteArray;
out16 : PSmallIntArray;
begin
pbuf := PCardinalArray(dma.buffer);
if (s_testsound^.value <> 0) then
begin
// write a fixed sine wave
count := (endtime - paintedtime);
for i:=0 to count-1 do
begin
paintbuffer[i].left := Trunc(sin((paintedtime+i)*0.1)*20000*256);
paintbuffer[i].right := paintbuffer[i].left;
end;
end;
if ((dma.samplebits = 16) and (dma.channels = 2)) then
begin // optimized case
S_TransferStereo16 (pbuf, endtime);
end
else
begin // general case
p := @paintbuffer;
count := (endtime - paintedtime) * dma.channels;
out_mask := dma.samples - 1;
out_idx := paintedtime * dma.channels and out_mask;
step := 3 - dma.channels;
if (dma.samplebits = 16) then
begin
out16 := PSmallIntArray(pbuf);
while (count<>0) do
begin
Dec(Count);
val := p[0] shr 8;
p := Pointer(Cardinal(p) + step * SizeOf(Integer));
if (val > $7fff) then
val := $7fff
else if (val < SmallInt($8000)) then
val := SmallInt($8000);
out16[out_idx] := val;
out_idx := (out_idx + 1) and out_mask;
end;
end
else if (dma.samplebits = 8) then
begin
out8 := PByteArray(pbuf);
while (count<>0) do
begin
Dec(Count);
val := p[0] shr 8;
p := Pointer(Cardinal(p) + step * SizeOf(Byte));
if (val > $7fff) then
val := $7fff
else if (val < SmallInt($8000)) then
val := $8000;
out8[out_idx] := (val shr 8) + 128;
out_idx := (out_idx + 1) and out_mask;
end;
end;
end;
end;
(*
===============================================================================
CHANNEL MIXING
===============================================================================
*)
procedure S_PaintChannels(endtime : Integer);
var
i, _end: Integer;
sc: sfxcache_p;
ltime, count: Integer;
ps: playsound_p;
s, stop: Integer;
begin
snd_vol := Trunc(s_volume^.value * 256);
//Com_Printf ("%i to %i\n", paintedtime, endtime);
while (paintedtime < endtime) do
begin
// if paintbuffer is smaller than DMA buffer
_end := endtime;
if (endtime - paintedtime > PAINTBUFFER_SIZE) then
_end := paintedtime + PAINTBUFFER_SIZE;
// start any playsounds
while (True) do
begin
ps := s_pendingplays.next;
if (ps = @s_pendingplays) then
break; // no more pending sounds
if (ps^._begin <= paintedtime) then
begin
S_IssuePlaysound (ps);
continue;
end;
if (ps^._begin < _end) then
_end := ps^._begin; // stop here
break;
end;
// clear the paint buffer
if (s_rawend < paintedtime) then
begin
// Com_Printf ("clear\n");
FillChar(paintbuffer,(_end - paintedtime) * sizeof(portable_samplepair_t),0);
end
else
begin // copy from the streaming sound source
//stop := (_end < s_rawend) ? _end : s_rawend;
if (_end < s_rawend) then
stop := _end
else
stop := s_rawend;
for i := paintedtime to stop-1 do
begin
s := i and (MAX_RAW_SAMPLES-1);
paintbuffer[i-paintedtime] := s_rawsamples_[s];
end;
// if (i != end)
// Com_Printf ("partial stream\n");
// else
// Com_Printf ("full stream\n");
for i := stop to _end-1 do begin
paintbuffer[i-paintedtime].left := 0;
paintbuffer[i-paintedtime].right := 0;
end;
end;
// paint in the channels.
for i:=0 to MAX_CHANNELS-1 do //i++, ch++)
begin
ltime := paintedtime;
while (ltime < _end) do
begin
if ((channels[i].sfx=nil) or ((channels[i].leftvol=0) and (channels[i].rightvol=0))) then
break;
// max painting is to the end of the buffer
count := _end - ltime;
// might be stopped by running out of data
if (channels[i]._end - ltime < count) then
count := channels[i]._end - ltime;
sc := S_LoadSound(channels[i].sfx);
if (sc = nil) then
break;
if ((count > 0) and (channels[i].sfx<>nil)) then
begin
if (sc^.width = 1) then// FIXME; 8 bit asm is wrong now
S_PaintChannelFrom8(@channels[i], sc, count, ltime - paintedtime)
else
S_PaintChannelFrom16(@channels[i], sc, count, ltime - paintedtime);
Inc(ltime, count);
end;
// if at end of loop, restart
if (ltime >= channels[i]._end) then
begin
if (channels[i].autosound) then
begin // autolooping sounds always go back to start
channels[i].pos := 0;
channels[i]._end := ltime + sc^.length;
end
else if (sc^.loopstart >= 0) then
begin
channels[i].pos := sc^.loopstart;
channels[i]._end := ltime + sc^.length - channels[i].pos;
end
else
begin // channel just stopped
channels[i].sfx := nil;
end;
end;
end;
end;
// transfer out according to DMA format
S_TransferPaintBuffer(_end);
paintedtime := _end;
end;
end;
procedure S_InitScaletable ;
var
i, j, scale: Integer;
begin
s_volume^.modified := false;
for i := 0 to 31 do
begin
scale := Trunc(i * 8 * 256 * s_volume^.value);
for j:=0 to 255 do
snd_scaletable[i][j] := ShortInt(j) * scale;
end;
end;
//#if !(defined __linux__ && defined __i386__)
{$IFNDEF id386}
//procedure S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count, int offset)
procedure S_PaintChannelFrom8 (ch:channel_p;sc:sfxcache_p; count, offset:Integer);
var
data, i: Integer;
lscale, rscale: PInteger;
sfx: PByteArray;
samp: portable_samplepair_p;
begin
if (ch^.leftvol > 255) then
ch^.leftvol := 255;
if (ch^.rightvol > 255) then
ch^.rightvol := 255;
//ZOID-- shr 11 has been changed to shr 3, shr 11 didn't make much sense
//as it would always be zero.
lscale := @snd_scaletable[ ch^.leftvol shr 3];
rscale := @snd_scaletable[ ch^.rightvol shr 3];
sfx := Pointer(Cardinal(@sc^.data) + ch^.pos);
samp := @paintbuffer[offset];
for i := 0 to count-1 do
begin
data := sfx[i];
samp.left := samp.left + PIntegerArray(lscale)[data];
samp.right := samp.right + PIntegerArray(rscale)[data];
Inc(samp);
end;
ch^.pos := ch^.pos + count;
end;
{$ELSE}
//__declspec( naked ) procedure S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count, int offset)
procedure S_PaintChannelFrom8(ch : channel_p; sc : sfxcache_p; count, offset: Integer);
asm
push esi
push edi
push ebx
push ebp
mov ebx,ds:dword ptr[4+16+esp]
mov esi,ds:dword ptr[8+16+esp]
mov eax,ds:dword ptr[4+ebx]
mov edx,ds:dword ptr[8+ebx]
cmp eax,255
jna @LLeftSet
mov eax,255
@LLeftSet:
cmp edx,255
jna @LRightSet
mov edx,255
@LRightSet:
and eax,0F8h
add esi,20
and edx,0F8h
mov edi,ds:dword ptr[16+ebx]
mov ecx,ds:dword ptr[12+16+esp]
add esi,edi
shl eax,7
add edi,ecx
shl edx,7
mov ds:dword ptr[16+ebx],edi
add eax,offset snd_scaletable
add edx,offset snd_scaletable
sub ebx,ebx
mov bl,ds:byte ptr[-1+esi+ecx*1]
test ecx,1
jz @LMix8Loop
mov edi,ds:dword ptr[eax+ebx*4]
mov ebp,ds:dword ptr[edx+ebx*4]
add edi,ds:dword ptr[paintbuffer+0-8+ecx*8]
add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8]
mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi
mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp
mov bl,ds:byte ptr[-2+esi+ecx*1]
dec ecx
jz @LDone
@LMix8Loop:
mov edi,ds:dword ptr[eax+ebx*4]
mov ebp,ds:dword ptr[edx+ebx*4]
add edi,ds:dword ptr[paintbuffer+0-8+ecx*8]
add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8]
mov bl,ds:byte ptr[-2+esi+ecx*1]
mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi
mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp
mov edi,ds:dword ptr[eax+ebx*4]
mov ebp,ds:dword ptr[edx+ebx*4]
mov bl,ds:byte ptr[-3+esi+ecx*1]
add edi,ds:dword ptr[paintbuffer+0-8*2+ecx*8]
add ebp,ds:dword ptr[paintbuffer+4-8*2+ecx*8]
mov ds:dword ptr[paintbuffer+0-8*2+ecx*8],edi
mov ds:dword ptr[paintbuffer+4-8*2+ecx*8],ebp
sub ecx,2
jnz @LMix8Loop
@LDone:
pop ebp
pop ebx
pop edi
pop esi
ret
end;
{$ENDIF}
//#endif
procedure S_PaintChannelFrom16 (ch:channel_p;sc:sfxcache_p; count, offset:Integer);
var
data,
left,
right,
leftvol,
rightvol,
i: Integer;
sfx: PSmallIntArray;
samp: portable_samplepair_p;
begin
leftvol := ch^.leftvol*snd_vol;
rightvol := ch^.rightvol*snd_vol;
sfx := Pointer(Cardinal(@sc^.data) + ch^.pos * SizeOf(SmallInt));
samp := @paintbuffer[offset];
for i := 0 to count-1 do
begin
data := sfx[i];
left := (data * leftvol) shr 8;
right := (data * rightvol) shr 8;
samp.left := samp.left + left;
samp.right := samp.right + right;
Inc(samp);
end;
ch^.pos := ch^.pos + count;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -