📄 mmadpcm.pas
字号:
{ }
{ the block header is composed of the following data: }
{ 1 byte predictor per channel }
{ 2 byte delta per channel }
{ 2 byte first sample per channel }
{ 2 byte second sample per channel }
{ }
{ this gives us (7 * bChannels) bytes of header information }
{ so first write the 1 byte predictor for each channel into the }
{ destination buffer }
for m := 0 to bChannels-1 do
begin
PByte(pDst)^ := abBestPredictor[m];
inc(pDst);
end;
{ now write the 2 byte delta per channel... }
for m := 0 to bChannels-1 do
begin
PSmallint(pDst)^ := aiDelta[m];
inc(pDst,2);
end;
{ finally, write the first two samples (2 bytes each per channel) }
for m := 0 to bChannels-1 do
begin
PSmallint(pDst)^ := aiSamp1[m];
inc(pDst,2);
end;
for m := 0 to bChannels-1 do
begin
PSmallint(pDst)^ := aiSamp2[m];
inc(pDst,2);
end;
{ the number of bytes that we have written to the destination }
{ buffer is (7 * bChannels)--so add this to our total number of }
{ bytes written to the destination.. for our return value. }
inc(dwTotalConverted,wBlockHeaderBytes);
{ we have written the header for this block--now write the data }
{ chunk (which consists of a bunch of encoded nibbles). note that }
{ we start our count at 2 because we already wrote the first }
{ two samples into the destination buffer as part of the header }
wFirstNibble := 1;
for n := 2 to wBlockSize-1 do
begin
{ each channel gets encoded independently... obviously. }
for m := 0 to bChannels-1 do
begin
{ use our chosen best predictor and grab the coefficient }
{ pair to use for this channel... }
i := abBestPredictor[m];
iCoef1 := lpwfADPCM^.aCoef[i].iCoef1;
iCoef2 := lpwfADPCM^.aCoef[i].iCoef2;
{ copy into cheaper variables because we access them a lot }
iSamp1 := aiSamp1[m];
iSamp2 := aiSamp2[m];
iDelta := aiDelta[m];
{ calculate the prediction based on the previous two samples }
lPrediction := sar(Longint(iSamp1) * iCoef1 +
Longint(iSamp2) * iCoef2,MSADPCM_CSCALE);
{ grab the sample to encode--convert it to 16 bit data if }
{ necessary... }
if (bBitsPerSample <> 8) then
begin
iSample := PSmallint(lpSamples)^;
inc(lpSamples,2);
end
else
begin
iSample := (Smallint(PByte(lpSamples)^) - 128) shl 8;
end;
{ encode the sample }
lError := iSample - lPrediction;
iOutput := (lError div iDelta);
if (iOutput > MSADPCM_OUTPUT4_MAX) then
iOutput := MSADPCM_OUTPUT4_MAX
else if (iOutput < MSADPCM_OUTPUT4_MIN) then
iOutput := MSADPCM_OUTPUT4_MIN;
lSamp := lPrediction + (Longint(iDelta) * iOutput);
if (lSamp > 32767) then
lSamp := 32767
else if (lSamp < -32768) then
lSamp := -32768;
{ compute the next iDelta }
iDelta := sar((gaiP4[iOutput and 15] * Longint(iDelta)),MSADPCM_PSCALE);
if (iDelta < MSADPCM_DELTA4_MIN) then
iDelta := MSADPCM_DELTA4_MIN;
{ save updated values for this channel back into the }
{ original arrays... }
aiDelta[m] := iDelta;
aiSamp2[m] := iSamp1;
aiSamp1[m] := lSamp;
{ we have another nibble of encoded data--either combine }
{ this with the previous nibble and write out a full }
{ byte, or save this nibble for the next nibble to be }
{ combined into a full byte and written to the destination }
{ buffer... uhg! }
if (wFirstNibble <> 0) then
begin
wNextWrite := sal(iOutput and 15,4);
wFirstNibble := 0;
end
else
begin
PByte(pDst)^ := (wNextWrite or (iOutput and 15));
inc(pDst);
inc(dwTotalConverted);
inc(wFirstNibble);
end;
end;
end;
inc(dwInputPos,wBlockSize * cbSample);
end;
{ free the memory used for our small data buffer and return the number }
{ of bytes that we wrote into the destination buffer... }
GlobalFreePtr(lpSamplesBuf);
Result := dwTotalConverted;
end;
(*============================================================================*)
(* *)
(* the code below provides 'support' routines for building/verifying ADPCM *)
(* headers, etc. *)
(* *)
(* the coefficient pairs that should be in the wave format header for *)
(* the Microsoft 4 Bit ADPCM algorithm. the code to copy the coefficients *)
(* into the wave format header is shown: *)
(* *)
(* short gaiCoef1[] = { 256, 512, 0, 192, 240, 460, 392 }; *)
(* short gaiCoef2[] = { 0, -256, 0, 64, 0, -208, -232 }; *)
(* *)
(* for (w = 0; w < MSADPCM_NUM_COEF; w++) *)
(* { *)
(* lpwfADPCM->aCoef[w].iCoef1 = gaiCoef1[w]; *)
(* lpwfADPCM->aCoef[w].iCoef2 = gaiCoef2[w]; *)
(* } *)
(* *)
(*============================================================================*)
const
gaiCoef1: array[0..6]of Smallint = (256, 512, 0, 192, 240, 460, 392);
gaiCoef2: array[0..6]of Smallint = (0, -256, 0, 64, 0, -208, -232);
function adpcmIsValidFormat(lpwfx: PWaveFormatEx): Boolean;
var
lpwfADPCM: PADPCMWaveFormat;
w: WORD;
begin
if (lpwfx = nil) or (lpwfx^.wFormatTag <> WAVE_FORMAT_ADPCM) or
(lpwfx^.wBitsPerSample <> 4) or (lpwfx^.nChannels < 1) or
(lpwfx^.nChannels > MSADPCM_MAX_CHANNELS) then
begin
Result := False;
exit;
end;
{ check coef's to see if it is Microsoft's standard 4 Bit ADPCM }
lpwfADPCM := Pointer(lpwfx);
if (lpwfADPCM^.wNumCoef <> MSADPCM_NUM_COEF) then
begin
Result := False;
exit;
end;
for w := 0 to MSADPCM_NUM_COEF-1 do
begin
if (lpwfADPCM^.aCoef[w].iCoef1 <> gaiCoef1[w]) or
(lpwfADPCM^.aCoef[w].iCoef2 <> gaiCoef2[w]) then
begin
Result := False;
exit;
end;
end;
Result := True;
end;
{==============================================================================}
function adpcmBuildFormatHeader(lpwfxSrc, lpwfxDst: PWaveFormatEx; PCMBitLength,DestChannels,DestRate: integer): Boolean;
var
lpwfADPCM: PADPCMWaveFormat;
wBlockAlign,
wChannels,
wSamplesPerSec,
wBitsPerSample,
wHeaderBytes,
w: Word;
dw: DWORD;
begin
{ if the source format is PCM, then build an ADPCM destination format }
{ header... assuming the PCM format header is valid. }
if (lpwfxSrc^.wFormatTag = WAVE_FORMAT_PCM) then
begin
if not pcmIsValidFormat(lpwfxSrc) then
begin
Result := False;
exit;
end;
lpwfADPCM := Pointer(lpwfxDst);
if (DestChannels > 0) then
wChannels := DestChannels
else
wChannels := lpwfxSrc^.nChannels;
if (DestRate > 0) then
wSamplesPerSec := DestRate
else
wSamplesPerSec := lpwfxSrc^.nSamplesPerSec;
wBitsPerSample := 4;
{ fill in destination header with appropriate ADPCM stuff based }
{ on source PCM header... }
lpwfxDst^.wFormatTag := WAVE_FORMAT_ADPCM;
lpwfxDst^.nSamplesPerSec := wSamplesPerSec;
lpwfxDst^.nChannels := wChannels;
lpwfxDst^.wBitsPerSample := wBitsPerSample;
{ choose a block alignment that makes sense for the sample rate }
{ that the original PCM data is. basically, this needs to be }
{ some reasonable number to allow efficient streaming, etc. }
{ }
{ don't let block alignment get too small... }
wBlockAlign := 256 * wChannels;
if (wSamplesPerSec > 11025) then
wBlockAlign := wBlockAlign * (wSamplesPerSec div 11000);
lpwfxDst^.nBlockAlign := wBlockAlign;
{ compute that 'samples per block' that will be in the encoded }
{ ADPCM data blocks. this is determined by subtracting out the }
{ 'other info' contained in each block--a block is composed of }
{ a header followed by the encoded data. }
{ }
{ the block header is composed of the following data: }
{ 1 byte predictor per channel }
{ 2 byte delta per channel }
{ 2 byte first sample per channel }
{ 2 byte second sample per channel }
{ }
{ this gives us (7 * wChannels) bytes of header information that }
{ contains our first two full samples (so we add two below). }
wHeaderBytes := (7 * wChannels);
w := (wBlockAlign - wHeaderBytes) * 8;
lpwfADPCM^.wSamplesPerBlock := (w div (wBitsPerSample * wChannels)) + 2;
{ now compute the avg bytes per second (man this code bites!) }
dw := ((Longint(wBitsPerSample) * wChannels *
Longint(lpwfxDst^.nSamplesPerSec)) div 8);
lpwfxDst^.nAvgBytesPerSec := (dw + wHeaderBytes + ((dw div wBlockAlign) * wHeaderBytes));
{ fill in the cbSize field of the extended wave format header. }
{ this number is the number of _EXTRA BYTES_ *after* the end }
{ of the WAVEFORMATEX structure that are need for the compression }
{ format. }
{ }
{ for Microsoft's 4 Bit ADPCM format, this number is 32: }
lpwfxDst^.cbSize := sizeof(TADPCMWAVEFORMAT) - sizeof(TWaveFormatEx) +
((MSADPCM_NUM_COEF-1) * sizeof(TADPCMCOEFSET));
{ copy the Microsoft 4 Bit ADPCM coef's into the header }
lpwfADPCM^.wNumCoef := MSADPCM_NUM_COEF;
for w := 0 to MSADPCM_NUM_COEF-1 do
begin
lpwfADPCM^.aCoef[w].iCoef1 := gaiCoef1[w];
lpwfADPCM^.aCoef[w].iCoef2 := gaiCoef2[w];
end;
Result := True;
end
else if (lpwfxSrc^.wFormatTag = WAVE_FORMAT_ADPCM) then
begin
{ if the source format is ADPCM, then build an appropriate PCM header }
if not adpcmIsValidFormat(lpwfxSrc) then
begin
Result := False;
exit;
end;
if (DestChannels > 0) then
wChannels := DestChannels
else
wChannels := lpwfxSrc^.nChannels;
if (DestRate > 0) then
wSamplesPerSec := DestRate
else
wSamplesPerSec := lpwfxSrc^.nSamplesPerSec;
if (PCMBitLength > 0) then
wBitsPerSample := PCMBitLength
else
wBitsPerSample := 16;
{ fill in the info for our destination format... }
pcmBuildWaveHeader(lpwfxDst,wBitsPerSample, wChannels, wSamplesPerSec);
Result := True;
end
else Result := False;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -