The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 4,183 other subscribers

Delphi: playing Chimes.wav as an external file or embedded WAVE resource in Delphi XE5.

Posted by jpluimers on 2018/01/10

As a by-effect, this article seems to one of the few that shows where Delphi uses the .dres file extension introduced around Delphi XE.

Recently I had to play some notification sounds in a Windows Delphi application where the application deployment should be as easy as possible: preferable copying the EXE around.

Playing a sound file seems easy, especially if it is a [WayBackWAV file: just use the [WayBack] PlaySound or the (older) [WayBack] sndPlaySound API functions.

But if you start searching on the internet, you see lots of curious implementations for playing WAV resources through sndPlaySound.

The actual implementation is really really easy though, just make sure you follow the steps right and nothing can go wrong.

[WayBack] The full source code is on my BeSharp.net repository, here is how to to it step by step:

The steps depend on the MMSystem unit, so most of the code translates back to [WayBack] Turbo Pascal for Windows (yes, the 16-bit Pascal days when the MMSystem unit was introduced) with the exception of the SND_SENTRY flag.

The thing that more recent Delphi versions made a lot easier is embedding WAV files as WAVE resources, more on that further on.

All examples use the combination of the flags SND_NODEFAULT, SND_ASYNC or SND_SENTRY:

  • SND_NODEFAULT makes sure no other sound gets substituted if the file or resource cannot be found.
  • SND_ASYNC plays the sound asynchronously in a separate thread so your main thread continues to execute while the sound plays.
  • SND_SENTRY is for [WayBackWindows Vista and up to notify hearing impaired in a visual way that a sound is being played through a SoundSentry event.

Stop playing sound

Playing sounds starts with stopping anything that is playing from your current app with this line of code:


PlaySound(nil, 0, SND_ASYNC);

It uses the nil as a sound parameter in combination with the SND_ASYNC flag to stop playing any sound that started using the PlaySound or sndPlaySound functions in your application.

Playing a WAV file

This is also a one-liner.


PlaySound('C:\Windows\Media\chimes.wav', 0, SND_FILENAME or SND_NODEFAULT or SND_ASYNC or SND_SENTRY);

The initial demo project is a bit larger, as it involves a VCL Form and a project just for the single purpose on showing how the project will change when embedding a WAVE resource.

Embedding a WAV file as a WAVE resource

MARKDOWN_HASH4d6405500fb43f9f98154b18224bc165MARKDOWN_HASH” in the “MARKDOWN_HASH9e727fdd3aec8274f46685441900280dMARKDOWN_HASH” menu

In the past this was overly complicated: people were manually assembling RES files, or wrote [WayBackRC scripts to create RES files and included those in Delphi.

No need for that any more: somewhere around Delphi XE this got much easier:

  1. Open the Project menu, then choose Resources and Images... just like shown on the right:

  2. Press the Add button:

  1. Choose a .WAV file (in this case C:\Windows\Media\chimes.wav)

  1. Rename the resource to chimes_wav (to make sure there is no . in it):

  1. Ensure the Resource type is [WayBack] WAVE (PlaySound requires it, so do not use RCDATA or RC_DATA [WayBack]):

Performing the above steps result in a few very interesting changes in your Delphi project.

A. The cool thing is that Delphi now has embedded the resource as an external link the the .dproj file, as seen by this diff:

B. The .dpr file also got changed loading the *.dres file in-between the program and uses like:


program PlayChimesWavAsEmbeddedResourceVclProject;
{$R *.dres}
uses

Now you only need to add one more line of code to play this embedded WAVE resource…

Playing a WAVE resource

Two changes you need to do here:

  1. Use the resource name, not the file name.
  2. SND_RESOURCE flag.

Note there is no need any more for uppercase resource names, but there cannot be any periods in them as the [WayBackvalid characters in resource names are limited to A-Z, a-z, 0-9, and underscores ( _ ) and resource names must be unique in the scope of the application (not only the resource file).

So the line becomes this:


PlaySound('chimes_wav', HInstance, SND_RESOURCE or SND_NODEFAULT or SND_ASYNC or SND_SENTRY);

leaving the diff like this:

So there is [WayBackno need to use FindResource, LoadResource, LockResource, nor [WayBack] TResourceStream. You still can, but that makes it overly complicated.

With the PlaySound solution above, you might need to cast the string of the filename or resource name to PChar if you pass the string as variable, but that is about as complicated as it gets.

Playing from memory

I haven’t included an example for playing sounds from memory. The reason is that using [WayBackSND_MEMORY with SND_ASYNC requires you to make sure [WayBack] the memory buffer lasts longer than the sound will play. And since the sound plays asynchronously in a separate thread, you have to make sure the memory outlives the thread. Which can be hard.

As of Windows 7, Microsoft fixes this by buffering up to a maximum of 2 megabytes of WAV data for you, but still you need to be careful.

Playing cross plaform

When you want to play sounds in a cross platform way, you need to look into the FireMonkey [Archive.is] FMX TMediaPlayer. That is beyond the scope of this blog post, but a [WayBackDelphi XE4 example is here.

–jeroen

keywords:

  PlaySound(nil, 0, SND_ASYNC);

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

 
%d bloggers like this: