Sound Advice

First Published in QL Today V10/I4/p35

Thanks to the hard work done a number of people, its never been so easy to add decent sound to your programs as now. If you have a Qx0 or QPC2 you can use the SMSQ/E Sample Sound System (SSSS - shortened here to SSS, to encompass Qdos Classic) to play sound samples and even real music. This can make a big difference to the fun and usability of your programs.

Here I present a few simple routines that you can merge into your own programs directly, or just take a look at, to get an idea of how easy it can be to produce your own. These routines take into account that you may want your programs also to be able to run on platforms that dont have the supporting hardware, in which case they fall back on the familiar BEEP.

All you need in addition to the platforms mentioned (QPC2 must be V3.20 or later) are Wolfgang Lenerz'es Soundfile toolkit, which you can download from the Internet (contact details below) and some sound samples.

For distributable programs I prefer the Soundfile toolkit as it can be compiled in with the code, whether you use Turbo or Qlib. Simon Goodwin's sound device is more versatile, but needs to be loaded residentially at boot time or at least before starting your program.

Sound samples are a different matter. Until someone creates an online sound sample library, you will have to filch them from other programs (for example from the game D-Miner, available off my website) or record and/or convert your own. (How? Download Jonathan Hudson's Qdos port of SOX to convert standard .wav files to the ub format, or search for and download a Windoze compatible version of SOX on the Internet and use that.)

Ok, so lets get our hands dirty. My comments normally describe the code block immediately above, unless otherwise stated:

1 REMark $$asmb=win1_snd_sound_bin,0,10
2 :

If you want to compile the program with Qlib and include the soundfile toolkit with the code, you need this line. The corresponding Turbo directive is:

1 REMark %%win1_snd_sound_bin,0,10
2 :

You dont need to compile a separate version for non-SSS platforms as, provided you dont call any of the procedures in the toolkit, it wont error. The routines shown here take care of that for you.

3 REMark Test for presence of Sampled Sound System
4 TstSSS
5 :

One of the first things to do is to determine whether the SSS is present and set a switch accordingly. I use two global program switches relating to sound:

1) ssspresent can be true of false depending on whether the SSS was detected or not. This switch is set inside the TstSSS procedure.

2) sound is used by the sound subroutines to determine whether the user wants sound or not.

6 sx$ = '_ub'      : REMark Sound file extension
7 sd$ = 'ram1_snd_': REMark Sound file folder
8 :

Initialise some variables used in the sound routines. Better have them up front where they can be easily seen and altered, rather than buried somewhere deep inside your program.

9 game = 0: win = 1: REMark Some dummy game variables
10 RANDOMISE DATE
11 :

Ive devised a very silly "game" to help illustrate usage.

12 REMark User input or config file determines sound on/off
13 snd = YesNo('Do you want sound')
14 SetSound snd: REMark Switch sound on/off
15 PRINT 'Ready to rock'
16 Ping 'ok': PAUSE 50: REMark Allow time to display sound and text
17 :

Its always a good idea to give the user the choice of having sound or not. SetSound sets the global program sound switch to on or off. If sound is wanted, it also sets the switch to the best available sound. Finally Ping gives a confirming noise if sound is on (see below)

18 REMark Your program goes here. Eg..
19 :
20 CLS
21 REPeat main
22  PlayGame
23  IF game = win THEN
24   PRINT 'You win :o)'
25   Ping 'win'
26  ELSE
27   PRINT 'You loose :-('
28   Burp 'loose'
29  END IF
30  IF NOT YesNo("Another game"): EXIT main
31 END REPeat main
32 Ping 'bye'
33 :

The main program loop simulates a game. If you "win" it plays the "win_ub" sound sample, otherwise it plays the "loose_ub" sound sample. Thats really all there is to it! The rest is just detail!

Ping and Burp take a filename parameter which means you can re-use them to play different sound samples, but they fall back on only two kinds of beep: 'Nice beep' and 'Bad beep'. But this is a simplification for the purposes of this demonstration. If you know how to be creative with BEEP, you could do things differently and very much better. The point is that you can follow any action with a sound, as shown. Every click on a button could produce a different sample sound and fall back on a different beep if you wish.

