Home > other >  How to play wav file from the resources
How to play wav file from the resources

Time:05-21

I need to play and loop a WAV audio track from resources.

I found an answer to a similar question here: image

My resources look like this (don't mind the name of the project):

image

The code I pasted into my project is:

procedure TForm1.FormShow(Sender: TObject);
begin
  PlaySound(BG, 0, SND_RESOURCE or SND_ASYNC);
end;

And the whole thing looks like this:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Imaging.pngimage, Vcl.ExtCtrls, Unit2, Unit3, Unit4, Unit5,
  Vcl.MPlayer, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    MediaPlayer1: TMediaPlayer;
    Button1: TButton;
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure MediaPlayer1Enter(Sender: TObject);
    procedure MediaPlayer1Notify(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
procedure TForm1.FormShow(Sender: TObject);
begin
  PlaySound(BG, 0, SND_RESOURCE or SND_ASYNC);
end;

Maybe I need to include some library or something else? I'm new to Delphi.

CodePudding user response:

To use PlaySound() in Delphi, you simply need to add the Winapi.MMSystem unit to your uses clause.

But, since you also have a TMediaPlayer in your project, you could use that instead of PlaySound(), which would have the extra benefit of giving you more control over the playback (pausing/resuming, skipping, etc).

TMediaPlayer does not natively support playing WAV audio from a resource, but it can be done with a little extra coding.

Internally, TMediaPlayer uses MCI via the mciSendCommand() function. According to Microsoft (HOWTO: Use MCI to Play AVI/WAVE Files from Memory), you can setup MCI to play WAV audio from memory (such as a resource) by installing a custom IO callback, and then specifying that callback when opening the player device. Fortunately, the callback is triggered by file extension, hence this approach is compatible with the TMediaPlayer.FileName property.

So, you should be able to write an IO callback function with a custom file extension (for example, .RES for resource), and have that callback load the WAV resource and read its data, and then you would set MediaPlayer1.DeviceType to dtWaveAudio and MediaPlayer1.FileName to a filename ending with the custom extension. The rest is handled by the OS for you, and you can then use MediaPlayer1 to control the playback as needed.

For example:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Imaging.pngimage, Vcl.ExtCtrls, Unit2, Unit3, Unit4, Unit5,
  Vcl.MPlayer, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    MediaPlayer1: TMediaPlayer;
    Button1: TButton;
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure MediaPlayer1Enter(Sender: TObject);
    procedure MediaPlayer1Notify(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  Winapi.MMSystem;
  
{$R *.dfm}

function MAKEFOURCC(ch0, ch1, ch2, ch3: BYTE): FOURCC;
begin
  Result := DWORD(ch0) or (DWORD(ch1) shl 8) or (DWORD(ch2) shl 16) or (DWORD(ch3) shl 24); 
end;

function MyResourceIOProc(lpMMIOInfo: PMMIOInfo; uMessage: UINT; lParam1, lParam2: LPARAM): LRESULT; stdcall;
var
  Res: TResourceStream;

  function GetResourceStream: TResourceStream;
  begin
    Move(lpMMIOInfo.adwInfo, Result, SizeOf(TResourceStream));
  end;

  procedure SetResourceStream(Stream: TResourceStream);
  begin
    Move(Stream, lpMMIOInfo.adwInfo, SizeOf(TResourceStream));
  end;

begin
  case uMessage of
    MMIOM_OPEN: begin
      try
        Res := TResourceStream.Create(ChangeFileExt(PChar(lParam1), ''), 'WAVE');
      except
        SetResourceStream(nil);
        Exit(MMIOM_CANNOTOPEN);
      end;
      SetResourceStream(Res);
      lpMMIOInfo.lDiskOffset := 0;
      Exit(MMSYSERR_NOERROR);
    end;   
    MMIOM_CLOSE: begin
      Res := GetResourceStream;
      SetResourceStream(nil);
      Res.Free;
      Exit(MMSYSERR_NOERROR);
    end;   
    MMIOM_READ: begin
      Res := GetResourceStream;
      Move((PByte(Res.Memory)   lpMMIOInfo.lDiskOffset)^, Pointer(lParam1)^, lParam2);
      Inc(lpMMIOInfo.lDiskOffset, lParam2);
      Exit(lParam2);
    end;
    MMIOM_SEEK: begin
      case lParam2 of
        SEEK_SET: begin
          lpMMIOInfo.lDiskOffset := lParam1;
        end;
        SEEK_CUR: begin
          Inc(lpMMIOInfo.lDiskOffset, lParam1);
        end;
        SEEK_END: begin
          Res := GetResourceStream;
          lpMMIOInfo.lDiskOffset := Res.Size - 1 - lParam1;
        end;
      end;
      Exit(lpMMIOInfo.lDiskOffset);
    end;
  else
    Exit(MMSYSERR_NOERROR);
  end;
end;

const
  ccRES: FOURCC = MAKEFOURCC(Ord('R'), Ord('E'), Ord('S'), Ord(' '));

procedure TForm1.FormCreate(Sender: TObject);
begin
  mmioInstallIOProc(ccRES, TFNMMIOProc(MyResourceIOProc), MMIO_INSTALLPROC or MMIO_GLOBALPROC); 
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  mmioInstallIOProc(ccRES, nil, MMIO_REMOVEPROC);
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  MediaPlayer1.FileName := 'BG.RES ';
  MediaPlayer1.Open;
  MediaPlayer1.Play;
end;
  • Related