Current issue

Vol.26 No.4

Vol.26 No.4


© 1984-2024
British APL Association
All rights reserved.

Archive articles posted online on request: ask the archivist.


Volume 16, No.2

This article might contain pre-Unicode character-mapped APL code.
See here for details.

Hackers’ Corner: Making Waves in WIN32

by Adrian Smith (

The one piece of luggage I invariably forget to pack is some kind of travelling alarm clock. This habit came home to roost in Austria on our eclipse-spotting outing, when we needed to be up early to catch the morning coach for a cross-border raid on the Czech Republic. Of course I had a computer with me, but Delia (being nearly 4 years old) has no sound card, so I regret to say I wrote my wake-up call in APL*PLUS/PC using ŒSOUND. However this reminded me that several people had been having trouble getting WAV files to play properly on Windows95, and that I had never updated my little workspace that plays sounds from memory. I rather expected this to be easy, but good ol’ Microsoft had gone out of their corporate way to make things awkward, as usual ....

Playing WAV Files in Windows95

Here is a simple translation of the old 16-bit code ...

     ’ {r}„{flg}Play snd;sps
[1]   © Play a .WAV file (synchronous by default)
[2]   ©  flg„+/0(synch) 1(asynch) 2(no deflt) 8(repeat) 16(nostop)
[3]    snd„snd,(~'.'¹snd)/'.WAV'
[4]    'sps'ŒNA'U winmm.C32|sndPlaySoundA <0T U'
[5]    –(0=ŒNC'flg')/'flg„0'
[6]    r„sps snd flg

Of course they changed the name of the DLL and you need to add an ‘A’ to the function name, but so far so good. It makes a noise. However when you call it with a 1 as the optional left argument, silence. This is a real nuisance if you want sound effects in games, as you really want the noise to keep playing after control has returned to APL. Stef provided the answer to this one ...

     ’ {r}„{flg}Play snd
[1]   © Play a .WAV file (asynchronous by default)
[2]   ©  flg„+/0(synch) 1(asynch) 2(no deflt) 8(repeat) 16(nostop)
[3]    snd„snd,(~'.'¹snd)/'.WAV'
[4]    'sps'ŒNA'U winmm.C32|sndPlaySoundA <0T U'
[5]    –(0=ŒNC'flg')/'flg„0'
[6]    r„sps snd flg

Spot the difference! That’s right, the boot’s bigger [1]. Well actually the header line is smaller, by 4 characters – when the ŒNA call goes out of scope the sound dies, so you must not localise it. First problem solved, but Adrian just a little peeved.

Playing Sounds from Memory

This is the real he-man stuff. The 16-bit code made heavy use of HMEMCPY to move bytes into a block of allocated, locked memory. Attempts to make the obvious translation to 32-bit calls met with nothing but VALUE ERROR as I tried kernel32, user32 and so on. Having nearly given up, I dug into the Windows documentation on the CD and found no trace of HMEMCPY in the Win32 API. However I did get some hits in Windows.H, but as a #DEFINE to something like RTLCopyMemory. OK, let’s look for this ... once again no joy so back to the header file to discover that a little further down, this is itself defined as RtlMoveMemory, with exactly the same syntax as before. Actually, it copies, and the significance of the Rtl is that the rightmost bytes are copied first which can matter if your memory blocks overlap. So, to play a sound from memory:

     ’ PlaySound sound;GlobalAlloc;GlobalLock;GlobalUnlock;
[1]   © Add header structure to sound and play from memory image.
[2]    sound„AddHdr sound
[3]    ŒNA'U kernel32.C32|GlobalAlloc U U'
[4]    ŒNA'U kernel32.C32|GlobalLock U'
[5]    ŒNA'U kernel32.C32|GlobalUnlock U'
[6]    ŒNA'U kernel32.C32|GlobalFree U'
[7]    ŒNA'kernel32.C32|RtlMoveMemory U <U1[] U'
[8]    ŒNA'U winmm.C32|sndPlaySoundA U U'
[9]    handle„GlobalAlloc 0(½sound)
[10]   pointer„GlobalLock handle
[11]   sink„RtlMoveMemory pointer sound(½sound)
[12]  © The <4> is the flag to play from memory at <pointer>
[13]   sink„sndPlaySoundA pointer 4
[14]  © Don't forgat to free the memory!!
[15]   sink„GlobalUnlock handle
[16]   sink„GlobalFree handle

Wow! Let’s try it ...

     ’ MakeTSF
[1]   © Make Tonic Sol Fah as variables
[2]    (doh re mi fah soh lah ti dohtop)„{100,þ440×(2*÷12)*+/¾†0 2 2 1 2 2 2 1}¨¼8
doh re mi
 440 100  493.8833013 100  554.365262 100
     ’ {r}„Square arg;ŒIO;sam;freq;ampl;dur;data;size;dieaway;vol
[1]    freq vol„arg ª dur„2˜400÷freq ª ŒIO„0 ª sam„14567
[2]    size„˜dur×sam
[3]    dieaway„freq÷1000000
[4]    ampl„1000×*-dieaway×¼size
[5]    data„˜ampl×size½1±±2×(freq×dur)×(¼size)÷size
[6]   © Add random noise to leading edge (percussive effect)
[7]    (200†data)+„?200½1000
[8]    data„˜data×vol÷0.1×—/|data
[9]    data„(ampl>2)/data
[10]   data„255˜0—128+data
[11]   r„data

     ½Square doh

     ’ Scale
[1]    {PlaySound Square ¾}¨doh re mi fah soh lah ti dohtop
     ’ r„AddHdr snd;hdr;sam;size;ŒIO
[1]   © Add header structure to sound, assuming standard sampling rate.
[2]   ©    R  I  F  F {size}     W  A  V  E   f   m   t  bl          ... etc
[3]    hdr„82 73 70 70 6 192 0 0 87 65 86 69 102 109 116 32 16 0 0 0 1 0 1 0 17 43
            0 0 17 43 0 0 1 0 8 0 100 97 116 97 226 191 0 0
[4]    ŒIO„0 ª sam„14567 ª size„œ½snd
[6]    hdr[24 25 28 29]„4½²256 256‚sam
[7]    hdr[¹4 40+›¼4]„8½²256 256 256 256‚size
[9]   © Return completed structure, ready to be played ...
[10]   r„hdr,snd

OK, it sounds terrible, as the lower notes take a little longer to compute than the high ones! However for making happy beeping noises at the end of a long process – the sort of thing we always used ŒSOUND for – it works rather well.


  1. Jeremy Clarkson, BBC Top Gear, on the new Mazda MX-5.
script began 10:55:14
caching off
debug mode off
cache time 3600 sec
indmtime not found in cache
cached index is fresh
recompiling index.xml
index compiled in 0.1782 secs
read index
read issues/index.xml
identified 26 volumes, 101 issues
array (
  'id' => '10008850',
regenerated static HTML
article source is 'HTML'
source file encoding is ''
read as 'Windows-1252'
URL: #ref => art10008850#ref
completed in 0.2189 secs