Note: When storing sound samples in a library it is a good idea to group them and give them evocative names such as 'quack' or 'gunshot1'. However, when you use them in a program it is better to rename copies of them according to their function in the program. This makes the program logic clearer and also allows the user to replace the default sounds with his own selection.

34 DEFine PROCedure PlayGame
35 REMark Dummy game routine
36 game = RND(0 TO 1)
37 END DEFine PlayGame
38 :

Well, that was your free game ;o)

39 DEFine FuNction YesNo(txt$)
40 PRINT txt$; ' <Y/n>? ';
41 IF INKEY$(-1) INSTR 'y' & CHR$(10): PRINT 'Y': RETurn 1
42 PRINT 'N': RETurn 0
43 END DEFine YesNo
44 :

Here follow the sound routines:

45 REMark Sound routines
46 :
47 DEFine PROCedure SetSound(s)
48 IF s THEN
49  IF ssspresent: sound = 2: ELSE : sound = 1
50 ELSE
51  sound = 0
52 END IF
53 END DEFine SetSound
54 :

SetSound 0 turns the program sound off, while SetSound 1 turns sound on. It also determines the level of sound available to the program; either BEEP or SSS. All it is is a logical switch in a wrapper for convenience, as the option of changing the sound status should be available to the user at any time.

55 DEFine PROCedure Ping(nm$)
56 REMark Nice sound
57 IF sound = 2 THEN
58  IF FTEST(sd$ & nm$ & sx$) = 0 THEN
59   KILLSOUND: SOUNDFILE sd$ & nm$ & sx$
60  ELSE
61   BEEP 999, 2
62  END IF
63 ELSE
64  IF sound: BEEP 999, 2
65 END IF
66 END DEFine Ping
67 :
68 DEFine PROCedure Burp(nm$)
69 REMark Bad sound
70 IF sound = 2 THEN
71  IF FTEST(sd$ & nm$ & sx$) = 0 THEN
72   KILLSOUND: SOUNDFILE sd$ & nm$ & sx$
73  ELSE
74   BEEP 999, 255
75  END IF
76 ELSE
77  IF sound: BEEP 999, 255
78 END IF
79 END DEFine Burp
80 :

The logic of Ping and Burp is:
If sound is off: do nothing
If sound is on then
 If SSS available then
  If the file exists: play sample sound
In all other cases just beep

81 DEFine PROCedure TstSSS
82 LOCal adr
83 REMark GLOBal ssspresent
84 REMark Test for presence of Sampled Sound System
85 REMark V0.01 pjwitte 2oo5
86 :
87 adr = ALCHP(26)
88 POKE_L adr +  0, HEX("43fa0016"): REMark lea.l result,a1
89 POKE_L adr +  4, HEX("26780070"): REMark move.l exv_i4,a3
90 POKE_L adr +  8, HEX("0cab5353"): REMark cmp.l #sss.flag,-8(a3)
91 POKE_L adr + 12, HEX("5353fff8")
92 POKE_L adr + 16, HEX("57e90001"): REMark seq 1(a1)
93 POKE_W adr + 20, HEX("7000")    : REMark moveq#0,d0
94 POKE_W adr + 22, HEX("4e75")    : REMark rts
95 POKE_W adr + 24, 0              : REMark ds.w 1
96 CALL adr
97 ssspresent = PEEK_W(adr + 24)
98 RECHP adr
99 END DEFine TstSSS
100 :

TstSSS is the only little awkwardness that remains, and hopefully it will one day be redundant: There is no easy and documented way offered by the OS of determining whether the Sampled Sound System is available or not, so we have to resort to a bit of jiggery-pokery. All this routine does is check whether it can find the SSS flag at the documented location just above the interrupt server, and set the ssspresent switch accordingly.

The program and associated sound samples can be downloaded here.

Have fun!

©pjwitte November 8th 2005

D-Miner can be found here

The soundfile toolkit can be found at Dilwyn's QL repository

The Qdos version of Sox and Simon Goodwin's sound device and a collection of sound tools - including samples - can also be found there.