什么是在delphi中从数组数据中播放声音的最简单方法

时间:2010-08-11 08:48:00

标签: delphi audio multimedia

有没有简单的功能?我正在寻找类似的东西

播放(@data,44000,100 {时间});

5 个答案:

答案 0 :(得分:7)

我在PCM音频操作方面做了很多工作。在播放自定义波形音频数据的短序列时,我总是使用此功能:

var
  PlaySoundStopper: PBoolean;
  SoundPlayerActive: boolean = false;

procedure PlaySound(const Sound: TASSound);
var
  hWave: HWAVEOUT;
  hdr: TWaveHdr;
  buf: PAnsiChar;
  fmt: TWaveFormatEx;
  i: Integer;
  n: Integer;
begin

  try

    with fmt do
    begin
      wFormatTag := WAVE_FORMAT_PCM;
      nChannels := length(Sound.Channels);
      nSamplesPerSec := Sound.SampleRate;
      wBitsPerSample := 32;
      nAvgBytesPerSec := nChannels * nSamplesPerSec * wBitsPerSample div 8;
      nBlockAlign := nChannels * wBitsPerSample div 8;
      cbSize := 0;
    end;

    GetMem(buf, fmt.nChannels * length(Sound.Channels[0]) * sizeof(TASWaveformSample));
    if length(Sound.Channels) = 1 then
      CopyMemory(buf, @(Sound.Channels[0, 0]), length(Sound.Channels[0]) * sizeof(TASWaveformSample))
    else
      for i := 0 to high(Sound.Channels[0]) do
        for n := 0 to high(Sound.Channels) do
          CopyMemory(buf + sizeof(TASWaveformSample) * (i * fmt.nChannels + n), @(Sound.Channels[n, i]), sizeof(TASWaveformSample));

    if waveOutOpen(@hWave, WAVE_MAPPER, @fmt, 0, 0, CALLBACK_NULL) <> MMSYSERR_NOERROR then
      raise Exception.Create('SoundPlayerThread.Execute: waveOutOpen failed: ' + SysErrorMessage(GetLastError));

    ZeroMemory(@hdr, sizeof(hdr));
    with hdr do
    begin
      lpData := buf;
      dwBufferLength := fmt.nChannels * length(Sound.Channels[0]) * sizeof(TASWaveformSample);
      dwFlags := 0;
    end;

    try

      SoundPlayerActive := true;

      waveOutPrepareHeader(hWave, @hdr, sizeof(hdr));
      waveOutWrite(hWave, @hdr, sizeof(hdr));
      sleep(500);

      while waveOutUnprepareHeader(hWave, @hdr, sizeof(hdr)) = WAVERR_STILLPLAYING do
        if PlaySoundStopper^ then
        begin
          waveOutPause(hWave);
          waveOutUnprepareHeader(hWave, @hdr, sizeof(hdr));
          break;
        end
        else
          sleep(100);

    finally
      SoundPlayerActive := false;
      waveOutClose(hWave);
      FreeMem(buf);
    end;

  except
    on E: Exception do MessageBox(0, PChar(E.ClassName + ': ' + E.Message), 'Sound Playback Error', MB_ICONERROR);
  end;
end;

,其中

type
  TASWaveformSample = integer; // signed 32-bit; -2147483648..2147483647
  TASWaveformSamples = packed array of TASWaveformSample; // one channel
  PASSound = ^TASSound;
  TASSound = record
    Channels: packed array of TASWaveformSamples;
    SampleRate: cardinal;
  end;

或许更好的方法是使用线程进行播放。然后我做

var
  OwnerForm: HWND; // = 0;
  SndSource: PASSound; // = nil;
  ThreadPlaying: boolean; // = false;

type
  TSoundPlayerThread = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  end;

实施为

procedure TSoundPlayerThread.Execute;
var
  hWave: HWAVEOUT;
  hdr: TWaveHdr;
  buf: PAnsiChar;
  fmt: TWaveFormatEx;
  i: Integer;
  n: Integer;
