Embedding and playing Sound/Music in a Delphi Application

Or

The new old stuff…

We had to make the application play specific sounds as a status indicator that doesn’t require the user to look at the screen. To avoid external dependencies, the idea is to embed some WAV files as resources in the executable.

Now if you go and search online for some example, you’ll fine plenty of very similar recipes (and duplicates).

Unfortunately most of those are quite outdated and whilst still workable are not as simple as you can get it with the modern Delphi versions and require some command-line invoking the resource compiler BRCC32 manually… (just Google the title and you’ll see)

So let’s try to make the IDE work for us.

Add a resource file to your project:
File | New | Other |Text File

Choose extension .RC and check “Add new file to current project”:

Now fill it with a couple of resource declarations in the form of

ResName (up to you) RESTYPE Value

like:
Tada WAV "C:\Windows\Media\tada.wav"
Ring WAV "C:\Windows\Media\ringout.wav"

Next time you compile your project, the resources will be compiled and included in the exe.

Now, let’s see how to play the embedded Looney Tunes…

That’s also a part where snippets you can find here or there are somewhat outdated (or preserving compatibility with “old-old” Windows APIs)

Here ‘s some sample code to paste in the standard New VCL Forms Application where you dropped a Button1 object onto Form1:

unit UTestWav;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  mmsystem;

{$R *.dfm}

procedure FGPlayASound(const AResName: string);
 var
   HResource: TResourceHandle;
   HResData: THandle;
   PWav: Pointer;
 begin
  HResource := FindResource(HInstance, PChar(AResName), 'WAV');
  if HResource <> 0 then begin
    HResData:=LoadResource(HInstance, HResource);
    if HResData <> 0 then begin
      PWav:=LockResource(HResData);
      if Assigned(PWav) then begin
        // uses MMSystem
        sndPlaySound(nil, SND_NODEFAULT); // nil = stop currently playing
        sndPlaySound(PWav, SND_ASYNC or SND_MEMORY);
      end;
//      UnlockResource(HResData); // unnecessary per MSDN
//      FreeResource(HResData);   // unnecessary per MSDN
    end;
  end
  else
    RaiseLastOSError;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FGPlayASound('Tada');
end;

initialization
  ReportMemoryLeaksOnShutdown := True;
end.

Add 64 bits platform:

Compile and run the 32bits or 64 bits versions….

Voilà!
Now, it’s up to you to add any kind of resources you want.

Update:

As Chris Rolliston pointed out in a comment, there is an IDE alternative to creating the RC file and managing the resources manually that is worth mentioning. (Thanks Chris!)

Project | Resources and Images…

Now, there is only one little change to make in FGPlayASound:
You have to change the resource type in the call to FindResource from ‘WAV’ to RT_RCDATA.

Pros:

  • You can see your resource files right there in the Project Manager
  • You can add the file directly from a standard Open dialog.

Cons:

  • The choice of predefined types of resources is very limited (as is the files selector). Pretty much everything has to fall under the RCDATA “catch all”.
  • You cannot specify any other type of resource.
  • It is easier to load the wrong type of resource (Form dfm for instance) and try to play it as a WAV…
  • Chris also mentions a bug in the dialog forcing you to rename twice a resource you just added.

So when opening the exe with a resource editor, you see this:

vs what we had:

As usual, YMMV…

This entry was posted in Delphi, Resources, User Interface, Windows and tagged , , , , , , . Bookmark the permalink.

8 Responses to Embedding and playing Sound/Music in a Delphi Application

  1. You can also avoid creating a RC file manually by choosing Project|Resources and Images… from the main menu bar, and adding the resource items from there. Admitedly, the Resources and Images dialog box does have an annoying bug in which you sometimes have to rename a newly added resource twice, but nonetheless, you end up with each resource listed in the Project Manager, which I personally find quite convenient (e.g., it makes opening one up or seeing the file in Explorer very easy).

  2. Pingback: A little bit of zis, etc. #4 | A little bit of zis, a little byte of zat

  3. Eric says:

    This feature would be even more useful if it was at the source code level rather than at the project level (ie. if the $R directive would accept .rc files and instruct the build to handle them).

    A lot of resources are in practice attached to a library, a component or a form, and those can thus be shared across multiple projects.

    When handled at the project level, resources are a PITA as you have to manually take care of adding all the resources to all the projects, or you will just end up with (silently) outdated or missing resources. And there is also the issue it’s stored in the fragile dproj, which brings its very own bag of issues when versionned and shared across multiple developer machines.

    So while it sounds nice at first, in practice it’ not something you can safely use in the real world… :-(

  4. gordon says:

    I would love to know how to play and stop multiple wave files independently, not just as warning sounds.

  5. Oliver says:

    Eric, the $R-directive does accept .rc files. You can write:

    {$R MyResources.res ‘MyResources.rc’}

    This will instruct the resource compiler to compile MyResources.rc into MyResources.res before compiling the containing project. It’s documented, too: … well, kind of… that article does not mention that this directive will actually call the resource compiler into action… I would have to check but IIRC it only does so if you use the single quotes around the .rc file name.

    Cheers,

    Oliver

  6. Oliver says:

    An edit to my last comment (which is still in moderation as I type this):

    Apparently, the {$R MyResources.res ‘MyResources.rc’} will no longer invoke the resource compiler in recent Delphi versions. I have a suspicion that this functionality might have been removed when they switched to MSBuild… pity for that.

    At least there are enough references to this on the web to ensure me that I hadn’t just dreamed this up somehow…. ;)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>