WaveUtils.pas
上传用户:wanyu_2000
上传日期:2021-02-21
资源大小:527k
文件大小:32k
源码类别:

DVD

开发平台:

Delphi

  1. {------------------------------------------------------------------------------}
  2. {                                                                              }
  3. {  WaveUtils - Utility functions and data types                                }
  4. {  by Kambiz R. Khojasteh                                                      }
  5. {                                                                              }
  6. {  kambiz@delphiarea.com                                                       }
  7. {  http://www.delphiarea.com                                                   }
  8. {                                                                              }
  9. {------------------------------------------------------------------------------}
  10. {.$I DELPHIAREA.INC}
  11. unit WaveUtils;
  12. interface
  13. uses
  14.   Windows, Messages, Classes, SysUtils,MSAcm, mmSystem;
  15. const
  16. // addons for extra codecs
  17.   WAVE_FORMAT_MSG723 = 66;
  18.   WAVE_FORMAT_MPEGLAYER3 = 85;
  19.   MPEGLAYER3_WFX_EXTRA_BYTES = 12;
  20.   MPEGLAYER3_ID_UNKNOWN = 0;
  21.   MPEGLAYER3_ID_MPEG = 1;
  22.   MPEGLAYER3_ID_CONSTANTFRAMESIZE = 2;
  23.   MPEGLAYER3_FLAG_PADDING_ISO = $00000000;
  24.   MPEGLAYER3_FLAG_PADDING_ON = $00000001;
  25.   MPEGLAYER3_FLAG_PADDING_OFF = $00000002;
  26. type
  27.   TACMWaveFormat = packed record
  28.     case integer of
  29.       0 : (Format : TWaveFormatEx);
  30.       1 : (RawData : Array[0..128] of byte);
  31.   end;
  32.   // Milliseconds to string format specifiers
  33.   TMS2StrFormat = (
  34.     msHMSh, // Hour:Minute:Second.Hunderdth
  35.     msHMS,  // Hour:Minute:Second
  36.     msMSh,  // Minute:Second.Hunderdth
  37.     msMS,   // Minute:Second
  38.     msSh,   // Second.Hunderdth
  39.     msS,    // Second
  40.     msAh,   // Best format with hunderdth of second
  41.     msA);   // Best format without hunderdth of second
  42.   // Standard PCM Audio Format
  43.   TPCMChannel = (cMono, cStereo);
  44.   TPCMSamplesPerSec = (ss8000Hz, ss11025Hz, ss22050Hz, ss44100Hz, ss48000Hz);
  45.   TPCMBitsPerSample = (bs8Bit, bs16Bit);
  46.   TPCMFormat = (nonePCM, Mono8Bit8000Hz, Stereo8bit8000Hz, Mono16bit8000Hz,
  47.     Stereo16bit8000Hz, Mono8bit11025Hz, Stereo8bit11025Hz, Mono16bit11025Hz,
  48.     Stereo16bit11025Hz, Mono8bit22050Hz, Stereo8bit22050Hz, Mono16bit22050Hz,
  49.     Stereo16bit22050Hz, Mono8bit44100Hz, Stereo8bit44100Hz, Mono16bit44100Hz,
  50.     Stereo16bit44100Hz, Mono8bit48000Hz, Stereo8bit48000Hz, Mono16bit48000Hz,
  51.     Stereo16bit48000Hz);
  52.   // Wave Device Supported PCM Formats
  53.   TWaveDeviceFormats = set of TPCMFormat;
  54.   // Wave Out Device Supported Features
  55.   TWaveOutDeviceSupport = (dsVolume, dsStereoVolume, dsPitch, dsPlaybackRate,
  56.     dsPosition, dsAsynchronize, dsDirectSound);
  57.   TWaveOutDeviceSupports = set of TWaveOutDeviceSupport;
  58.   // Wave Out Options
  59.   TWaveOutOption = (woSetVolume, woSetPitch, woSetPlaybackRate);
  60.   TWaveOutOptions = set of TWaveOutOption;
  61.   // Wave Audio Exceptions
  62.   EWaveAudioError = class(Exception);
  63.   EWaveAudioSysError = class(EWaveAudioError);
  64.   EWaveAudioInvalidOperation = class(EWaveAudioError);
  65.   // Wave Audio Events
  66.   TWaveAudioEvent = procedure(Sender: TObject) of object;
  67.   TWaveAudioGetFormatEvent = procedure(Sender: TObject;
  68.     out pWaveFormat: PWaveFormatEx; var FreeIt: Boolean) of object;
  69.   TWaveAudioGetDataEvent = function(Sender: TObject; const Buffer: Pointer;
  70.     BufferSize: DWORD; var NumLoops: DWORD): DWORD of object;
  71.   TWaveAudioGetDataPtrEvent = function(Sender: TObject; out Buffer: Pointer;
  72.     var NumLoops: DWORD; var FreeIt: Boolean): DWORD of object;
  73.   TWaveAudioDataReadyEvent = procedure(Sender: TObject; const Buffer: Pointer;
  74.     BufferSize: DWORD; var FreeIt: Boolean) of object;
  75.   TWaveAudioLevelEvent = procedure(Sender: TObject; Level: Integer) of object;
  76.   TWaveAudioFilterEvent = procedure(Sender: TObject; const Buffer: Pointer;
  77.     BufferSize: DWORD) of object;
  78. // Retrieves format, size, and offset of the wave audio for an open mmIO
  79. // handle. On success when the the function returns true, it is the caller
  80. // responsibility to free the memory allocated for the Wave Format structure.
  81. function GetWaveAudioInfo(mmIO: HMMIO; out pWaveFormat: PWaveFormatEx;
  82.   out DataSize, DataOffset: DWORD): Boolean;
  83. // Initializes a new wave RIFF format in an open mmIO handle. The previous
  84. // content of mmIO will be lost.
  85. function CreateWaveAudio(mmIO: HMMIO; const pWaveFormat: PWaveFormatEx;
  86.   out ckRIFF, ckData: TMMCKInfo): Boolean;
  87. // Updates the chunks and closes an mmIO handle.
  88. procedure CloseWaveAudio(mmIO: HMMIO; var ckRIFF, ckData: TMMCKInfo);
  89. // Retrieves format, size, and offset of the wave audio for a stream. On
  90. // success when the the function returns true, it is the caller responsibility
  91. // to free the memory allocated for the Wave Format structure.
  92. function GetStreamWaveAudioInfo(Stream: TStream; out pWaveFormat: PWaveFormatEx;
  93.   out DataSize, DataOffset: DWORD): Boolean;
  94. // Initializes wave RIFF format in a stream and returns the mmIO handle.
  95. // After calling this function, the previous content of the stream will be lost.
  96. function CreateStreamWaveAudio(Stream: TStream; const pWaveFormat: PWaveFormatEx;
  97.   out ckRIFF, ckData: TMMCKInfo): HMMIO;
  98. // Opens wave RIFF format in a stream for read and write operations and returns
  99. // the mmIO handle.
  100. function OpenStreamWaveAudio(Stream: TStream): HMMIO;
  101. // Claculates the wave buffer size for the specified duration.
  102. function CalcWaveBufferSize(const pWaveFormat: PWaveFormatEx; Duration: DWORD): DWORD;
  103. // Returns the string representation of a wave audio format.
  104. function GetWaveAudioFormat(const pWaveFormat: PWaveFormatEx): String;
  105. // Returns the wave's length in milliseconds.
  106. function GetWaveAudioLength(const pWaveFormat: PWaveFormatEx; DataSize: DWORD): DWORD;
  107. // Returns the wave's bit rate in kbps (kilo bits per second).
  108. function GetWaveAudioBitRate(const pWaveFormat: PWaveFormatEx): DWORD;
  109. // Returns the wave data volume peak level in percent (PCM format only).
  110. function GetWaveAudioPeakLevel(const Data: Pointer; DataSize: DWORD;
  111.   BitsPerSample: WORD): Integer;
  112. // Inverts the wave data (PCM format only).
  113. procedure InvertWaveAudio(const Data: Pointer; DataSize: DWORD;
  114.   BitsPerSample: WORD);
  115. // Fills the wave data with silence
  116. procedure SilenceWaveAudio(const Data: Pointer; DataSize: DWORD;
  117.   BitsPerSample: WORD);
  118. // Increases/Decreases the wave data volume by the specified percentage (PCM format only).
  119. procedure ChangeWaveAudioVolume(const Data: Pointer; DataSize: DWORD;
  120.   BitsPerSample: WORD; Percent: Integer);
  121. // Converts the wave data to the specified format (PCM format only). The caller is
  122. // responsible to release the memory allocated for the converted wave data.
  123. function ConvertWaveFormat(const srcFormat: PWaveFormatEx; srcData: Pointer; srcDataSize: DWORD;
  124.   const dstFormat: PWaveFormatEx; out dstData: Pointer; out dstDataSize: DWORD): Boolean;
  125. // Initializes a standard PCM wave format header. The size of memory referenced
  126. // by the pWaveFormat parameter must not be less than the size of TWaveFormatEx
  127. // record.
  128. procedure SetPCMAudioFormat(const pWaveFormat: PWaveFormatEx; Channels: TPCMChannel;
  129.   SamplesPerSec: TPCMSamplesPerSec; BitsPerSample: TPCMBitsPerSample);
  130. // Initializes a standard PCM wave format header (shorcut form). The size of
  131. // memory referenced by the pWaveFormat parameter must not be less than the
  132. // size of TWaveFormatEx record.
  133. procedure SetPCMAudioFormatS(const pWaveFormat: PWaveFormatEx; PCMFormat: TPCMFormat);
  134. // Returns the standard PCM format specifier of a wave format.
  135. function GetPCMAudioFormat(const pWaveFormat: PWaveFormatEx): TPCMFormat;
  136. // Converts milliseconds to string
  137. function MS2Str(Milliseconds: DWORD; Fmt: TMS2StrFormat): String;
  138. // Waits for the scnchronize object while lets the caller thread processes
  139. // its messages.
  140. function WaitForSyncObject(SyncObject: THandle; Timeout: DWORD): DWORD;
  141. // User defined mmIOProc to handle Delphi streams by Windows' mmIO functions
  142. function mmioStreamProc(lpmmIOInfo: PMMIOInfo; uMsg, lParam1, lParam2: DWORD): LRESULT; stdcall;
  143. implementation
  144. { A subset of Microsoft Audio Compression Manager (ACM) API }
  145. { http://msdn.microsoft.com/library/en-us/multimed/htm/_win32_audio_compression_manager.asp }
  146. const
  147.   // acmStreamConvert flags
  148.   ACM_STREAMCONVERTF_BLOCKALIGN = $00000004;
  149.   ACM_STREAMCONVERTF_START      = $00000010;
  150.   ACM_STREAMCONVERTF_END        = $00000020;
  151.   // acmStreamOpen flags
  152.   ACM_STREAMOPENF_QUERY         = $00000001;
  153.   ACM_STREAMOPENF_ASYNC         = $00000002;
  154.   ACM_STREAMOPENF_NONREALTIME   = $00000004;
  155.   // acmStreamSize flags
  156.   ACM_STREAMSIZEF_SOURCE        = $00000000;
  157.   ACM_STREAMSIZEF_DESTINATION   = $00000001;
  158. type
  159.   // ACM Driver Handle
  160.   HACMDRIVER = DWORD;
  161.   // ACM Stream Handle
  162.   HACMSTREAM = DWORD;
  163.   // ACM Stream Header
  164.   PACMSTREAMHEADER = ^TACMSTREAMHEADER;
  165.   TACMSTREAMHEADER = packed record
  166.     cbStruct: DWORD;
  167.     fdwStatus: DWORD;
  168.     dwUser: DWORD;
  169.     pbSrc: PBYTE;
  170.     cbSrcLength: DWORD;
  171.     cbSrcLengthUsed: DWORD;
  172.     dwSrcUser: DWORD;
  173.     pbDst: PBYTE;
  174.     cbDstLength: DWORD;
  175.     cbDstLengthUsed: DWORD;
  176.     dwDstUser: DWORD;
  177.     dwReservedDriver: array[0..9] of DWORD;
  178.   end;
  179.   // ACM Wave Filter
  180.   PWAVEFILTER = ^TWAVEFILTER;
  181.   TWAVEFILTER = packed record
  182.     cbStruct: DWORD;
  183.     dwFilterTag: DWORD;
  184.     fdwFilter: DWORD;
  185.     dwReserved: array[0..4] of DWORD;
  186.   end;
  187. function acmStreamOpen(var phas: HACMSTREAM; had: HACMDRIVER;
  188.   pwfxSrc: PWAVEFORMATEX; pwfxDst: PWAVEFORMATEX; pwfltr: PWAVEFILTER;
  189.   dwCallback: DWORD; dwInstance: DWORD; fdwOpen: DWORD): MMRESULT; stdcall;
  190.   external 'msacm32.dll';
  191. function acmStreamClose(has: HACMSTREAM; fdwClose: DWORD): MMRESULT; stdcall;
  192.   external 'msacm32.dll';
  193. function acmStreamPrepareHeader(has: HACMSTREAM; var pash: TACMSTREAMHEADER;
  194.   fdwPrepare: DWORD): MMRESULT; stdcall;
  195.   external 'msacm32.dll';
  196. function acmStreamUnprepareHeader(has: HACMSTREAM; var pash: TACMSTREAMHEADER;
  197.   fdwUnprepare: DWORD): MMRESULT; stdcall;
  198.   external 'msacm32.dll';
  199. function acmStreamConvert(has: HACMSTREAM; var pash: TACMSTREAMHEADER;
  200.   fdwConvert: DWORD): MMRESULT; stdcall;
  201.   external 'msacm32.dll';
  202. function acmStreamSize(has: HACMSTREAM; cbInput: DWORD;
  203.   var pdwOutputBytes: DWORD; fdwSize: DWORD): MMRESULT; stdcall;
  204.   external 'msacm32.dll';
  205. { Global Procedures }
  206. // To open a stream using mmIO API functions, use the following code sample:
  207. //
  208. //    FillChar(mmioInfo, SizeOf(mmioInfo), 0);
  209. //    mmioInfo.pIOProc := @mmioStreamProc;
  210. //    mmioInfo.adwInfo[0] := DWORD(your_stream_instance);
  211. //    mmIO := mmioOpen(nil, @mmioInfo, dwOpenFlags);
  212. //
  213. // The flags specified by the dwOpenFlags parameter of mmioOpen function can
  214. // be only one of MMIO_READ, MMIO_WRITE, and MMIO_READWRITE flags. If you use
  215. // another flags, simply they will be ignored by this user defined function.
  216. function mmIOStreamProc(lpmmIOInfo: PMMIOInfo; uMsg, lParam1, lParam2: DWORD): LRESULT; stdcall;
  217. var
  218.   Stream: TStream;
  219. begin
  220.   if Assigned(lpmmIOInfo) and (lpmmIOInfo^.adwInfo[0] <> 0) then
  221.   begin
  222.     Stream := TStream(lpmmIOInfo^.adwInfo[0]);
  223.     case uMsg of
  224.       MMIOM_OPEN:
  225.       begin
  226.         if TObject(lpmmIOInfo^.adwInfo[0]) is TStream then
  227.         begin
  228.           Stream.Seek(0, SEEK_SET);
  229.           lpmmIOInfo^.lDiskOffset := 0;
  230.           Result := MMSYSERR_NOERROR;
  231.         end
  232.         else
  233.           Result := -1;
  234.       end;
  235.       MMIOM_CLOSE:
  236.         Result := MMSYSERR_NOERROR;
  237.       MMIOM_SEEK:
  238.         try
  239.           if lParam2 = SEEK_CUR then
  240.             Stream.Seek(lpmmIOInfo^.lDiskOffset, SEEK_SET);
  241.           Result := Stream.Seek(lParam1, lParam2);
  242.           lpmmIOInfo^.lDiskOffset := Result;
  243.         except
  244.           Result := -1;
  245.         end;
  246.       MMIOM_READ:
  247.         try
  248.           Stream.Seek(lpmmIOInfo^.lDiskOffset, SEEK_SET);
  249.           Result := Stream.Read(Pointer(lParam1)^, lParam2);
  250.           lpmmIOInfo^.lDiskOffset := Stream.Seek(0, SEEK_CUR);
  251.         except
  252.           Result := -1;
  253.         end;
  254.       MMIOM_WRITE,
  255.       MMIOM_WRITEFLUSH:
  256.         try
  257.           Stream.Seek(lpmmIOInfo^.lDiskOffset, SEEK_SET);
  258.           Result := Stream.Write(Pointer(lParam1)^, lParam2);
  259.           lpmmIOInfo^.lDiskOffset := Stream.Seek(0, SEEK_CUR);
  260.         except
  261.           Result := -1;
  262.         end
  263.     else
  264.       Result := MMSYSERR_NOERROR;
  265.     end;
  266.   end
  267.   else
  268.     Result := -1;
  269. end;
  270. // Retrieves format, size, and offset of the wave audio for an open mmIO
  271. // handle. On success when the the function returns true, it is the caller
  272. // responsibility to free the memory allocated for the Wave Format structure.
  273. function GetWaveAudioInfo(mmIO: HMMIO; out pWaveFormat: PWaveFormatEx;
  274.   out DataSize, DataOffset: DWORD): Boolean;
  275.   function GetWaveFormat(const ckRIFF: TMMCKInfo): Boolean;
  276.   var
  277.     ckFormat: TMMCKInfo;
  278.   begin
  279.     Result := False;
  280.     ckFormat.ckid := mmioStringToFOURCC('fmt', 0);
  281.     if (mmioDescend(mmIO, @ckFormat, @ckRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) and
  282.        (ckFormat.cksize >= SizeOf(TWaveFormat)) then
  283.     begin
  284.       if ckFormat.cksize < SizeOf(TWaveFormatEx) then
  285.       begin
  286.         GetMem(pWaveFormat, SizeOf(TWaveFormatEx));
  287.         FillChar(pWaveFormat^, SizeOf(TWaveFormatEx), 0);
  288.       end
  289.       else
  290.         GetMem(pWaveFormat, ckFormat.cksize);
  291.       Result := (mmioRead(mmIO, PChar(pWaveFormat), ckFormat.cksize) = Integer(ckFormat.cksize));
  292.     end;
  293.   end;
  294.   function GetWaveData(const ckRIFF: TMMCKInfo): Boolean;
  295.   var
  296.     ckData: TMMCKInfo;
  297.   begin
  298.     Result := False;
  299.     ckData.ckid := mmioStringToFOURCC('data', 0);
  300.     if (mmioDescend(mmIO, @ckData, @ckRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then
  301.     begin
  302.       DataSize := ckData.cksize;
  303.       DataOffset := ckData.dwDataOffset;
  304.       Result := True;
  305.     end;
  306.   end;
  307. var
  308.   ckRIFF: TMMCKInfo;
  309.   OrgPos: Integer;
  310. begin
  311.   Result := False;
  312.   OrgPos := mmioSeek(mmIO, 0, SEEK_CUR);
  313.   try
  314.     mmioSeek(mmIO, 0, SEEK_SET);
  315.     ckRIFF.fccType := mmioStringToFOURCC('WAVE', 0);
  316.     if (mmioDescend(mmIO, @ckRIFF, nil, MMIO_FINDRIFF) = MMSYSERR_NOERROR) then
  317.     begin
  318.       pWaveFormat := nil;
  319.       if GetWaveFormat(ckRIFF) and GetWaveData(ckRIFF) then
  320.         Result := True
  321.       else if Assigned(pWaveFormat) then
  322.         ReallocMem(pWaveFormat, 0);
  323.     end
  324.   finally
  325.     mmioSeek(mmIO, OrgPos, SEEK_SET);
  326.   end;
  327. end;
  328. // Initializes a new wave RIFF format in an open mmIO handle. The previous
  329. // content of mmIO will be lost.
  330. function CreateWaveAudio(mmIO: HMMIO; const pWaveFormat: PWaveFormatEx;
  331.   out ckRIFF, ckData: TMMCKInfo): Boolean;
  332. var
  333.   ckFormat: TMMCKInfo;
  334.   FormatSize: Integer;
  335. begin
  336.   Result := False;
  337.   FormatSize := SizeOf(TWaveFormatEx) + pWaveFormat^.cbSize;
  338.   mmIOSeek(mmIO, 0, SEEK_SET);
  339.   FillChar(ckRIFF, SizeOf(TMMCKInfo), 0);
  340.   ckRIFF.fccType := mmioStringToFOURCC('WAVE', 0);
  341.   if mmioCreateChunk(mmIO, @ckRIFF, MMIO_CREATERIFF) = MMSYSERR_NOERROR then
  342.   begin
  343.     FillChar(ckFormat, SizeOf(TMMCKInfo), 0);
  344.     ckFormat.ckid := mmioStringToFOURCC('fmt', 0);
  345.     if (mmioCreateChunk(mmIO, @ckFormat, 0) = MMSYSERR_NOERROR) and
  346.        (mmioWrite(mmIO, PChar(pWaveFormat), FormatSize) = FormatSize) and
  347.        (mmioAscend(mmIO, @ckFormat, 0) = MMSYSERR_NOERROR) then
  348.     begin
  349.       FillChar(ckData, SizeOf(TMMCKInfo), 0);
  350.       ckData.ckid := mmioStringToFOURCC('data', 0);
  351.       Result := (mmioCreateChunk(mmIO, @ckData, 0) = MMSYSERR_NOERROR);
  352.     end;
  353.   end;
  354. end;
  355. // Updates the chunks and closes an mmIO handle.
  356. procedure CloseWaveAudio(mmIO: HMMIO; var ckRIFF, ckData: TMMCKInfo);
  357. begin
  358.   mmioAscend(mmIO, @ckData, 0);
  359.   mmioAscend(mmIO, @ckRIFF, 0);
  360.   mmioClose(mmIO, 0);
  361. end;
  362. // Retrieves format, size, and offset of the wave audio for a stream. On
  363. // success when the the function returns true, it is the caller responsibility
  364. // to free the memory allocated for the Wave Format structure.
  365. function GetStreamWaveAudioInfo(Stream: TStream; out pWaveFormat: PWaveFormatEx;
  366.   out DataSize, DataOffset: DWORD): Boolean;
  367. var
  368.   mmIO: HMMIO;
  369. begin
  370.   Result := False;
  371.   if Stream.Size <> 0 then
  372.   begin
  373.     mmIO := OpenStreamWaveAudio(Stream);
  374.     if mmIO <> 0 then
  375.       try
  376.         Result := GetWaveAudioInfo(mmIO, pWaveFormat, DataSize, DataOffset);
  377.       finally
  378.         mmioClose(mmIO, MMIO_FHOPEN);
  379.       end;
  380.   end;
  381. end;
  382. // Initializes wave RIFF format in a stream and returns the mmIO handle.
  383. // After calling this function, the previous content of the stream will be lost.
  384. function CreateStreamWaveAudio(Stream: TStream; const pWaveFormat: PWaveFormatEx;
  385.  out ckRIFF, ckData: TMMCKInfo): HMMIO;
  386. begin
  387.   Result := OpenStreamWaveAudio(Stream);
  388.   if Result <> 0 then
  389.   begin
  390.     Stream.Size := 0;
  391.     if not CreateWaveAudio(Result, pWaveFormat, ckRIFF, ckData) then
  392.     begin
  393.       mmioClose(Result, MMIO_FHOPEN);
  394.       Result := 0;
  395.     end;
  396.   end;
  397. end;
  398. // Opens wave RIFF format in a stream for read and write operations and returns
  399. // the mmIO handle.
  400. function OpenStreamWaveAudio(Stream: TStream): HMMIO;
  401. var
  402.   mmIOInfo: TMMIOINFO;
  403. begin
  404.   FillChar(mmIOInfo, SizeOf(mmIOInfo), 0);
  405.   mmIOInfo.pIOProc := @mmIOStreamProc;
  406.   mmIOInfo.adwInfo[0] := DWORD(Stream);
  407.   Result := mmioOpen(nil, @mmIOInfo, MMIO_READ or MMIO_WRITE);
  408. end;
  409. // Claculates the wave buffer size for the specified duration.
  410. function CalcWaveBufferSize(const pWaveFormat: PWaveFormatEx; Duration: DWORD): DWORD;
  411. var
  412.   Alignment: DWORD;
  413. begin
  414.   Result := MulDiv(Duration, pWaveFormat^.nAvgBytesPerSec, 1000);
  415.   if pWaveFormat^.nBlockAlign <> 0 then
  416.   begin
  417.     Alignment := Result mod pWaveFormat^.nBlockAlign;
  418.     if Alignment <> 0 then Inc(Result, pWaveFormat^.nBlockAlign - Alignment);
  419.   end;
  420. end;
  421. // Returns the string representation of a wave audio format.
  422. function GetWaveAudioFormat(const pWaveFormat: PWaveFormatEx): String;
  423. const
  424.   Channels: array[1..2] of String = ('Mono', 'Stereo');
  425. begin
  426.   with pWaveFormat^ do
  427.   begin
  428.     if nChannels in [1..2] then
  429.       Result := Format('%.3f kHz, %d Bit, %s', [nSamplesPerSec / 1000,
  430.         wBitsPerSample, Channels[nChannels]])
  431.     else
  432.       Result := Format('%.3f kHz, %d Bit, %d Ch', [nSamplesPerSec / 1000,
  433.         wBitsPerSample, nChannels]);
  434.     if wFormatTag = WAVE_FORMAT_PCM then
  435.       Result := 'PCM ' + Result;
  436.   end;
  437. end;
  438. // Returns the wave's length in milliseconds.
  439. function GetWaveAudioLength(const pWaveFormat: PWaveFormatEx; DataSize: DWORD): DWORD;
  440. begin
  441.   with pWaveFormat^ do
  442.     if nAvgBytesPerSec <> 0 then
  443.       Result := MulDiv(1000, DataSize, nAvgBytesPerSec)
  444.     else
  445.       Result := 0;
  446. end;
  447. // Returns the wave's bit rate in kbps (kilo bits per second).
  448. function GetWaveAudioBitRate(const pWaveFormat: PWaveFormatEx): DWORD;
  449. begin
  450.   with pWaveFormat^ do
  451.     Result := MulDiv(nSamplesPerSec, nChannels * wBitsPerSample, 1000);
  452. end;
  453. // Returns the wave data peak level in percent (PCM format only).
  454. function GetWaveAudioPeakLevel(const Data: Pointer; DataSize: DWORD;
  455.   BitsPerSample: WORD): Integer;
  456.   function GetAudioPeakLevel8Bit: Integer;
  457.   var
  458.     pSample: PByte;
  459.     Max: Byte;
  460.   begin
  461.     Max := 0;
  462.     pSample := Data;
  463.     while DataSize > 0 do
  464.     begin
  465.       if pSample^ > Max then
  466.         Max := pSample^;
  467.       Inc(pSample);
  468.       Dec(DataSize);
  469.     end;
  470.     if ByteBool(Max and $80) then
  471.       Max := Max and $7F
  472.     else
  473.       Max := 0;
  474.     Result := (100 * Max) div $7F;
  475.   end;
  476.   function GetAudioPeakLevel16Bit: Integer;
  477.   var
  478.     pSample: PSmallInt;
  479.     Max: SmallInt;
  480.   begin
  481.     Max := 0;
  482.     pSample := Data;
  483.     while DataSize > 0 do
  484.     begin
  485.       if pSample^ > Max then
  486.         Max := pSample^;
  487.       Inc(pSample);
  488.       Dec(DataSize, 2);
  489.     end;
  490.     Result := (100 * Max) div $7FFF;
  491.   end;
  492. begin
  493.   case BitsPerSample of
  494.     8: Result := GetAudioPeakLevel8Bit;
  495.     16: Result := GetAudioPeakLevel16Bit;
  496.   else
  497.     Result := -1;
  498.   end;
  499. end;
  500. // Inverts the wave data (PCM format only).
  501. procedure InvertWaveAudio(const Data: Pointer; DataSize: DWORD;
  502.   BitsPerSample: WORD);
  503.   procedure Invert8Bit;
  504.   var
  505.     pStart, pEnd: PByte;
  506.   begin
  507.     pStart := Data;
  508.     pEnd := PByte(DWORD(pStart) + DataSize - SizeOf(Byte));
  509.     while DWORD(pStart) < DWORD(pEnd) do
  510.     begin
  511.       pStart^ := pStart^ xor pEnd^;
  512.       pEnd^ := pStart^ xor pEnd^;
  513.       pStart^ := pStart^ xor pEnd^;
  514.       Inc(pStart);
  515.       Dec(pEnd);
  516.     end;
  517.   end;
  518.   procedure Invert16Bit;
  519.   var
  520.     pStart, pEnd: PSmallInt;
  521.   begin
  522.     pStart := Data;
  523.     pEnd := PSmallInt(DWORD(pStart) + DataSize - SizeOf(SmallInt));
  524.     while DWORD(pStart) < DWORD(pEnd) do
  525.     begin
  526.       pStart^ := pStart^ xor pEnd^;
  527.       pEnd^ := pStart^ xor pEnd^;
  528.       pStart^ := pStart^ xor pEnd^;
  529.       Inc(pStart);
  530.       Dec(pEnd);
  531.     end;
  532.   end;
  533. begin
  534.   case BitsPerSample of
  535.     8: Invert8Bit;
  536.     16: Invert16Bit;
  537.   end;
  538. end;
  539. // Fills the wave data with silence
  540. procedure SilenceWaveAudio(const Data: Pointer; DataSize: DWORD;
  541.   BitsPerSample: WORD);
  542. begin
  543.   case BitsPerSample of
  544.     8: FillChar(Data^, DataSize, $7F);
  545.     16: FillChar(Data^, DataSize, 0);
  546.   end;
  547. end;
  548. // Increases/Decreases the wave data volume by the specified percentage (PCM format only).
  549. procedure ChangeWaveAudioVolume(const Data: Pointer; DataSize: DWORD;
  550.   BitsPerSample: WORD; Percent: Integer);
  551.   procedure ChangeVolume8Bit;
  552.   var
  553.     pSample: PByte;
  554.     Value: Integer;
  555.   begin
  556.     pSample := Data;
  557.     while DataSize > 0 do
  558.     begin
  559.       Value := pSample^ + (pSample^ * Percent) div 100;
  560.       if Value > High(Byte) then
  561.         Value := High(Byte)
  562.       else if Value < 0 then
  563.         Value := 0;
  564.       pSample^ := Value;
  565.       Inc(pSample);
  566.       Dec(DataSize, SizeOf(Byte));
  567.     end;
  568.   end;
  569.   procedure ChangeVolume16Bit;
  570.   var
  571.     pSample: PSmallInt;
  572.     Value: Integer;
  573.   begin
  574.     pSample := Data;
  575.     while DataSize > 0 do
  576.     begin
  577.       Value := pSample^ + (pSample^ * Percent) div 100;
  578.       if Value > High(SmallInt) then
  579.         Value := High(SmallInt)
  580.       else if Value < -High(SmallInt) then
  581.         Value := -High(SmallInt);
  582.       pSample^ := Value;
  583.       Inc(pSample);
  584.       Dec(DataSize, SizeOf(SmallInt));
  585.     end;
  586.   end;
  587. begin
  588.   case BitsPerSample of
  589.     8: ChangeVolume8Bit;
  590.     16: ChangeVolume16Bit;
  591.   end;
  592. end;
  593. // Converts the wave data to the specified format. The caller is responsible to
  594. // release the memory allocated for the converted wave data buffer.
  595. function ConvertWaveFormat(const srcFormat: PWaveFormatEx; srcData: Pointer; srcDataSize: DWORD;
  596.   const dstFormat: PWaveFormatEx; out dstData: Pointer; out dstDataSize: DWORD): Boolean;
  597. var
  598.   hStream: HACMSTREAM;
  599.   Header: TACMSTREAMHEADER;
  600. begin
  601.   Result := False;
  602.   if acmStreamOpen(hStream, 0, srcFormat, dstFormat, nil, 0, 0, ACM_STREAMOPENF_NONREALTIME) = 0 then
  603.   begin
  604.     try
  605.       if acmStreamSize(hStream, srcDataSize, dstDataSize, ACM_STREAMSIZEF_SOURCE) = 0 then
  606.       begin
  607.         dstData := nil;
  608.         FillChar(Header, SizeOf(Header), 0);
  609.         ReallocMem(dstData, dstDataSize);
  610.         try
  611.           Header.cbStruct := SizeOf(Header);
  612.           Header.pbSrc := srcData;
  613.           Header.cbSrcLength := srcDataSize;
  614.           Header.pbDst := dstData;
  615.           Header.cbDstLength := dstDataSize;
  616.           if acmStreamPrepareHeader(hStream, Header, 0) = 0 then
  617.             try
  618.               Result := (acmStreamConvert(hStream, Header, ACM_STREAMCONVERTF_START or ACM_STREAMCONVERTF_END) = 0);
  619.             finally
  620.               acmStreamUnprepareHeader(hStream, Header, 0);
  621.             end;
  622.         finally
  623.           ReallocMem(dstData, Header.cbDstLengthUsed);
  624.           dstDataSize := Header.cbDstLengthUsed;
  625.         end;
  626.       end;
  627.     finally
  628.       acmStreamClose(hStream, 0);
  629.     end;
  630.   end;
  631. end;
  632. // Initializes a standard PCM wave format header. The size of memory referenced
  633. // by the pWaveFormat parameter must not be less than the size of TWaveFormatEx
  634. // record.
  635. procedure SetPCMAudioFormat(const pWaveFormat: PWaveFormatEx;
  636.   Channels: TPCMChannel; SamplesPerSec: TPCMSamplesPerSec;
  637.   BitsPerSample: TPCMBitsPerSample);
  638. begin
  639.   with pWaveFormat^ do
  640.   begin
  641.     wFormatTag := WAVE_FORMAT_PCM;
  642.     case Channels of
  643.       cMono: nChannels := 1;
  644.       cStereo: nChannels := 2;
  645.     end;
  646.     case SamplesPerSec of
  647.       ss8000Hz: nSamplesPerSec := 8000;
  648.       ss11025Hz: nSamplesPerSec := 11025;
  649.       ss22050Hz: nSamplesPerSec := 22050;
  650.       ss44100Hz: nSamplesPerSec := 44100;
  651.       ss48000Hz: nSamplesPerSec := 48000;
  652.     end;
  653.     case BitsPerSample of
  654.       bs8Bit: wBitsPerSample := 8;
  655.       bs16Bit: wBitsPerSample := 16;
  656.     end;
  657.     nBlockAlign := MulDiv(nChannels, wBitsPerSample, 8);
  658.     nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
  659.     cbSize := 0;
  660.   end;
  661. end;
  662. // Initializes a standard PCM wave format header (shorcut form). The size of
  663. // memory referenced by the pWaveFormat parameter must not be less than the
  664. // size of TWaveFormatEx record.
  665. procedure SetPCMAudioFormatS(const pWaveFormat: PWaveFormatEx; PCMFormat: TPCMFormat);
  666. begin
  667.   case PCMFormat of
  668.     Mono8Bit8000Hz:
  669.       SetPCMAudioFormat(pWaveFormat, cMono, ss8000Hz, bs8Bit);
  670.     Mono8Bit11025Hz:
  671.       SetPCMAudioFormat(pWaveFormat, cMono, ss11025Hz, bs8Bit);
  672.     Mono8Bit22050Hz:
  673.       SetPCMAudioFormat(pWaveFormat, cMono, ss22050Hz, bs8Bit);
  674.     Mono8Bit44100Hz:
  675.       SetPCMAudioFormat(pWaveFormat, cMono, ss44100Hz, bs8Bit);
  676.     Mono8Bit48000Hz:
  677.       SetPCMAudioFormat(pWaveFormat, cMono, ss48000Hz, bs8Bit);
  678.     Mono16Bit8000Hz:
  679.       SetPCMAudioFormat(pWaveFormat, cMono, ss8000Hz, bs16Bit);
  680.     Mono16Bit11025Hz:
  681.       SetPCMAudioFormat(pWaveFormat, cMono, ss11025Hz, bs16Bit);
  682.     Mono16Bit22050Hz:
  683.       SetPCMAudioFormat(pWaveFormat, cMono, ss22050Hz, bs16Bit);
  684.     Mono16Bit44100Hz:
  685.       SetPCMAudioFormat(pWaveFormat, cMono, ss44100Hz, bs16Bit);
  686.     Mono16Bit48000Hz:
  687.       SetPCMAudioFormat(pWaveFormat, cMono, ss48000Hz, bs16Bit);
  688.     Stereo8bit8000Hz:
  689.       SetPCMAudioFormat(pWaveFormat, cStereo, ss8000Hz, bs8Bit);
  690.     Stereo8bit11025Hz:
  691.       SetPCMAudioFormat(pWaveFormat, cStereo, ss11025Hz, bs8Bit);
  692.     Stereo8bit22050Hz:
  693.       SetPCMAudioFormat(pWaveFormat, cStereo, ss22050Hz, bs8Bit);
  694.     Stereo8bit44100Hz:
  695.       SetPCMAudioFormat(pWaveFormat, cStereo, ss44100Hz, bs8Bit);
  696.     Stereo8bit48000Hz:
  697.       SetPCMAudioFormat(pWaveFormat, cStereo, ss48000Hz, bs8Bit);
  698.     Stereo16bit8000Hz:
  699.       SetPCMAudioFormat(pWaveFormat, cStereo, ss8000Hz, bs16Bit);
  700.     Stereo16bit11025Hz:
  701.       SetPCMAudioFormat(pWaveFormat, cStereo, ss11025Hz, bs16Bit);
  702.     Stereo16bit22050Hz:
  703.       SetPCMAudioFormat(pWaveFormat, cStereo, ss22050Hz, bs16Bit);
  704.     Stereo16bit44100Hz:
  705.       SetPCMAudioFormat(pWaveFormat, cStereo, ss44100Hz, bs16Bit);
  706.     Stereo16bit48000Hz:
  707.       SetPCMAudioFormat(pWaveFormat, cStereo, ss48000Hz, bs16Bit);
  708.   end;
  709. end;
  710. // Returns the standard PCM format specifier of a wave format.
  711. function GetPCMAudioFormat(const pWaveFormat: PWaveFormatEx): TPCMFormat;
  712. begin
  713.   Result := nonePCM;
  714.   with pWaveFormat^ do
  715.     if wFormatTag = WAVE_FORMAT_PCM then
  716.     begin
  717.       if (nChannels = 1) and (nSamplesPerSec = 8000) and (wBitsPerSample = 8) then
  718.         Result := Mono8Bit8000Hz
  719.       else if (nChannels = 2) and (nSamplesPerSec = 8000) and (wBitsPerSample = 8) then
  720.         Result := Stereo8Bit8000Hz
  721.       else if (nChannels = 1) and (nSamplesPerSec = 8000) and (wBitsPerSample = 16) then
  722.         Result := Mono16bit8000Hz
  723.       else if (nChannels = 2) and (nSamplesPerSec = 8000) and (wBitsPerSample = 16) then
  724.         Result := Stereo16Bit8000Hz
  725.       else if (nChannels = 1) and (nSamplesPerSec = 11025) and (wBitsPerSample = 8) then
  726.         Result := Mono8Bit11025Hz
  727.       else if (nChannels = 2) and (nSamplesPerSec = 11025) and (wBitsPerSample = 8) then
  728.         Result := Stereo8Bit11025Hz
  729.       else if (nChannels = 1) and (nSamplesPerSec = 11025) and (wBitsPerSample = 16) then
  730.         Result := Mono16bit11025Hz
  731.       else if (nChannels = 2) and (nSamplesPerSec = 11025) and (wBitsPerSample = 16) then
  732.         Result := Stereo16Bit11025Hz
  733.       else if (nChannels = 1) and (nSamplesPerSec = 22050) and (wBitsPerSample = 8) then
  734.         Result := Mono8Bit22050Hz
  735.       else if (nChannels = 2) and (nSamplesPerSec = 22050) and (wBitsPerSample = 8) then
  736.         Result := Stereo8Bit22050Hz
  737.       else if (nChannels = 1) and (nSamplesPerSec = 22050) and (wBitsPerSample = 16) then
  738.         Result := Mono16bit22050Hz
  739.       else if (nChannels = 2) and (nSamplesPerSec = 22050) and (wBitsPerSample = 16) then
  740.         Result := Stereo16Bit22050Hz
  741.       else if (nChannels = 1) and (nSamplesPerSec = 44100) and (wBitsPerSample = 8) then
  742.         Result := Mono8Bit44100Hz
  743.       else if (nChannels = 2) and (nSamplesPerSec = 44100) and (wBitsPerSample = 8) then
  744.         Result := Stereo8Bit44100Hz
  745.       else if (nChannels = 1) and (nSamplesPerSec = 44100) and (wBitsPerSample = 16) then
  746.         Result := Mono16bit44100Hz
  747.       else if (nChannels = 2) and (nSamplesPerSec = 44100) and (wBitsPerSample = 16) then
  748.         Result := Stereo16Bit44100Hz
  749.       else if (nChannels = 1) and (nSamplesPerSec = 48000) and (wBitsPerSample = 8) then
  750.         Result := Mono8Bit48000Hz
  751.       else if (nChannels = 2) and (nSamplesPerSec = 48000) and (wBitsPerSample = 8) then
  752.         Result := Stereo8Bit48000Hz
  753.       else if (nChannels = 1) and (nSamplesPerSec = 48000) and (wBitsPerSample = 16) then
  754.         Result := Mono16bit48000Hz
  755.       else if (nChannels = 2) and (nSamplesPerSec = 48000) and (wBitsPerSample = 16) then
  756.         Result := Stereo16Bit48000Hz
  757.     end;
  758. end;
  759. // Converts milliseconds to string
  760. function MS2Str(Milliseconds: DWORD; Fmt: TMS2StrFormat): String;
  761. var
  762.   HSecs, Secs, Mins, Hours: DWORD;
  763. begin
  764.   HSecs := Milliseconds div 10;
  765.   Secs := HSecs div 100;
  766.   Mins := Secs div 60;
  767.   Hours := Mins div 60;
  768.   if Fmt in [msAh, msA] then
  769.   begin
  770.     if Hours <> 0 then
  771.       if Fmt = msAh then Fmt := msHMSh  else Fmt := msHMS
  772.     else if Mins <> 0 then
  773.       if Fmt = msAh then Fmt := msMSh else Fmt := msMS
  774.     else
  775.       if Fmt = msAh then Fmt := msSh else Fmt := msS
  776.   end;
  777.   case Fmt of
  778.     msHMSh:
  779.       Result := Format('%u%s%2.2u%s%2.2u%s%2.2u',
  780.         [Hours, TimeSeparator, Mins mod 60, TimeSeparator, Secs mod 60, DecimalSeparator, HSecs mod 100]);
  781.     msHMS:
  782.       Result := Format('%u%s%2.2u%s%2.2u',
  783.         [Hours, TimeSeparator, Mins mod 60, TimeSeparator, Secs mod 60]);
  784.     msMSh:
  785.       Result := Format('%u%s%2.2u%s%2.2u',
  786.         [Mins, TimeSeparator, Secs mod 60, DecimalSeparator, HSecs mod 100]);
  787.     msMS:
  788.       Result := Format('%u%s%2.2u',
  789.         [Mins, TimeSeparator, Secs mod 60]);
  790.     msSh:
  791.       Result := Format('%u%s%2.2u',
  792.         [Secs, DecimalSeparator, HSecs mod 100]);
  793.     msS:
  794.       Result := Format('%u', [Secs]);
  795.   else
  796.     Result := IntToStr(Milliseconds);
  797.   end;
  798. end;
  799. // Waits for the scnchronize object while lets the caller thread processes
  800. // its messages.
  801. function WaitForSyncObject(SyncObject: THandle; Timeout: DWORD): DWORD;
  802. const
  803.   EVENTMASK = QS_PAINT or QS_TIMER or QS_SENDMESSAGE or QS_POSTMESSAGE;
  804. var
  805.   Msg: TMsg;
  806.   StartTime: DWORD;
  807.   EllapsedTime: DWORD;
  808.   Handle: THandle;
  809. begin
  810.   Handle := SyncObject;
  811.   if (SyncObject = GetCurrentThread) or (SyncObject = GetCurrentProcess) then
  812.     DuplicateHandle(GetCurrentProcess, SyncObject, GetCurrentProcess, @Handle, SYNCHRONIZE, False, 0);
  813.   try
  814.     repeat
  815.       StartTime := GetTickCount;
  816.       Result := MsgWaitForMultipleObjects(1, Handle, False, Timeout, EVENTMASK);
  817.       if Result = WAIT_OBJECT_0 + 1 then
  818.       begin
  819.         while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
  820.         begin
  821.           if ((Msg.message < WM_KEYFIRST) or (Msg.message > WM_KEYLAST)) and
  822.              ((Msg.message < WM_MOUSEFIRST) or (Msg.message > WM_MOUSELAST)) then
  823.           begin
  824.             TranslateMessage(Msg);
  825.             DispatchMessage(Msg);
  826.             if Msg.message = WM_QUIT then Exit;
  827.           end;
  828.         end;
  829.         if Timeout <> INFINITE then
  830.         begin
  831.           EllapsedTime := GetTickCount - StartTime;
  832.           if EllapsedTime < Timeout then
  833.             Dec(Timeout, EllapsedTime)
  834.           else
  835.             Timeout := 0;
  836.         end;
  837.       end;
  838.     until Result <> WAIT_OBJECT_0 + 1;
  839.   finally
  840.     if SyncObject <> Handle then
  841.       CloseHandle(Handle);
  842.   end;
  843. end;
  844. end.