unit ACMOut;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ACMConvertor, MMSystem, MSACM;
type
  EACMOut = class(Exception);
  TBufferPlayedEvent = procedure(Sender : TObject; Header : PWaveHDR) of object;
  TACMOut = class(TComponent)
  private
    { Private declarations }
    FActive                   : Boolean;
    FNumBuffersLeft           : Byte;
    FBackBufferList           : TList;
    FNumBuffers               : Byte;
    FBufferList               : TList;
    FFormat                   : TACMWaveFormat;
    FOnBufferPlayed           : TBufferPlayedEvent;
    FWaveOutHandle            : HWaveOut;
    FWindowHandle             : HWnd;
    function GetBufferCount: Integer;
  protected
    { Protected declarations }
    function  NewHeader : PWaveHDR;
    procedure DisposeHeader(Header : PWaveHDR);
    procedure DoWaveDone(Header : PWaveHdr);
    procedure WndProc(var Message : TMessage);
  public
    { Public declarations }
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
    procedure Close;
    procedure Open(aFormat : TACMWaveFormat);
    procedure Play(var Buffer; Size : Integer);
    procedure RaiseException(const aMessage : String; Result : Integer);    property Active           : Boolean
      read FActive;
    property BufferCount      : Integer
      read GetBufferCount;
    property Format           : TACMWaveFormat
      read FFormat;
    property WindowHandle     : HWnd
      read FWindowHandle;  published
    { Published declarations }
    property NumBuffers      : Byte
      read FNumBuffers
      write FNumBuffers;
    property OnBufferPlayed   : TBufferPlayedEvent
      read FOnBufferPlayed
      write FOnBufferPlayed;
  end;procedure Register;implementationprocedure Register;
begin
  RegisterComponents('Sound', [TACMOut]);
end;{ TACMOut }procedure TACMOut.Close;
var
  X                           : Integer;
begin
  if not Active then exit;
  FActive := False;
  WaveOutReset(FWaveOutHandle);
  WaveOutClose(FWaveOutHandle);
  FBackBufferList.Clear;
  FWaveOutHandle := 0;
  For X:=FBufferList.Count-1 downto 0 do DisposeHeader(PWaveHDR(FBufferList[X]));
end;constructor TACMOut.Create(AOwner: TComponent);
begin
  inherited;
  FBufferList := TList.Create;
  FBackBufferList := TList.Create;
  FActive := False;
  FWindowHandle := AllocateHWND(WndProc);
  FWaveOutHandle := 0;
  FNumBuffers := 4;end;destructor TACMOut.Destroy;
begin
  if Active then Close;
  FBufferList.Free;
  DeAllocateHWND(FWindowHandle);
  FBackBufferList.Free;
  inherited;
end;procedure TACMOut.DisposeHeader(Header: PWaveHDR);
var
  X                           : Integer;
begin
  X := FBufferList.IndexOf(Header);
  if X < 0 then exit;
  Freemem(header.lpData);
  Freemem(header);
  FBufferList.Delete(X);
end;procedure TACMOut.DoWaveDone(Header : PWaveHdr);
var
  Res                         : Integer;
begin
  if not Active then exit;
  if Assigned(FOnBufferPlayed) then FOnBufferPlayed(Self, Header);
  Res := WaveOutUnPrepareHeader(FWaveOutHandle, Header, SizeOf(TWaveHDR));
  if Res <> 0 then RaiseException('WaveOut-UnprepareHeader',Res);
  DisposeHeader(Header);
end;function TACMOut.GetBufferCount: Integer;
begin
  Result := FBufferList.Count;
end;function TACMOut.NewHeader: PWaveHDR;
begin
  GetMem(Result, SizeOf(TWaveHDR));
  FBufferList.Add(Result);
end;procedure TACMOut.Open(aFormat: TACMWaveFormat);
var
  Res                         : Integer;
  Device                      : Integer;
  Params                      : Integer;
begin
  if Active then exit;
  FWaveOutHandle := 0;
  FNumBuffersLeft := FNumBuffers;
  FFormat := aFormat;  if FFormat.Format.wFormatTag = 1 then begin
    Params := CALLBACK_WINDOW or WAVE_MAPPED;
    Device := 0;
  end else begin
    Params := CALLBACK_WINDOW or WAVE_MAPPED;
    Device := 0;
  end;
  Res := WaveOutOpen(@FWaveOutHandle,Device,@FFormat.Format,FWindowHandle,0, params);
  if Res <> 0 then exit;
  //RaiseException('WaveOutOpen',Res);
  FActive := True;
end;procedure TACMOut.Play(var Buffer; Size: Integer);
var
  TempHeader                  : PWaveHdr;
  Data                        : Pointer;
  Res                         : Integer;
  X                           : Integer;  procedure PlayHeader(Header : PWaveHDR);
  begin
    Res := WaveOutPrepareHeader(FWaveOutHandle,Header,SizeOf(TWaveHDR));
    if Res <> 0 then RaiseException('WaveOut-PrepareHeader',Res);    Res := WaveOutWrite(FWaveOutHandle, Header, SizeOf(TWaveHDR));
    if Res <> 0 then RaiseException('WaveOut-Write',Res);
  end;begin
  if Size = 0 then exit;
  if not active then exit;
  TempHeader := NewHeader;
  GetMem(Data, Size);
  Move(Buffer,Data^,Size);
  with TempHeader^ do begin
    lpData := Data;
    dwBufferLength := Size;
    dwBytesRecorded :=0; //Was " := Size;" but not needed, and crashes some PC's
    dwUser := 0;
    dwFlags := 0;
    dwLoops := 1;
  end;  if FNumBuffersLeft > 0 then begin
    FBackBufferList.Add(TempHeader);
    Dec(FNumBuffersLeft);
  end else begin
    for X:=0 to FBackBufferList.Count-1 do
      PlayHeader(PWaveHDR(FBackBufferList[X]));
    FBackBufferList.Clear;
    PlayHeader(TempHeader);
  end;
