MMACMDlg.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:43k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {========================================================================}
  2. {=                (c) 1995-98 SwiftSoft Ronald Dittrich                 =}
  3. {========================================================================}
  4. {=                          All Rights Reserved                         =}
  5. {========================================================================}
  6. {=  D 01099 Dresden             = Fax.: +49 (0)351-8037944              =}
  7. {=  Loewenstr.7a                = info@swiftsoft.de                     =}
  8. {========================================================================}
  9. {=  Actual versions on http://www.swiftsoft.de/mmtools.html             =}
  10. {========================================================================}
  11. {=  This code is for reference purposes only and may not be copied or   =}
  12. {=  distributed in any format electronic or otherwise except one copy   =}
  13. {=  for backup purposes.                                                =}
  14. {=                                                                      =}
  15. {=  No Delphi Component Kit or Component individually or in a collection=}
  16. {=  subclassed or otherwise from the code in this unit, or associated   =}
  17. {=  .pas, .dfm, .dcu, .asm or .obj files may be sold or distributed     =}
  18. {=  without express permission from SwiftSoft.                          =}
  19. {=                                                                      =}
  20. {=  For more licence informations please refer to the associated        =}
  21. {=  HelpFile.                                                           =}
  22. {========================================================================}
  23. {=  $Date: 06.09.98 - 02:53:10 $                                        =}
  24. {========================================================================}
  25. Unit MMACMDlg;
  26. {$I COMPILER.INC}
  27. interface
  28. uses
  29. {$IFDEF WIN32}
  30.     Windows,
  31. {$ELSE}
  32.     WinTypes,
  33.     WinProcs,
  34. {$ENDIF}
  35.     SysUtils,
  36.     Classes,
  37.     Controls,
  38.     Forms,
  39.     Dialogs,
  40.     MMSystem,
  41.     MMUtils,
  42.     MMObj,
  43.     MMRegs,
  44.     MMRiff,
  45.     MMWaveIO,
  46.     MMWave,
  47.     MMACM;
  48. type
  49.     EMMACMError = class(Exception);
  50.     TMMACMEnumFormats = (efAll,efInput,efOutput,efConvert,efSuggest,efRestrict);
  51.     TMMACMCodecEnum   = procedure (Sender: TObject; dwFormatTag: DWORD; Description: String; var Continue: Boolean) of object;
  52.     TMMACMFormatEnum  = procedure (Sender: TObject; pwfx: PWaveFormatEx; Description: String; var Continue: Boolean) of object;
  53.     {-- TMMACM ---------------------------------------------------------}
  54.     TMMACM = class(TMMNonVisualComponent)
  55.     private
  56.        FACMPresent   : Boolean;
  57.        FACMVersion   : Longint;
  58.        FNumDrivers   : Longint;
  59.        FNumCodecs    : Longint;
  60.        FNumConverters: Longint;
  61.        FNumFilters   : Longint;
  62.        FMaxFormatSize: Longint;
  63.        FMaxFilterSize: Longint;
  64.        FEnumFormats  : TMMACMEnumFormats;
  65.        FTitle        : String;
  66.        FPWaveFilter  : PWaveFilter;
  67.        FPWaveFormatEx: PWaveFormatEx;
  68.        FSource       : TMMCustomWaveFile;
  69.        FOnCodecEnum  : TMMACMCodecEnum;
  70.        FOnFormatEnum : TMMACMFormatEnum;
  71.        procedure SetSource(aSource: TMMCustomWaveFile);
  72.        procedure SetACMPresent(aValue: Boolean);
  73.        procedure SetNumDrivers(aValue: Longint);
  74.        procedure SetPWaveFormat(aValue: PWaveFormatEx);
  75.        procedure SetPWaveFilter(aValue: PWaveFilter);
  76.        function  GetWave: TMMWave;
  77.     protected
  78.        procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  79.     public
  80.        constructor Create(AOwner: TComponent); override;
  81.        destructor  Destroy; override;
  82.        procedure AllocWaveHeader(var pwfx: PWaveFormatEx);
  83.        procedure FreeWaveHeader(var pwfx: PWaveFormatEX);
  84.        procedure AllocWaveFilter(var pwfltr: PWaveFilter);
  85.        procedure FreeWaveFilter(var pwfltr: PWaveFilter);
  86.        function GetFormatDescription(pwfx: PWaveFormatEx;var sFormatTag,sFormat: String): Boolean;
  87.        function GetFilterDescription(pwfltr: PWaveFilter;var sFilterTag,sFilter: String): Boolean;
  88.        function EnumerateFormats(wFormatTag: integer): Boolean;
  89.        function EnumerateCodecs: Boolean;
  90.        function SuggestFormat(pwfxSrc: PWaveFormatEx; dwSuggest: Longint): Boolean;
  91.        function ChooseFormat(pwfxSrc: PWaveFormatEx; Title: String): Boolean;
  92.        function ChooseFilter(pwfltrSrc: PWaveFilter; Title: String): Boolean;
  93.        function QueryConvert(pwfxDst: PWaveFormatEx; pwfltr: PWaveFilter): Boolean;
  94.        function ProcessFile(const FileName: TFileName; pwfxDst: PWaveFormatEx; pwfltr: PWaveFilter): Boolean;
  95.        function FilterFile(const FileName: TFileName): Boolean;
  96.        function CreateFile(const FileName: TFileName): Boolean;
  97.        function ConvertFile(const FileName: TFileName): Boolean;
  98.        property DriverVersion: Longint read FacmVersion;
  99.        property PWaveFormat: PWaveFormatEx read FPWaveFormatEx write SetPWaveFormat;
  100.        property MaxFormatSize: Longint read FMaxFormatSize;
  101.        property PFilter: PWaveFilter read FPWaveFilter write SetPWaveFilter;
  102.        property MaxFilterSize: Longint read FMaxFilterSize;
  103.        property Wave: TMMWave read GetWave;
  104.     published
  105.        property OnCodecEnum: TMMACMCodecEnum read FOnCodecEnum write FOnCodecEnum;
  106.        property OnFormatEnum: TMMACMFormatEnum read FOnFormatEnum write FOnFormatEnum;
  107.        property ACMPresent: Boolean read FACMPresent write SetACMPresent;
  108.        property NumDrivers: Longint read FNumDrivers write SetNumDrivers;
  109.        property NumCodecs: Longint read FNumCodecs write SetNumDrivers;
  110.        property NumConverters: Longint read FNumConverters write SetNumDrivers;
  111.        property NumFilters: Longint read FNumFilters write SetNumDrivers;
  112.        property EnumFormats: TMMACMEnumFormats read FEnumFormats write FEnumFormats default efAll;
  113.        property Title: String read FTitle write FTitle;
  114.        property Source: TMMCustomWaveFile read FSource write SetSource;
  115.     end;
  116. function acmBuildTrueSpeechHeader: PTrueSpeechWaveFormat;
  117. function acmBuildGSM610Header(SampleRate: integer): PGSM610WaveFormat;
  118. function acmBuildADPCMHeader(SampleRate, Channels: integer): PADPCMWaveFormat;
  119. function acmBuildMPEG1Header(SampleRate, Bitrate, Channels: integer): PMPEG1WaveFormat;
  120. function acmBuildMP3Header(SampleRate, Bitrate, Channels: integer): PWaveFormatEx;
  121. function acmBuildMPEGHeader(Layer,SampleRate, Bitrate, Channels: integer): PWaveFormatEx;
  122. function acmGetFormatDescription(pwfx: PWaveFormatEx; var sFormatTag,sFormat: String): Boolean;
  123. function acmGetFilterDescription(pwfltr: PWaveFilter; var sFilterTag,sFilter: String): Boolean;
  124. procedure acmSaveFormatToRegistry(pwfx: PWaveFormatEx;
  125.                                   RootKey: integer; const LocalKey,Field: String);
  126. function  acmGetFormatFromRegistry(RootKey: integer; const LocalKey,Field: String): PWaveFormatEx;
  127. implementation
  128. uses MMString;
  129. {========================================================================}
  130. function acmBuildTrueSpeechHeader: PTrueSpeechWaveFormat;
  131. begin
  132.    Result := GlobalAllocMem(sizeOf(TTrueSpeechWaveFormat));
  133.    with Result^ do
  134.    begin
  135.       wfx.wFormatTag     := WAVE_FORMAT_DSPGROUP_TRUESPEECH;
  136.       wfx.nChannels      := 1;
  137.       wfx.nSamplesPerSec := 8000;
  138.       wfx.nAvgBytesPerSec:= 1067;
  139.       wfx.nBlockAlign    := 32;
  140.       wfx.wBitsPerSample := 1;
  141.       wfx.cbSize         := 32;
  142.       wRevision          := 1;
  143.       nSamplesPerBlock   := $00F0;
  144.    end;
  145. end;
  146. {========================================================================}
  147. function acmBuildGSM610Header(SampleRate: integer): PGSM610WaveFormat;
  148. begin
  149.    Result := GlobalAllocMem(sizeOf(TGSM610WaveFormat));
  150.    with Result^ do
  151.    begin
  152.       wfx.wFormatTag     := WAVE_FORMAT_GSM610;
  153.       wfx.nChannels      := 1;
  154.       wfx.nSamplesPerSec := SampleRate;
  155.       wfx.wBitsPerSample := 0;
  156.       wfx.nAvgBytesPerSec:= 0;
  157.       wfx.nBlockAlign    := 65;
  158.       wfx.cbSize         := 2;
  159.       wSamplesPerBlock   := 320;
  160.       case Word(SampleRate) of
  161.          8000 : wfx.nAvgBytesPerSec := 1625;
  162.          11025: wfx.nAvgBytesPerSec := 2239;
  163.          22050: wfx.nAvgBytesPerSec := 4478;
  164.          44100: wfx.nAvgBytesPerSec := 8957;
  165.       end;
  166.    end;
  167. end;
  168. {========================================================================}
  169. function acmBuildADPCMHeader(SampleRate, Channels: integer): PADPCMWaveFormat;
  170. const
  171.      MSADPCM_NUM_COEF = 7;
  172.      gaiCoef1: array[0..MSADPCM_NUM_COEF-1]of Smallint = (256,  512,  0, 192, 240,  460,  392);
  173.      gaiCoef2: array[0..MSADPCM_NUM_COEF-1]of Smallint = (0, -256,  0,  64,   0, -208, -232);
  174. var
  175.    wBitsperSample,
  176.    wHeaderBytes,
  177.    w: Word;
  178.    dw: DWORD;
  179. begin
  180.    wBitsPerSample := 4;
  181.    { fill in destination header with appropriate ADPCM stuff based }
  182.    { on source PCM header...                                       }
  183.    Result := GlobalAllocMem(sizeOf(TADPCMWaveFormat)+MSADPCM_NUM_COEF*sizeOf(TADPCMCOEFSET));
  184.    with Result^ do
  185.    begin
  186.       wfx.wFormatTag     := WAVE_FORMAT_ADPCM;
  187.       wfx.nSamplesPerSec := SampleRate;
  188.       wfx.nChannels      := Channels;
  189.       wfx.wBitsPerSample := wBitsperSample;
  190.       { choose a block alignment that makes sense for the sample rate  }
  191.       { that the original PCM data is. basically, this needs to be     }
  192.       { some reasonable number to allow efficient streaming, etc.      }
  193.       {                                                                }
  194.       { don't let block alignment get too small...                     }
  195.       wfx.nBlockAlign := 256 * Channels;
  196.       if (SampleRate > 11025) then
  197.           wfx.nBlockAlign := wfx.nBlockAlign * (SampleRate div 11000);
  198.       { compute that 'samples per block' that will be in the encoded   }
  199.       { ADPCM data blocks. this is determined by subtracting out the   }
  200.       { 'other info' contained in each block--a block is composed of   }
  201.       {  a header followed by the encoded data.                        }
  202.       {                                                                }
  203.       { the block header is composed of the following data:            }
  204.       {     1 byte predictor per channel                               }
  205.       {     2 byte delta per channel                                   }
  206.       {     2 byte first sample per channel                            }
  207.       {     2 byte second sample per channel                           }
  208.       {                                                                }
  209.       { this gives us (7 * wChannels) bytes of header information that }
  210.       { contains our first two full samples (so we add two below).     }
  211.       wHeaderBytes := (7 * Channels);
  212.       w := (wfx.nBlockAlign - wHeaderBytes) * 8;
  213.       wSamplesPerBlock := (w div (wBitsPerSample * Channels)) + 2;
  214.       { now compute the avg bytes per second (man this code bites!) }
  215.       dw := ((Longint(wBitsPerSample) * Channels * Longint(SampleRate)) div 8);
  216.       wfx.nAvgBytesPerSec := (dw + wHeaderBytes + ((dw div wfx.nBlockAlign) * wHeaderBytes));
  217.       { fill in the cbSize field of the extended wave format header.    }
  218.       { this number is the number of _EXTRA BYTES_ *after* the end      }
  219.       { of the WAVEFORMATEX structure that are need for the compression }
  220.       { format.                                                         }
  221.       {                                                                 }
  222.       { for Microsoft's 4 Bit ADPCM format, this number is 32:          }
  223.       wfx.cbSize := sizeof(TADPCMWAVEFORMAT) - sizeof(TWaveFormatEx) +
  224.                            ((MSADPCM_NUM_COEF-1) * sizeof(TADPCMCOEFSET));
  225.       { copy the Microsoft 4 Bit ADPCM coef's into the header }
  226.       wNumCoef := MSADPCM_NUM_COEF;
  227.       for w := 0 to MSADPCM_NUM_COEF-1 do
  228.       begin
  229.          aCoef[w].iCoef1 := gaiCoef1[w];
  230.          aCoef[w].iCoef2 := gaiCoef2[w];
  231.       end;
  232.    end;
  233. end;
  234. {========================================================================}
  235. function acmBuildMPEG1Header(SampleRate, Bitrate, Channels: integer): PMPEG1WaveFormat;
  236. var
  237.    wSamplesPerBlock: Integer;
  238. begin
  239.    // Example: SampleRate: 44100, Bitrate: 128000, Channels: 2
  240.    Result := GlobalAllocMem(sizeOf(TMPEG1WaveFormat));
  241.    with Result^ do
  242.    begin
  243.       wfx.wFormatTag     := WAVE_FORMAT_MPEG;
  244.       wfx.nChannels      := Channels;
  245.       wfx.nSamplesPerSec := SampleRate;
  246.       wfx.wBitsPerSample := 16;
  247.       wfx.nAvgBytesPerSec:= 0;
  248.       wfx.nBlockAlign    := 144 * (BitRate * wfx.nChannels) div SampleRate;
  249.       wfx.cbSize         := 22;
  250.       wSamplesPerBlock   := 1120;
  251.       fwHeadLayer        := ACM_MPEG_LAYER2;
  252.       fwHeadModeExt      := $0F;
  253.       wHeadEmphasis      := 1;   // no emphasis
  254.       fwHeadFlags        := ACM_MPEG_ID_MPEG1;// or ACM_MPEG_PROTECTIONBIT or ACM_MPEG_COPYRIGHT or ACM_MPEG_ORIGINALHOME;
  255.       dwPTSLow           := 0;
  256.       dwPTSHigh          := 0;
  257.       if (wfx.nChannels = 1) then
  258.           fwHeadMode     := ACM_MPEG_SINGLECHANNEL
  259.       else
  260.           fwHeadMode     := ACM_MPEG_JOINTSTEREO;
  261.       dwHeadBitrate      := BitRate * wfx.nChannels;
  262.       wfx.nAvgBytesPerSec:= (((wfx.nSamplesPerSec * 100) div wSamplesPerBlock)*wfx.nBlockAlign) div 100;
  263.    end;
  264. end;
  265. {========================================================================}
  266. function acmBuildMP3Header(SampleRate, Bitrate, Channels: integer): PWaveFormatEx;
  267. const
  268.     MP3Ext: array[0..11] of Byte = ($01,$00,$02,$00,$00,$00,$00,$00,$01,$00,$71,$05);
  269. var
  270.     BlockAlign: Double;
  271. begin
  272.     Result := GlobalAllocMem(sizeOf(TWaveFormatEx)+12);
  273.     with Result^ do
  274.     begin
  275.        wFormatTag     := WAVE_FORMAT_MPEG_LAYER3;
  276.        nChannels      := Channels;
  277.        nSamplesPerSec := SampleRate;
  278.        wBitsPerSample := 0;
  279.        nBlockAlign    := 1;
  280.        BlockAlign     := (144 * BitRate) / SampleRate;
  281.        nAvgBytesPerSec:= Round((((SampleRate*100) / 1152)*BlockAlign) / 100);
  282.        cbSize         := sizeOf(MP3Ext);
  283.        GlobalMoveMem(MP3Ext,(PChar(Result)+sizeOf(TWaveFormatEx))^,sizeOf(MP3Ext));
  284.        PWord(PChar(Result)+sizeOf(TWaveFormatEx)+6)^ := Trunc(BlockAlign);
  285.     end;
  286. end;
  287. {========================================================================}
  288. function acmBuildMPEGHeader(Layer,SampleRate, Bitrate, Channels: integer): PWaveFormatEx;
  289. begin
  290.    if (Layer = 2) then
  291.        Result := Pointer(acmBuildMPEG1Header(SampleRate, BitRate, Channels))
  292.    else if (Layer = 3) then
  293.        Result := Pointer(acmBuildMP3Header(SampleRate, BitRate, Channels))
  294.    else
  295.        Result := nil;
  296. end;
  297. {========================================================================}
  298. function acmGetFormatDescription(pwfx: PWaveFormatEx;var sFormatTag,sFormat: String): Boolean;
  299. var
  300.    mmr : MMRESULT;
  301.    aftd: TACMFormatTagDetails;
  302.    afd : TACMFormatDetails;
  303. begin
  304.    { get the name for the format tag of the specified format }
  305.    Result := False;
  306.    if (pwfx <> nil) then
  307.    begin
  308.       if acmDLLLoaded and (HiWord(acmGetVersion) >= $0200) then
  309.       begin
  310.          { get the name for the format tag of the specified format }
  311.          { initialize all unused members of the TACMFORMATTAGDETAILS }
  312.          { structure to zero                                         }
  313.          FillChar(aftd, sizeOf(aftd), 0);
  314.          { fill in the required members of the TACMFORMATTAGDETAILS }
  315.          { structure for the ACM_FORMATTAGDETAILSF_FORMATTAG query  }
  316.          aftd.cbStruct    := sizeOf(aftd);
  317.          aftd.dwFormatTag := pwfx^.wFormatTag;
  318.          { ask the ACM to find the first available driver that }
  319.          { supports the specified format tag                   }
  320.          mmr := acmFormatTagDetails(0, @aftd, ACM_FORMATTAGDETAILSF_FORMATTAG);
  321.          if (mmr = 0) then
  322.              { copy the format tag name into the caller's buffer }
  323.              sFormatTag := StrPas(aftd.szFormatTag)
  324.          else
  325.              { no ACM driver is available that supports the }
  326.              { specified format tag                         }
  327.              wioGetFormatName(pwfx, sFormatTag);
  328.          { get the description of the attributes for the specified format }
  329.          { initialize all unused members of the ACMFORMATDETAILS }
  330.          { structure to zero                                     }
  331.          FillChar(afd, sizeOf(afd), 0);
  332.          { fill in the required members of the ACMFORMATDETAILS  }
  333.          { structure for the ACM_FORMATDETAILSF_FORMAT query     }
  334.          afd.cbStruct    := sizeof(afd);
  335.          afd.dwFormatTag := pwfx^.wFormatTag;
  336.          afd.pwfx        := pwfx;
  337.          { the cbwfx member must be initialized to the total size  }
  338.          { in bytes needed for the specified format. for a PCM     }
  339.          { format, the cbSize member of the WAVEFORMATEX structure }
  340.          { is not valid.                                           }
  341.          afd.cbwfx := wioSizeOfWaveFormat(pwfx);
  342.          { ask the ACM to find the first available driver that }
  343.          { supports the specified format                       }
  344.          mmr := acmFormatDetails(0, @afd, ACM_FORMATDETAILSF_FORMAT);
  345.          if (mmr = 0) then
  346.              { copy the format attributes description into the caller's buffer }
  347.              sFormat := StrPas(afd.szFormat)
  348.          else
  349.              { no ACM driver is available that supports the specified format }
  350.              wioGetFormat(pwfx, sFormat);
  351.       end
  352.       else
  353.       begin
  354.          wioGetFormatName(pwfx, sFormatTag);
  355.          wioGetFormat(pwfx, sFormat);
  356.       end;
  357.       Result := True;
  358.    end;
  359. end;
  360. {========================================================================}
  361. function acmGetFilterDescription(pwfltr: PWaveFilter;var sFilterTag,sFilter: String): Boolean;
  362. var
  363.    mmr : MMRESULT;
  364.    aftd: TACMFilterTagDetails;
  365.    afd : TACMFilterDetails;
  366. begin
  367.    { get the name for the filter tag of the specified filter }
  368.    Result := False;
  369.    if acmDLLLoaded and (HiWord(acmGetVersion) >= $0200) and (pwfltr <> nil) then
  370.    begin
  371.       { initialize all unused members of the TACMFILTERTAGDETAILS }
  372.       { structure to zero                                         }
  373.       FillChar(aftd, sizeOf(aftd), 0);
  374.       { fill in the required members of the TACMFILTERTAGDETAILS }
  375.       { structure for the ACM_FILTERTAGDETAILSF_FILTERTAG query  }
  376.       aftd.cbStruct    := sizeOf(aftd);
  377.       aftd.dwFilterTag := pwfltr^.dwFilterTag;
  378.       { ask the ACM to find the first available driver that }
  379.       { supports the specified filter tag                   }
  380.       mmr := acmFilterTagDetails(0, @aftd, ACM_FILTERTAGDETAILSF_FILTERTAG);
  381.       if (mmr <> 0) then
  382.          { no ACM driver is available that supports the specified filter tag }
  383.          sFilterTag := LoadResStr(IDS_WAVEUNKNOWN) + ' FilterTag'
  384.       else
  385.          { copy the filter tag name into the caller's buffer }
  386.          sFilterTag := StrPas(aftd.szFilterTag);
  387.       { get the description of the attributes for the specified filter }
  388.       { initialize all unused members of the TACMFILTERDETAILS }
  389.       { structure to zero                                      }
  390.       FillChar(afd, sizeOf(afd), 0);
  391.       { fill in the required members of the TACMFILTERDETAILS }
  392.       { structure for the ACM_FILTERDETAILSF_FILTER query     }
  393.       afd.cbStruct    := sizeOf(afd);
  394.       afd.dwFilterTag := pwfltr^.dwFilterTag;
  395.       afd.pwfltr      := pwfltr;
  396.       afd.cbwfltr     := pwfltr^.cbStruct;
  397.       { ask the ACM to find the first available driver that }
  398.       { supports the specified filter                       }
  399.       mmr := acmFilterDetails(0, @afd, ACM_FILTERDETAILSF_FILTER);
  400.       if (mmr <> 0) then
  401.           { no ACM driver is available that supports the specified filter }
  402.           sFilter := LoadResStr(IDS_WAVEUNKNOWN)+ ' Filter'
  403.       else
  404.           { copy the filter attributes description into the caller's buffer }
  405.           sFilter := StrPas(afd.szFilter);
  406.       Result := True;
  407.    end;
  408. end;
  409. {========================================================================}
  410. procedure acmSaveFormatToRegistry(pwfx: PWaveFormatEx;
  411.                                   RootKey: integer; const LocalKey,Field: String);
  412. begin
  413.    SaveInRegistryBinary(RootKey,LocalKey,Field,pwfx^,wioSizeOfWaveFormat(pwfx));
  414. end;
  415. {========================================================================}
  416. function  acmGetFormatFromRegistry(RootKey: integer; const LocalKey,Field: String): PWaveFormatEx;
  417. var
  418.    wfx: array[0..1024] of Char;
  419. begin
  420.    if GetFromRegistryBinary(RootKey,LocalKey,Field,wfx,sizeOf(wfx)) > 0 then
  421.       Result := wioCopyWaveFormat(@wfx)
  422.    else
  423.       Result := nil;
  424. end;
  425. {== TMMACM =============================================================}
  426. constructor TMMACM.Create(aOwner:TComponent);
  427. begin
  428.    inherited Create(aOwner);
  429.    FACMPresent := False;
  430.    FEnumFormats := efAll;
  431.    FSource := nil;
  432.    FTitle := LoadResStr(IDS_ACMSELECT);
  433.    if (not acmDLLLoaded) then
  434.    begin
  435.       MessageDlg(LoadResStr(IDS_ACMNOACM), mtInformation, [mbOk], 0);
  436.    end
  437.    else
  438.    begin
  439.       FacmVersion := acmGetVersion;
  440.       if HiWord(FacmVersion) < $0200 then
  441.       begin
  442.          MessageDlg(Format(LoadResStr(IDS_ACMBADVERSION),
  443.                     [HiWord(FacmVersion) shr 8,HiWord(FacmVersion) and $FF]),
  444.                     mtInformation, [mbOk], 0);
  445.          exit;
  446.       end;
  447.       FACMPresent := True;
  448.       acmMetrics(0, ACM_METRIC_COUNT_DRIVERS, @FNumDrivers);
  449.       acmMetrics(0, ACM_METRIC_COUNT_CODECS, @FNumCodecs);
  450.       acmMetrics(0, ACM_METRIC_COUNT_CONVERTERS, @FNumConverters);
  451.       acmMetrics(0, ACM_METRIC_COUNT_FILTERS, @FNumFilters);
  452.       acmMetrics(0, ACM_METRIC_MAX_SIZE_FORMAT, @FMaxFormatSize);
  453.       acmMetrics(0, ACM_METRIC_MAX_SIZE_FILTER, @FMaxFilterSize);
  454.       AllocWaveHeader(FPWaveFormatEx);
  455.       AllocWaveFilter(FPWaveFilter);
  456.    end;
  457.    ErrorCode := ComponentRegistered(InitCode, Self, ClassName);
  458.    if (ErrorCode <> 0) then RegisterFailed(InitCode, Self , ClassName);
  459. end;
  460. {-- TMMACM -------------------------------------------------------------}
  461. Destructor TMMACM.Destroy;
  462. begin
  463.    FreeWaveHeader(FPWaveFormatEx);
  464.    FreeWaveFilter(FPWaveFilter);
  465.    inherited Destroy;
  466. end;
  467. {-- TMMACM -------------------------------------------------------------}
  468. procedure TMMACM.SetACMPresent(aValue: Boolean);
  469. begin
  470.    { dummy }
  471. end;
  472. {-- TMMACM -------------------------------------------------------------}
  473. procedure TMMACM.SetNumDrivers(aValue: Longint);
  474. begin
  475.    { dummy }
  476. end;
  477. {-- TMMACM -------------------------------------------------------------}
  478. procedure TMMACM.SetSource(aSource: TMMCustomWaveFile);
  479. begin
  480.    if (aSource is TMMCustomWaveFile) or (aSource = Nil) then
  481.    begin
  482.       if (FSource <> aSource) then
  483.       begin
  484.          FSource := aSource;
  485.       end;
  486.    end;
  487. end;
  488. {-- TMMACM -------------------------------------------------------------}
  489. procedure TMMACM.Notification(AComponent: TComponent; Operation: TOperation);
  490. begin
  491.    inherited Notification(AComponent, Operation);
  492.    if (Operation = opRemove) and (AComponent = FSource) then
  493.       FSource := Nil;
  494. end;
  495. {-- TMMACM -------------------------------------------------------------}
  496. procedure TMMACM.SetPWaveFormat(aValue: PWaveFormatEx);
  497. begin
  498.    if (aValue <> nil) then
  499.       move(aValue^, FPWaveFormatEx^, sizeOf(TWaveFormatEx) + aValue^.cbSize);
  500. end;
  501. {-- TMMACM -------------------------------------------------------------}
  502. procedure TMMACM.SetPWaveFilter(aValue: PWaveFilter);
  503. begin
  504.    if (aValue <> nil) then
  505.       move(aValue^, FPWaveFilter^, aValue^.cbStruct);
  506. end;
  507. {-- TMMACM -------------------------------------------------------------}
  508. function TMMACM.GetWave: TMMWave;
  509. begin
  510.    Result := nil;
  511.    if assigned(FSource) then
  512.    begin
  513.       Result := (FSource as TMMCustomWaveFile).Wave;
  514.    end;
  515. end;
  516. {-- TMMACM -------------------------------------------------------------}
  517. procedure TMMACM.AllocWaveHeader(var pwfx: PWaveFormatEx);
  518. begin
  519.    if ACMPresent and (FMaxFormatSize > 0) then
  520.       pwfx := GlobalAllocMem(FMaxFormatSize)
  521.    else
  522.       pwfx := nil;
  523. end;
  524. {-- TMMACM -------------------------------------------------------------}
  525. procedure TMMACM.FreeWaveHeader(var pwfx: PWaveFormatEx);
  526. begin
  527.    if ACMPresent then
  528.       GlobalFreeMem(pointer(pwfx));
  529. end;
  530. {-- TMMACM -------------------------------------------------------------}
  531. procedure TMMACM.AllocWaveFilter(var pwfltr: PWaveFilter);
  532. begin
  533.    if ACMPresent and (FMaxFilterSize > 0) then
  534.       pwfltr := GlobalAllocMem(FMaxFilterSize)
  535.    else
  536.       pwfltr := nil;
  537. end;
  538. {-- TMMACM -------------------------------------------------------------}
  539. procedure TMMACM.FreeWaveFilter(var pwfltr: PWaveFilter);
  540. begin
  541.    if ACMPresent then
  542.       GlobalFreeMem(pointer(pwfltr));
  543. end;
  544. {-- TMMACM -------------------------------------------------------------}
  545. function TMMACM.GetFormatDescription(pwfx: PWaveFormatEx;var sFormatTag,sFormat: String): Boolean;
  546. begin
  547.    Result := acmGetFormatDescription(pwfx,sFormatTag,sFormat);
  548. end;
  549. {-- TMMACM -------------------------------------------------------------}
  550. function TMMACM.GetFilterDescription(pwfltr: PWaveFilter;var sFilterTag,sFilter: String): Boolean;
  551. begin
  552.    Result := acmGetFilterDescription(pwfltr,sFilterTag,sFilter);
  553. end;
  554. {-----------------------------------------------------------------------}
  555. function acmDriverEnumCallback(hadid: THACMDRIVERID; dwInstance, fdwSupport: DWORD): Boolean; stdcall;
  556. begin
  557.    if (fdwSupport and ACMDRIVERDETAILS_SUPPORTF_CODEC <> 0) or
  558.       (fdwSupport and ACMDRIVERDETAILS_SUPPORTF_CONVERTER <> 0) then
  559.    begin
  560.       TList(dwInstance).Add(Pointer(hadid));
  561.    end;
  562. end;
  563. {-----------------------------------------------------------------------}
  564. function acmFormatEnumTagCallback(hadid: THACMDRIVERID; paftd:  PACMFORMATTAGDETAILS;
  565.                                   dwInstance, fdwSupport: DWORD): Boolean; stdcall;
  566. begin
  567.    Result := False;
  568.    if (dwInstance <> 0) then
  569.    with TMMACM(dwInstance) do
  570.    begin
  571.       if assigned(FOnCodecEnum) then
  572.          FOnCodecEnum(TMMACM(dwInstance), paftd.dwFormatTag, StrPas(paftd.szFormatTag), Result);
  573.    end;
  574. end;
  575. {-----------------------------------------------------------------------}
  576. function acmFormatEnumCallback(hadid: THACMDRIVERID; pafd:  PACMFORMATDETAILS;
  577.                                dwInstance, fdwSupport: DWORD): Boolean; stdcall;
  578. begin
  579.    Result := False;
  580.    if (dwInstance <> 0) then
  581.    with TMMACM(dwInstance) do
  582.    begin
  583.       if assigned(FOnFormatEnum) then
  584.          FOnFormatEnum(TMMACM(dwInstance), pafd.pwfx, StrPas(pafd.szFormat), Result);
  585.    end;
  586. end;
  587. {-- TMMACM -------------------------------------------------------------}
  588. function TMMACM.EnumerateCodecs: Boolean;
  589. var
  590.    aftd: TACMFORMATTAGDETAILS;
  591. begin
  592.    Result := False;
  593.    if not ACMPresent then exit;
  594.    FillChar(aftd,sizeOf(aftd),0);
  595.    aftd.cbStruct := sizeOf(aftd);
  596.    Result := acmFormatTagEnum(0, @aftd, acmFormatEnumTagCallback, Longint(Self), 0) = 0;
  597.    if not Result then // Bug Fix for Win98, some times the first call fails...
  598.       Result := acmFormatTagEnum(0, @aftd, acmFormatEnumTagCallback, Longint(Self), 0) = 0;
  599. end;
  600. {-- TMMACM -------------------------------------------------------------}
  601. function TMMACM.EnumerateFormats(wFormatTag: integer): Boolean;
  602. var
  603.    afd: TACMFORMATDETAILS;
  604.    fdwEnum: DWORD;
  605. begin
  606.    Result := False;
  607.    if not ACMPresent then exit;
  608.    FillChar(afd,sizeOf(afd),0);
  609.    afd.cbStruct := sizeOf(afd);
  610.    afd.pwfx := GlobalAllocMem(FMaxFormatSize);
  611.    try
  612.       afd.pwfx.wFormatTag := wFormatTag;
  613.       afd.dwFormatTag := wFormatTag;
  614.       afd.cbwfx := FMaxFormatSize;
  615.       fdwEnum := ACM_FORMATENUMF_WFORMATTAG;
  616.       case FEnumFormats of
  617.           efInput  : fdwEnum := fdwEnum or ACM_FORMATENUMF_INPUT;
  618.           efOutput : fdwEnum := fdwEnum or ACM_FORMATENUMF_OUTPUT;
  619.       end;
  620.       Result := acmFormatEnum(0, @afd, acmFormatEnumCallback, Longint(Self), fdwEnum) = 0;
  621.    finally
  622.       GlobalFreeMem(Pointer(afd.pwfx));
  623.    end;
  624. end;
  625. {-- TMMACM -------------------------------------------------------------}
  626. function TMMACM.SuggestFormat(pwfxSrc: PWaveFormatEx; dwSuggest: Longint): Boolean;
  627. var
  628.    mmr: MMRESULT;
  629.    cbwfx,cbwfxSrc: Longint;
  630. begin
  631.     Result := False;
  632.     if not ACMPresent or (pwfxSrc = nil) then exit;
  633.    { just in case no ACM driver is installed for the source format and }
  634.    { the source has a larger format size than the largest enabled ACM  }
  635.    { driver...                                                         }
  636.    cbwfxSrc := wioSizeOfWaveFormat(pwfxSrc);
  637.    cbwfx    := Max(FMaxFormatSize, cbwfxSrc);
  638.    if (cbwfx > FMaxFormatSize) then
  639.    begin
  640.       FMaxFormatSize := cbwfx;
  641.       FreeWaveHeader(FPWaveFormatEx);
  642.       AllocWaveHeader(FPWaveFormatEx);
  643.    end;
  644.    {$IFDEF WIN32}
  645.    {$IFDEF TRIAL}
  646.    {$DEFINE _HACK2}
  647.    {$I MMHACK.INC}
  648.    {$ENDIF}
  649.    {$ENDIF}
  650.    { 'suggest anything' }
  651.    mmr := acmFormatSuggest(0, pwfxSrc, FPWaveFormatEx, cbwfx, dwSuggest);
  652.    if (mmr <> 0) then
  653.       move(pwfxSrc^, FPWaveFormatEx^, cbwfxSrc);
  654.    Result := True;
  655. end;
  656. {-- TMMACM -------------------------------------------------------------}
  657. function TMMACM.ChooseFormat(pwfxSrc: PWaveFormatEx; Title: String): Boolean;
  658. Label Again;
  659. var
  660.    mmr : MMRESULT;
  661.    aBuf: array[0..255] of char;
  662.    afc : TACMFORMATCHOOSE;
  663.    cbwfx,cbwfxSrc: Longint;
  664.    TryCount: integer;
  665. begin
  666.    Result := False;
  667.    if not ACMPresent then exit;
  668.    { initialize the TACMFORMATCHOOSE members }
  669.    FillChar(afc, sizeOf(afc), 0);
  670.    if pwfxSrc <> nil then
  671.    begin
  672.       { just in case no ACM driver is installed for the source format and }
  673.       { the source has a larger format size than the largest enabled ACM  }
  674.       { driver...                                                         }
  675.       cbwfxSrc := wioSizeOfWaveFormat(pwfxSrc);
  676.       cbwfx    := Max(FMaxFormatSize, cbwfxSrc);
  677.       if (cbwfx > FMaxFormatSize) then
  678.       begin
  679.          FMaxFormatSize := cbwfx;
  680.          FreeWaveHeader(FPWaveFormatEx);
  681.          AllocWaveHeader(FPWaveFormatEx);
  682.       end;
  683.       { copy to our struc to init the dialog }
  684.       move(pwfxSrc^, FPWaveFormatEx^, cbwfxSrc);
  685.       afc.fdwStyle := afc.fdwStyle or ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT;
  686.    end;
  687.    afc.cbStruct        := sizeof(afc);
  688.    if (Owner <> nil) then
  689.        afc.hwndOwner   := (Owner as TWinControl).Handle;
  690.    afc.pwfx            := FPWaveFormatEx;
  691.    afc.cbwfx           := FMaxFormatSize;
  692.    if (Title <> '') then
  693.        afc.pszTitle    := StrPCopy(aBuf, Title);
  694.    afc.szFormatTag[0]  := #0;
  695.    afc.szFormat[0]     := #0;
  696.    afc.pszName         := nil;
  697.    afc.cchName         := 0;
  698.    afc.pwfxEnum        := nil;
  699.    afc.fdwEnum         := 0;
  700.    {$IFDEF WIN32}
  701.    {$IFDEF TRIAL}
  702.    {$DEFINE _HACK3}
  703.    {$I MMHACK.INC}
  704.    {$ENDIF}
  705.    {$ENDIF}
  706.    case FEnumFormats of
  707.        efInput  : afc.fdwEnum := ACM_FORMATENUMF_INPUT;
  708.        efOutput : afc.fdwEnum := ACM_FORMATENUMF_OUTPUT;
  709.        efConvert: if (pwfxSrc <> nil) then
  710.                   begin
  711.                      afc.fdwEnum := ACM_FORMATENUMF_CONVERT;
  712.                      afc.pwfxEnum:= pwfxSrc;
  713.                   end;
  714.        efSuggest: if (pwfxSrc <> nil) then
  715.                   begin
  716.                      afc.fdwEnum := ACM_FORMATENUMF_SUGGEST;
  717.                      afc.pwfxEnum:= pwfxSrc;
  718.                   end;
  719.        efRestrict:if (pwfxSrc <> nil) then
  720.                   begin
  721.                      afc.fdwEnum := ACM_FORMATENUMF_WFORMATTAG;
  722.                      afc.pwfxEnum:= pwfxSrc;
  723.                   end;
  724.         else afc.fdwEnum  := 0;
  725.    end;
  726.    afc.hInstance       := 0;
  727.    afc.pszTemplateName := nil;
  728.    afc.lCustData       := 0;
  729.    afc.pfnHook         := nil;
  730.    TryCount := 0;
  731. Again:
  732.    mmr := acmFormatChoose(@afc);
  733.    if (mmr <> 0) then
  734.    begin
  735.       if (mmr <> ACMERR_CANCELED) then
  736.       begin
  737.          inc(TryCount);
  738.          if (TryCount = 1) then goto Again; // Bug Fix for Win98
  739.          raise EMMACMError.Create('acmFormatChoose failed with error '+IntToStr(mmr));
  740.       end;
  741.       exit;
  742.    end;
  743.    Result := True;
  744. end;
  745. {-- TMMACM -------------------------------------------------------------}
  746. function TMMACM.ChooseFilter(pwfltrSrc: PWaveFilter; Title: String): Boolean;
  747. var
  748.    afc    : TACMFilterChoose;
  749.    aBuf   : array[0..255] of char;
  750.    mmr    : MMRESULT;
  751.    cbwfltr: Longint;
  752. begin
  753.    Result := False;
  754.    if not ACMPresent or (FNumFilters <= 0) then exit;
  755.    { initialize the TACMFILTERCHOOSE members }
  756.    FillChar(afc, sizeOf(afc), 0);
  757.    cbwfltr := Max(FMaxFilterSize, sizeof(TWaveFilter));
  758.    if (pwfltrSrc <> nil) then
  759.    begin
  760.       if (cbwfltr > FMaxFilterSize) then
  761.       begin
  762.          FMaxFilterSize := cbwfltr;
  763.          FreeWaveFilter(FPWaveFilter);
  764.          AllocWaveFilter(FPWaveFilter);
  765.       end;
  766.       move(pwfltrSrc^, FPWaveFilter, pwfltrSrc^.cbStruct);
  767.       afc.fdwStyle := ACMFILTERCHOOSE_STYLEF_INITTOFILTERSTRUCT;
  768.    end;
  769.    afc.cbStruct        := sizeOf(afc);
  770.    if (Owner <> nil) then
  771.        afc.hwndOwner   := (Owner as TWinControl).Handle;
  772.    afc.pwfltr          := FPWaveFilter;
  773.    afc.cbwfltr         := cbwfltr;
  774.    if (Title <> '') then
  775.        afc.pszTitle    := StrPCopy(aBuf, Title);
  776.    afc.szFilterTag[0]  := #0;
  777.    afc.szFilter[0]     := #0;
  778.    afc.pszName         := nil;
  779.    afc.cchName         := 0;
  780.    afc.fdwEnum         := 0;
  781.    mmr := acmFilterChoose(@afc);
  782.    if (mmr <> 0) then
  783.    begin
  784.       if (mmr <> ACMERR_CANCELED) then
  785.          raise EMMACMError.Create('acmFilterChoose failed with error '+IntToStr(mmr));
  786.       exit;
  787.    end;
  788.    Result := True;
  789. end;
  790. {-- TMMACM -------------------------------------------------------------}
  791. function TMMACM.QueryConvert(pwfxDst: PWaveFormatEx; pwfltr: PWaveFilter): Boolean;
  792. var
  793.    mmr: MMRESULT;
  794. begin
  795.    Result := False;
  796.    if ACMPresent and (FSource<>nil) and (Wave<>nil) then
  797.    begin
  798.       mmr := acmStreamOpen(nil, 0, Wave.PWaveFormat, pwfxDst, pwfltr, 0, 0,
  799.                            ACM_STREAMOPENF_NONREALTIME or
  800.                            ACM_STREAMOPENF_QUERY);
  801.       if (mmr = 0) then Result := True;
  802.    end;
  803. end;
  804. {-- TMMACM -------------------------------------------------------------}
  805. function TMMACM.ProcessFile(const FileName: TFileName; pwfxDst: PWaveFormatEx;
  806.                             pwfltr: PWaveFilter): Boolean;
  807. Label ERROR_CONVERT;
  808. var
  809.    mmr: MMRESULT;
  810.    Cancel: Boolean;
  811.    FOnProgress: TMMWaveProgress;
  812.    TmpFile, DestFile: PChar;
  813.    CurByte, NumBytes, NumRead, dw: Longint;
  814.    lpwioSrc, lpwioDst: PWaveIOCB;
  815.    pSrc,pDst: PChar;
  816.    SrcBufSize,DstBufSize: Longint;
  817.    pwfxSrc: PWaveFormatEx;
  818.    has: THACMSTREAM;
  819.    ash : TACMStreamHeader;
  820. begin
  821.    Result := False;
  822.    if not ACMPresent or (FSource = nil) or
  823.       (Wave = nil) or Wave.Empty or (FileName = '') then exit;
  824.    Cancel := False;
  825.    pSrc := nil;
  826.    pDst := nil;
  827.    lpwioSrc := Wave.PWaveIOInfo;
  828.    lpwioDst:= nil;
  829.    pwfxSrc := Wave.PWaveFormat;
  830.    if (pwfxDst = nil) then pwfxDst := pwfxSrc;
  831.    if not QueryConvert(pwfxDst, pwfltr) then
  832.       raise EMMACMError.Create(LoadResStr(IDS_ACMBADFORMAT));
  833.    { open the source file and set position to data chunk }
  834.    if wioWaveOpen(lpwioSrc) <> 0 then
  835.       goto ERROR_CONVERT;
  836.    { set the bytes to convert }
  837.    NumBytes := lpwioSrc^.dwBytesLeft;
  838.    { create the dest. WAVEIOINFO }
  839.    if wioCreateFileInfo(lpwioDst, pwfxDst) <> 0 then
  840.       goto ERROR_CONVERT;
  841.    { copy all known chunks to the destination }
  842.    if wioCopyFileInfo(lpwioDst, lpwioSrc) <> 0 then
  843.       goto ERROR_CONVERT;
  844.    TmpFile := StrAlloc(MAX_PATH+1);
  845.    DestFile := StrAlloc(MAX_PATH+1);
  846.    { first create a temporary file for the destination file }
  847.    StrPCopy(TmpFile, FileName);
  848.    if Not wioFileCreateTemp(TmpFile) then
  849.       goto ERROR_CONVERT;
  850.    { write the new Header to disc }
  851.    if wioWriteFileInfo(lpwioDst, TmpFile) <> 0 then
  852.       goto ERROR_CONVERT;
  853.    { compute source bytes to read (round down to nearest }
  854.    { block for one second of data)                       }
  855.    with pwfxSrc^ do
  856.    SrcBufSize := nAvgBytesPerSec-(nAvgBytesPerSec mod nBlockAlign);
  857.    mmr := acmStreamOpen(@has, 0, pwfxSrc, pwfxDst, pwfltr, 0, 0,
  858.                         ACM_STREAMOPENF_NONREALTIME);
  859.    if (mmr <> 0) then
  860.    begin
  861.       ShowMessage('acmStreamOpen failed with error '+IntToStr(mmr));
  862.       goto ERROR_CONVERT;
  863.    end;
  864.    mmr := acmStreamSize(has, SrcBufSize, DstBufSize,
  865.                         ACM_STREAMSIZEF_SOURCE);
  866.    if (mmr <> 0) then
  867.    begin
  868.       ShowMessage('acmStreamSize failed with error '+IntToStr(mmr));
  869.       goto ERROR_CONVERT;
  870.    end;
  871.    { allocate the src and dst buffers for reading/converting data }
  872.    pSrc := GlobalAllocPtr(GHND, SrcBufSize);
  873.    if (pSrc = Nil) then goto ERROR_CONVERT;
  874.    pDst := GlobalAllocPtr(GHND, DstBufSize);
  875.    if (pDst = Nil) then goto ERROR_CONVERT;
  876.    { setup the stream header structure }
  877.    ash.cbStruct          := sizeof(ash);
  878.    ash.fdwStatus         := 0;
  879.    ash.dwUser            := 0;
  880.    ash.pbSrc             := pSrc;
  881.    ash.cbSrcLength       := SrcBufSize;
  882.    ash.cbSrcLengthUsed   := 0;
  883.    ash.dwSrcUser         := SrcBufSize;
  884.    ash.pbDst             := pDst;
  885.    ash.cbDstLength       := DstBufSize;
  886.    ash.cbDstLengthUsed   := 0;
  887.    ash.dwDstUser         := DstBufSize;
  888.    mmr := acmStreamPrepareHeader(has, @ash, 0);
  889.    if (mmr <> 0) then
  890.    begin
  891.       ShowMessage('acmStreamPrepareHeader failed with error '+IntToStr(mmr));
  892.       goto ERROR_CONVERT;
  893.    end;
  894.    FOnProgress := Wave.OnProgress;
  895.    CurByte := 0;
  896.    while CurByte < NumBytes do
  897.    begin
  898.       { read the data to convert }
  899.       NumRead := min(SrcBufSize, NumBytes - CurByte);
  900.       NumRead := wioWaveReadData(lpwioSrc, pSrc, NumRead);
  901.       if (NumRead <= 0) then break;
  902.       ash.cbSrcLength     := NumRead;
  903.       ash.cbDstLengthUsed := 0;
  904.       mmr := acmStreamConvert(has, @ash, ACM_STREAMCONVERTF_BLOCKALIGN);
  905.       if (mmr <> 0) then
  906.       begin
  907.          ShowMessage('acmStreamConvert failed with error '+IntToStr(mmr));
  908.          goto ERROR_CONVERT;
  909.       end;
  910.       { wait until the converting is done }
  911.       while (ash.fdwStatus and ACMSTREAMHEADER_STATUSF_DONE = 0) do;
  912.       { adjust the current read position }
  913.       NumRead := NumRead - ash.cbSrcLengthUsed;
  914.       if (NumRead <> 0) then
  915.       begin
  916.          mmioSeek(lpwioSrc^.hmmio, -NumRead, SEEK_CUR);
  917.          inc(lpwioSrc^.dwBytesLeft, NumRead);
  918.       end;
  919.       inc(CurByte, ash.cbSrcLengthUsed);
  920.       NumRead := ash.cbDstLengthUsed;
  921.       if (NumRead <= 0) then break;
  922.       { have we space on the drive ? }
  923.       if not GetDiskFree(ExtractFilePath(TmpFile),NumRead+10240) then
  924.          goto ERROR_CONVERT;
  925.       { write the data out as we go... }
  926.       if wioWaveWriteData(lpwioDst, pDst, NumRead) <> NumRead then
  927.          goto ERROR_CONVERT;
  928.       { let the user have some time }
  929.       Application.ProcessMessages;
  930.       if assigned(FOnProgress) then
  931.          FOnProgress(Self, CurByte, NumBytes, Cancel);
  932.       if Cancel then goto ERROR_CONVERT;
  933.    end;
  934.    { cleanup pass }
  935.    while True do
  936.    begin
  937.       dw := 0;
  938.       NumRead := min(SrcBufSize, NumBytes - CurByte);
  939.       if (NumRead > 0) then
  940.       begin
  941.          dw := wioWaveReadData(lpwioSrc, pSrc, NumRead);
  942.          if (dw <= 0) then break;
  943.       end;
  944.       ash.cbSrcLength     := dw;
  945.       ash.cbDstLengthUsed := 0;
  946.       mmr := acmStreamConvert(has, @ash, ACM_STREAMCONVERTF_BLOCKALIGN or
  947.                                          ACM_STREAMCONVERTF_END);
  948.       if (mmr <> 0) then
  949.       begin
  950.          ShowMessage('acmStreamConvert failed with error '+IntToStr(mmr));
  951.          goto ERROR_CONVERT;
  952.       end;
  953.       { wait until the converting is done }
  954.       while (ash.fdwStatus and ACMSTREAMHEADER_STATUSF_DONE = 0) do;
  955.       dw := ash.cbDstLengthUsed;
  956.       if (dw = 0) then
  957.       begin
  958.          ash.cbDstLengthUsed := 0;
  959.          mmr := acmStreamConvert(has, @ash, ACM_STREAMCONVERTF_END);
  960.          if (mmr = 0) then
  961.          begin
  962.             { wait until the converting is done }
  963.             while (ash.fdwStatus and ACMSTREAMHEADER_STATUSF_DONE = 0) do;
  964.          end;
  965.          dw := ash.cbDstLengthUsed;
  966.          if (dw = 0) then break;
  967.       end;
  968.       { have we space on the drive ? }
  969.       if not GetDiskFree(ExtractFilePath(TmpFile),dw+10240) then
  970.          goto ERROR_CONVERT;
  971.       { write the data out as we go... }
  972.       if wioWaveWriteData(lpwioDst, pDst, dw) <> dw then
  973.          goto ERROR_CONVERT;
  974.       { adjust the current read position }
  975.       dw := NumRead - ash.cbSrcLengthUsed;
  976.       if (dw <> 0) then
  977.       begin
  978.          mmioSeek(lpwioSrc^.hmmio, -dw, SEEK_CUR);
  979.          inc(lpwioSrc^.dwBytesLeft, dw);
  980.       end;
  981.       inc(CurByte, ash.cbSrcLengthUsed);
  982.       if (ash.cbDstLengthUsed = 0) then break;
  983.       { let the user have some time }
  984.       Application.ProcessMessages;
  985.       if assigned(FOnProgress) then
  986.          FOnProgress(Self, CurByte, NumBytes, Cancel);
  987.       if Cancel then goto ERROR_CONVERT;
  988.    end;
  989.    { close source and temp file }
  990.    wioWaveClose(lpwioSrc);
  991.    wioWaveClose(lpwioDst);
  992.    StrPCopy(DestFile, FileName);
  993.    { delete the real destination file (if exits) }
  994.    if wioFileExists(DestFile) then wioFileDelete(DestFile);
  995.    { rename the temp file to the destination file }
  996.    if mmioRename(TmpFile, DestFile, Nil, 0) <> 0 then
  997.       goto ERROR_CONVERT;
  998.    Result := True;
  999. ERROR_CONVERT:
  1000.    wioWaveClose(lpwioSrc);
  1001.    wioWaveClose(lpwioDst);
  1002.    wioFreeFileInfo(lpwioDst);
  1003.    { clear up the stream stuff }
  1004.    if (ash.fdwStatus and ACMSTREAMHEADER_STATUSF_PREPARED <> 0) then
  1005.    begin
  1006.       ash.cbSrcLength := SrcBufSize;
  1007.       ash.cbDstLength := DstBufSize;
  1008.       mmr := acmStreamUnprepareHeader(has, @ash, 0);
  1009.       if (mmr <> 0) then
  1010.          ShowMessage('acmStreamUnprepareHeader failed with error '+IntToStr(mmr));
  1011.    end;
  1012.    acmStreamClose(has, 0);
  1013.    if (pSrc <> nil) then GlobalFreePtr(pSrc);
  1014.    if (pDst <> nil) then GlobalFreePtr(pDst);
  1015.    { make sure we delete the temp file }
  1016.    if wioFileExists(TmpFile) then wioFileDelete(TmpFile);
  1017.    StrDispose(TmpFile);
  1018.    StrDispose(DestFile);
  1019.    if not Result then
  1020.       if not Cancel then
  1021.          raise EMMACMError.Create(LoadResStr(IDS_ACMERROR))
  1022.       else
  1023.          raise EMMACMError.Create(LoadResStr(IDS_ACMABORT));
  1024. end;
  1025. {-- TMMACM -------------------------------------------------------------}
  1026. function TMMACM.CreateFile(const FileName: TFileName): Boolean;
  1027. begin
  1028.    Result := False;
  1029.    if ACMPresent and (FSource <> nil) and (Wave <> nil) then
  1030.    begin
  1031.       if not Wave.Empty and (FileName <> '') and (FPWaveFormatEx <> nil) then
  1032.       begin
  1033.          Wave.CreateFile(FileName, FPWaveFormatEx);
  1034.          Result := True;
  1035.       end;
  1036.    end;
  1037. end;
  1038. {-- TMMACM -------------------------------------------------------------}
  1039. function TMMACM.ConvertFile(const FileName: TFileName): Boolean;
  1040. begin
  1041.    Result := False;
  1042.    if ACMPresent and (FNumConverters > 0) and (FSource <> nil) and
  1043.       (Wave <> nil) and not Wave.Empty and (FileName <> '') then
  1044.    begin
  1045.       Result := ProcessFile(FileName, FPWaveFormatEx, nil);
  1046.    end;
  1047. end;
  1048. {-- TMMACM -------------------------------------------------------------}
  1049. function TMMACM.FilterFile(const FileName: TFileName): Boolean;
  1050. begin
  1051.    Result := False;
  1052.    if ACMPresent and (FNumFilters > 0) and (FSource <> nil) and
  1053.       (Wave <> nil) and not Wave.Empty and (FileName <> '') then
  1054.    begin
  1055.       Result := ProcessFile(FileName, nil, FPWaveFilter);
  1056.    end;
  1057. end;
  1058. end.