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

📄 mmadpcm.pas

📁 一套及时通讯的原码
💻 PAS
📖 第 1 页 / 共 3 页
字号:
{========================================================================}
{=                (c) 1995-98 SwiftSoft Ronald Dittrich                 =}
{========================================================================}
{=                          All Rights Reserved                         =}
{========================================================================}
{=  D 01099 Dresden             = Fax.: +49 (0)351-8037944              =}
{=  Loewenstr.7a                = info@swiftsoft.de                     =}
{========================================================================}
{=  Actual versions on http://www.swiftsoft.de/mmtools.html             =}
{========================================================================}
{=  This code is for reference purposes only and may not be copied or   =}
{=  distributed in any format electronic or otherwise except one copy   =}
{=  for backup purposes.                                                =}
{=                                                                      =}
{=  No Delphi Component Kit or Component individually or in a collection=}
{=  subclassed or otherwise from the code in this unit, or associated   =}
{=  .pas, .dfm, .dcu, .asm or .obj files may be sold or distributed     =}
{=  without express permission from SwiftSoft.                          =}
{=                                                                      =}
{=  For more licence informations please refer to the associated        =}
{=  HelpFile.                                                           =}
{========================================================================}
{=  $Date: 08.09.98 - 11:53:22 $                                        =}
{========================================================================}
unit MMADPCM;

{$I COMPILER.INC}

interface

uses
    {$IFDEF WIN32}
    Windows,
    {$ELSE}
    WinProcs,
    WinTypes,
    {$ENDIF}
    SysUtils,
    MMSystem,
    MMObj,
    MMUtils,
    MMMulDiv,
    MMRegs,
    MMPCMSup;


function adpcmDecode4Bit(lpwfADPCM: PADPCMWaveFormat; lpwfPCM: PWaveFormatEx; pSrc, pDst: PChar; dwSrcLen: DWORD): DWORD;
function adpcmEncode4Bit(lpwfPCM: PWaveFormatEx; lpwfADPCM: PADPCMWaveFormat; pSrc, pDst: PChar; dwSrcLen: DWORD): DWORD;
function adpcmIsValidFormat(lpwfx: PWaveFormatEx): Boolean;
function adpcmBuildFormatHeader(lpwfxSrc, lpwfxDst: PWaveFormatEx; PCMBitLength,DestChannels,DestRate: integer): Boolean;

implementation

(*============================================================================*)
(*                                                                            *)
(*  constants used by the Microsoft 4 Bit ADPCM algorithm                     *)
(*                                                                            *)
(*  CAUTION: the code contained in this file assumes that the number of       *)
(*  channels will be no greater than 2! this is for minor optimization        *)
(*  purposes and would be very easy to change if >2 channels is required.     *)
(*  it also assumes that the PCM data will either be 8 or 16 bit.             *)
(*                                                                            *)
(*  the code to look out for looks 'similar' to this:                         *)
(*                                                                            *)
(*      PCM.BytesPerSample = (PCM.BitsPerSample >> 3) << (Channels >> 1);     *)
(*                                                                            *)
(*============================================================================*)
const
     MSADPCM_NUM_COEF     = 7;
     MSADPCM_MAX_CHANNELS = 2;

     MSADPCM_CSCALE       = 8;
     MSADPCM_PSCALE       = 8;
     MSADPCM_CSCALE_NUM   = 1 shl MSADPCM_CSCALE;
     MSADPCM_PSCALE_NUM   = 1 shl MSADPCM_PSCALE;

     MSADPCM_DELTA4_MIN   = 16;

     MSADPCM_OUTPUT4_MAX  = 7;
     MSADPCM_OUTPUT4_MIN  = -8;


(*  Fixed point Delta adaption table:                              *)
(*                                                                 *)
(*      Next Delta = Delta * gaiP4[ this output ] / MSADPCM_PSCALE *)
const
     gaiP4: array[0..15]of Smallint = (230, 230, 230, 230, 307, 409, 512, 614,
                                       768, 614, 512, 409, 307, 230, 230, 230);

{==============================================================================}
function adpcmDecode4Bit(lpwfADPCM: PADPCMWaveFormat; lpwfPCM: PWaveFormatEx; pSrc, pDst: PChar; dwSrcLen: DWORD): DWORD;
Label adpcmDecode4BitExit;
type
   PCoefSet = ^TCoefSet;
   TCoefSet = array[0..0] of TADPCMCOEFSET;
var
   iInput, iNextInput, iFirstNibble, iDelta: Smallint;
   lSamp, lPrediction: Longint;
   bPredictor, bPredictors, bChannels, bBitsPerSample, m: Byte;
   n, wSamplesPerBlock, wBlockHeaderBytes: Word;
   dwTotalPos, dwDecoded: DWORD;
   aiSamp1: array[0..MSADPCM_MAX_CHANNELS-1] of Smallint;
   aiSamp2: array[0..MSADPCM_MAX_CHANNELS-1] of Smallint;
   aiCoef1: array[0..MSADPCM_MAX_CHANNELS-1] of Smallint;
   aiCoef2: array[0..MSADPCM_MAX_CHANNELS-1] of Smallint;
   aiDelta: array[0..MSADPCM_MAX_CHANNELS-1] of Smallint;

   lpCoefSet: PCoefSet;

begin
   { put some commonly used info in more accessible variables and init }
   { the wBlockHeaderBytes, dwDecoded and dwTotalPos vars...           }
   lpCoefSet         := @lpwfADPCM^.aCoef[0];
   bPredictors       := lpwfADPCM^.wNumCoef;
   bChannels         := lpwfADPCM^.wfx.nChannels;
   bBitsPerSample    := lpwfPCM^.wBitsPerSample;
   wSamplesPerBlock  := lpwfADPCM^.wSamplesPerBlock;
   wBlockHeaderBytes := bChannels * 7;
   dwDecoded         := 0;
   dwTotalPos        := 0;

   { step through each byte of ADPCM data and decode it to the requested }
   { PCM format (8 or 16 bit).                                           }

   while (dwTotalPos < dwSrcLen) do
   begin
      { the first thing we need to do with each block of ADPCM data is    }
      { to read the header which consists of 7 bytes of data per channel. }
      { so our first check is to make sure that we have _at least_        }
      { enough input data for a complete ADPCM block header--if there     }
      { is not enough data for the header, then exit.                     }
      {                                                                   }
      { the header looks like this:                                       }
      {      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. note   }
      { that as long as there is _at least_ (7 * bChannels) of header     }
      { info, we will grab the two samples from the header (and if no     }
      { data exists following the header we will exit in the decode       }
      { loop below).                                                      }
      inc(dwTotalPos,wBlockHeaderBytes);
      if (dwTotalPos > dwSrcLen) then
          goto adpcmDecode4BitExit;

      { grab and validate the predictor for each channel }
      for m := 0 to bChannels-1 do
      begin
         bPredictor := PByte(pSrc)^;
         inc(pSrc);
         if (bPredictor >=  bPredictors) then
         begin
            { the predictor is out of range--this is considered a      }
            { fatal error with the ADPCM data, so we fail by returning }
            { zero bytes decoded                                       }
            dwDecoded := 0;
            goto adpcmDecode4BitExit;
         end;

         { get the coefficients for the predictor index }
         aiCoef1[m] := lpCoefSet^[bPredictor].iCoef1;
         aiCoef2[m] := lpCoefSet^[bPredictor].iCoef2;
      end;

      { get the starting delta for each channel }
      for m := 0 to bChannels-1 do
      begin
         aiDelta[m] := PSmallint(pSrc)^;
         inc(pSrc,2);
      end;

      { get the sample just previous to the first encoded sample per channel }
      for m := 0 to bChannels-1 do
      begin
         aiSamp1[m] := PSmallint(pSrc)^;
         inc(pSrc,2);
      end;

      { get the sample previous to aiSamp1[x] per channel }
      for m := 0 to bChannels-1 do
      begin
         aiSamp2[m] := PSmallint(pSrc)^;
         inc(pSrc,2);
      end;

      { write out first 2 samples for each channel.                      }
      { NOTE: the samples are written to the destination PCM buffer      }
      { in the _reverse_ order that they are in the header block:        }
      { remember that aiSamp2[x] is the _previous_ sample to aiSamp1[x]. }
      if (bBitsPerSample = 8) then
      begin
         for m := 0 to bChannels-1 do
         begin
            PByte(pDst)^ := (aiSamp2[m] div 256) + 128;
            inc(pDst);
         end;
         for m := 0 to bChannels-1 do
         begin
            PByte(pDst)^ := (aiSamp1[m] div 256) + 128;
            inc(pDst);
         end;
      end
      else
      begin
         for m := 0 to bChannels-1 do
         begin
            PSmallint(pDst)^ := aiSamp2[m];
            inc(pDst,2);
         end;
         for m := 0 to bChannels-1 do
         begin
            PSmallint(pDst)^ := aiSamp1[m];
            inc(pDst,2);
         end;
      end;

      { we have decoded the first two samples for this block, so add }
      { two to our decoded count                                     }
      inc(dwDecoded,2);

      { we now need to decode the 'data' section of the ADPCM block.    }
      { this consists of packed 4 bit nibbles.                          }
      {                                                                 }
      { NOTE: we start our count for the number of data bytes to decode }
      { at 2 because we have already decoded the first 2 samples in     }
      { this block.                                                     }

      iFirstNibble := 1;
      for n := 2 to wSamplesPerBlock-1 do
      begin
         for m := 0 to bChannels-1 do
         begin
            if (iFirstNibble > 0) then
            begin
               { we need to grab the next byte to decode--make sure  }
               { that there is a byte for us to grab before continue }
               inc(dwTotalPos);
               if (dwTotalPos > dwSrcLen) then
                   goto adpcmDecode4BitExit;

               { grab the next two nibbles and create sign extended }
               { integers out of them:                              }
               {                                                    }
               { iInput is the first nibble to decode               }
               { iNextInput will be the next nibble decoded         }

               iNextInput   := PShortInt(pSrc)^;
               inc(pSrc);
               iInput       := Sar(iNextInput,4);
               { iNextInput := (iNextInput shl 12) shr 12); }
               asm
                  mov  ax, iNextInput
                  sal  ax, 12
                  sar  ax, 12
                  mov iNextInput, ax
               end;

               iFirstNibble := 0;
            end
            else
            begin
               { put the next sign extended nibble into iInput and }
               { decode it--also set iFirstNibble back to 1 so we  }
               { will read another byte from the source stream on  }
               { the next iteration...                             }

               iInput := iNextInput;
               iFirstNibble := 1;
            end;

            { compute the next Adaptive Scale Factor (ASF) and put }
            { this value in aiDelta for the current channel.       }

            iDelta := aiDelta[m];
            aiDelta[m] :=  Sar((gaiP4[iInput and 15] * Longint(iDelta)),MSADPCM_PSCALE);
            if (aiDelta[m] < MSADPCM_DELTA4_MIN) then
                aiDelta[m] := MSADPCM_DELTA4_MIN;

            { decode iInput (the sign extended 4 bit nibble)--there are     }
            { two steps to this:                                            }
            {                                                               }
            { 1.  predict the next sample using the previous two            }
            {     samples and the predictor coefficients:                   }
            {                                                               }
            {     Prediction = (aiSamp1[channel] * aiCoef1[channel] +       }
            {                   aiSamp2[channel] * aiCoef2[channel]) / 256; }
            {                                                               }
            { 2.  reconstruct the original PCM sample using the encoded     }
            {     sample (iInput), the Adaptive Scale Factor (aiDelta)      }
            {     and the prediction value computed in step 1 above.        }
            {                                                               }
            {     Sample = (iInput * aiDelta[channel]) + Prediction;        }

            lPrediction := Sar((Longint(aiSamp1[m]) * aiCoef1[m]) +
                               (Longint(aiSamp2[m]) * aiCoef2[m]),MSADPCM_CSCALE);
            lSamp := (Longint(iInput) * iDelta) + lPrediction;

            { now we need to clamp lSamp to [-32768..32767]--this value     }
            { will then be a valid 16 bit sample.                           }
            if (lSamp > 32767) then
                lSamp := 32767
            else if (lSamp < -32768) then
                lSamp := -32768;

            { lSamp contains the decoded iInput sample--now write it }
            { out to the destination buffer                          }

            if (bBitsPerSample = 8) then
            begin
               PByte(pDst)^ := (lSamp div 256) + 128;
               inc(pDst);
            end
            else
            begin
               PSmallint(pDst)^ := lSamp;
               inc(pDst,2);
            end;

            { ripple our previous samples down making the new aiSamp1 }
            { equal to the sample we just decoded                     }

            aiSamp2[m] := aiSamp1[m];
            aiSamp1[m] := lSamp;
         end;

         { we have decoded one more complete sample }
         inc(dwDecoded);
      end;
   end;

   { we're done decoding the input data. dwDecoded contains the number }
   { of complete _SAMPLES_ that were decoded. we need to return the    }
   { number of _BYTES_ decoded. so calculate the number of bytes per   }

⌨️ 快捷键说明

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