end;procedure TACMOut.RaiseException(const aMessage: String; Result: Integer);
begin
  case Result of
    ACMERR_NotPossible : Raise EACMOut.Create(aMessage + ' The requested operation cannot be performed.');
    ACMERR_BUSY : Raise EACMOut.Create(aMessage + ' The conversion stream is already in use.');
    ACMERR_UNPREPARED : Raise EACMOut.Create(aMessage + ' Cannot perform this action on a header that has not been prepared.');
    MMSYSERR_InvalFlag : Raise EACMOut.Create(aMessage + ' At least one flag is invalid.');
    MMSYSERR_InvalHandle : Raise EACMOut.Create(aMessage + ' The specified handle is invalid.');
    MMSYSERR_InvalParam : Raise EACMOut.Create(aMessage + ' At least one parameter is invalid.');
    MMSYSERR_NoMem : Raise EACMOut.Create(aMessage + ' The system is unable to allocate resources.');
    MMSYSERR_NoDriver : Raise EACmOut.Create(aMessage + ' A suitable driver is not available to provide valid format selections.');
    MMSYSERR_ALLOCATED : Raise EACMOut.Create(aMessage + ' The specified resource is already in use.');
    MMSYSERR_BADDEVICEID : Raise EACMOut.Create(aMessage + ' The specified resource does not exist.');
    WAVERR_BADFORMAT : Raise EACMOut.Create(aMessage + ' Unsupported audio format.');
    WAVERR_SYNC : Raise EACMOut.Create(aMessage + ' The specified device does not support asynchronous operation.');
  else
    if Result <> 0 then
      Raise EACMOut.Create(SysUtils.Format('%s raised an unknown error (code #%d)',[aMessage,Result]));
  end;end;procedure TACMOut.WndProc(var Message: TMessage);
begin
  case Message.Msg of
    MM_WOM_DONE : DoWaveDone(PWaveHDR(Message.LParam));
  end;
end;end.

解决方案 »

  1.   

    上边是一个控件源程序,主调用方法:
    TACMOut.Open(aFormat: TACMWaveFormat);
    TACMOut.Play(var Buffer; Size: Integer);
    TACMOut.Close;网上去找acmaudio控件
    个人意见:
    原单元中有几处小错
    过程:procedure TACMOut.Open(aFormat: TACMWaveFormat);中作了一下改动
    (播放gsm格式声音的问题,改一下就好了,对于pcm格式可正常使用,acmin单元中也应作相应改动)
      

  2.   

    我现在有一个新问题了。
    WAVEHDR里面地buffer指针所指向的区域应该分配多大?我简单的分配为nAvgBytesPerSec可以否?昨天看了一天的资料,现在知道用MM_WOM_系列的消息了。
    但是在MM_WOM_OPEN里面第一次使用waveOutWrite就出错。
    不知道是指针问题还是调用函数出错。麻烦。
      

  3.   

    WaveIn(录音)WaveHdr.lpData Buffer size is Format.nAvgBytesPerSec;
    WaveOut(输出)那就想多大就多大,不超过内存即可。WaveOut:WaveOutOpen(FWaveID, 0, @FFormat, Handle, 0, CALLBACK_WINDOW or WAVE_MAPPED); //用这个Flag//对应的消息是:MM_WOM_DONE
    //procedure WaveOutCallback(var msg: TMessage); message MM_WOM_DONE;
    procedure TWaveOut.WaveOutCallback(var msg: TMessage);
    var
      Header: PWaveHdr;
    begin
      Header := PWaveHdr(msg.LParam);
      try
        if FActive then
        begin
          WaveOutUnPrepareHeader(FWaveID^, Header, SizeOf(TWaveHdr));
          //再继续输出,就是增加输出Buffer
          AutoPlayBuffer;
        end;
      finally
        DeleteBuffer(Header); //删除/Free指针
      end;
    end;还有错误处理,只要调用对应的GetErrorText即可,不要用上面的raiseException(ErrorCode)函数了。function GetLen(S: string): Integer;
    var
      Len: Integer;
    begin
      Result := 1;
      Len := Length(S);
      while not (S[Result] = #0) and (Result < Len) do Inc(Result);
    end;procedure TWaveOut.CheckError(Res: Integer);
    var
      S: string;
    begin
      if Res <> 0 then
      begin
        SetLength(S, MAXERRORLENGTH);
        waveOutGetErrorText(Res, PChar(S), MAXERRORLENGTH);
        //waveInGetErrorText(Res, PChar(S), MAXERRORLENGTH);
        SetLength(msg, GetLen(msg));
        raise EWaveException.Create(msg); 
      end;
    end;Wave一般操作是:
    New(WaveID);  //var WaveID: PInteger;
    WaveInOpen/WaveOutOpen打开这个WaveID,并为它提供消息回调
    之后
    WaveIn: 在回调函数中,进行录音数据处理(数据在消息的LParam, WParam中)
    ,因为在作存数据(Buffer),所以要增加一定的Buffer给它,waveInPrepareHeader-->waveInAddBuffer
    然后就可能WaveInStart了。WaveOut简单,打开后就直接WaveOutPrepareHeader-->WaveOutWrite知道就这些。大概也就这样,不明再问。
      

  4.   

    http://www.csdn.net/expert/topic/499/499112.xml?temp=.7824213
    看看那里面的TWaveOut类,给它一个Format: TWaveFormatEx,然后直接播放(PlayBack)试试
      

  5.   

    哦,那里面有一些BUG,如果你看到什么错了那是因为FWaveStream的问题,你稍改一下。:)