what is the simpliest way to play sound from array data in delphi

后端 未结 4 1554
说谎
说谎 2020-12-31 21:01

Is there any simple function? I am searching something like that

Play(@data, 44000, 100 {time});

相关标签:
4条回答
  • 2020-12-31 21:11

    I have worked quite a lot with PCM audio manipulation. I always use this function when playing short sequences of custom waveform audio data:

    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;
    

    where

    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;
    

    A perhaps better way, is to use a thread for the playing. Then I do

    var
      OwnerForm: HWND; // = 0;
      SndSource: PASSound; // = nil;
      ThreadPlaying: boolean; // = false;
    
    type
      TSoundPlayerThread = class(TThread)
      private
        { Private declarations }
      protected
        procedure Execute; override;
      end;
    

    implemented as

    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;
    
    0 讨论(0)
  • 2020-12-31 21:21

    Microsoft has a Knowledge Base article telling you how you can play sound from memory using MCI. You'll probably need to have the wave file header in your array, or otherwise copy in the right data during the first read, but other than that it should be fairly easy to port over.

    0 讨论(0)
  • 2020-12-31 21:23

    Wave Audio Package has TLiveAudioPlayer component. It plays audio from buffer.

    0 讨论(0)
  • 2020-12-31 21:32

    The Win32 API PlaySound function can play standard RIFF-encoded audio (such as WAV audio) from a memory block by using its SND_MEMORY flag. Alternatively, if the audio is in the app's resources, you can use the SND_RESOURCE flag instead.

    0 讨论(0)
提交回复
热议问题