begin

  ThreadPlaying := true;
  try

   try

      if not Assigned(SndSource) then
        Exit;

      with fmt do
      begin
        wFormatTag := WAVE_FORMAT_PCM;
        nChannels := length(SndSource^.Channels);
        nSamplesPerSec := SndSource^.SampleRate;
        wBitsPerSample := 32;
        nAvgBytesPerSec := nChannels * nSamplesPerSec * wBitsPerSample div 8;
        nBlockAlign := nChannels * wBitsPerSample div 8;
        cbSize := 0;
      end;

      GetMem(buf, fmt.nChannels * length(SndSource^.Channels[0]) * sizeof(TASWaveformSample));
      if length(SndSource^.Channels) = 1 then
        CopyMemory(buf, @(SndSource^.Channels[0, 0]), length(SndSource^.Channels[0]) * sizeof(TASWaveformSample))
      else
        for i := 0 to high(SndSource^.Channels[0]) do
          for n := 0 to high(SndSource^.Channels) do
            CopyMemory(buf + sizeof(TASWaveformSample) * (i * fmt.nChannels + n), @(SndSource^.Channels[n, i]), sizeof(TASWaveformSample));

      if waveOutOpen(@hWave, WAVE_MAPPER, @fmt, 0, 0, CALLBACK_NULL) <> MMSYSERR_NOERROR then
        raise Exception.Create('SoundPlayerThread.Execute: waveOutOpen failed: ' + SysErrorMessage(GetLastError));

      ZeroMemory(@hdr, sizeof(hdr));
      with hdr do
      begin
        lpData := buf;
        dwBufferLength := fmt.nChannels * length(SndSource^.Channels[0]) * sizeof(TASWaveformSample);
        dwFlags := 0;
      end;

      waveOutPrepareHeader(hWave, @hdr, sizeof(hdr));
      waveOutWrite(hWave, @hdr, sizeof(hdr));
      sleep(500);

      while waveOutUnprepareHeader(hWave, @hdr, sizeof(hdr)) = WAVERR_STILLPLAYING do
      begin
        sleep(100);
        if Terminated then
          waveOutReset(hWave);
      end;

      waveOutClose(hWave);
      FreeMem(buf);

    except
      on E: Exception do MessageBox(0, PChar(E.ClassName + ': ' + E.Message), 'TSoundPlayerThread', MB_ICONERROR);
    end;

  finally
    ThreadPlaying := false;
  end;
end;

答案 1 :(得分:4)

Wave Audio Package有TLiveAudioPlayer组件。它从缓冲区播放音频。

答案 2 :(得分:2)

Microsoft有Knowledge Base article告诉您如何使用MCI从内存中播放声音。您可能需要在数组中使用wave文件头,或者在第一次读取时复制正确的数据,但除此之外,它应该相当容易移植。

答案 3 :(得分:2)

Win32 API PlaySound函数可以使用其SND_MEMORY标志从内存块播放标准的RIFF编码音频(如WAV音频)。或者,如果音频位于应用的资源中,则可以改为使用SND_RESOURCE标志。

答案 4 :(得分:0)

我找不到不基于过时的 sndPlaySound 的完整解决方案,所以这里有两个函数可以从 TMemoryStream 和文件中播放“.wav”文件:

uses mmsystem;

procedure PlaySoundFromFile(FileName : String);
var
  mStream : TMemoryStream;
begin
  mStream := TMemoryStream.Create;
  Try mStream.LoadFromFile(FileName); Except End;
  If mStream.Size > 0 then PlaySoundFromStream(mStream);
  mStream.Free;
end;

procedure PlaySoundFromStream(mStream : TMemoryStream);
begin
  PlaySound(mStream.Memory,0,SND_MEMORY or SND_SYNC);
end;

声音是同步播放的,您可以从记忆中找到其他 PlaySound 标志,在 Remy 的回答的链接中。如果切换到异步播放,请确保在播放结束前不要清除声音记忆。