Basically, this is just an alternative system I've put together for DBPro's default 3D sound system (load 3dsound, position sound, etc). It has a few advantages over the standard system, like support for multiple listeners (which is useful for things like split-screen multiplayer games where one player might be close to a sound but the other isn't), and it works for pretty much any sound, not just mono ones.
It's not totally finished, and there are a couple of bugs, but I'll try and improve it soon!
Make sure you have Matrix1Utils! I'll try and remove it's dependency on that soon Here's the command list:
+ Code SnippetSYSTEM COMMANDS:
setup3dsound()
Sets up the 3d sound system, and creates the default listener (listener 0) This needs to be called before any other 3d sound commands are called!
update3dsound()
This needs to be placed somewhere in your main loop to update the system!
SOUND COMMANDS:
add3dsound(soundno)
Adds a loaded sound to the 3d sound system
NOTE: Sounds should be loaded as ordinary sounds (load sound), not 3d sounds!
position3dsound(soundno, posx#, posy#, posz#)
Positions the sound in 3d space
set3dsoundpan(soundno, flag)
Enables (1) or disables (0) the left/right panning on the sound, relevant to the listener. Disabling the panning leaves you with a simple 'louder when closer, quieter when further away' system
get3dsounddistance(soundno, listenno)
Returns the distance of a sound from the distance of a listener)
LISTENER COMMANDS:
add3dlistener(listenno)
Creates a new listener
position3dlistener(listenno, posx#, posy#, posz#)
Positions the listener in 3d space
scale3dlistener(listenno, scale#)
Changes the sensitivity of the listener. The default scale is 1. To mute a listener, change it's scale value to zero.
rotate3dlistener(listenno, angx#, angy#, angz#)
Rotates the 3d listener. This is only useful if you're using left/right sound panning. The X and Z values don't affect anything for now!
set3dlistenerpan(listenno, flag)
Same at set3dsoundpan (enables or disables sound panning), but applies to all sounds the listener hears instead of just individual sounds
And here's the code:
To stop it from polluting your code with a load of functions, it's probably best to put in a seperate .dba file and use #include to add it
+ Code Snippettype sound3d
soundnumber as integer
sposx# as float
sposy# as float
sposz# as float
spanflag as boolean
updated as boolean
pannedtimes as integer
pan as integer
volume as integer
endtype
type listen3d
listennumber as integer
lposx# as float
lposy# as float
lposz# as float
langx# as float
langy# as float
langz# as float
lscale# as float
lpanflag as boolean
endtype
function setup3dsound()
dim sounds3d() as sound3d
dim listeners() as listen3d
array insert at bottom listeners()
listeners().listennumber = 0
listeners().lscale# = 1.0
listeners().lpanflag = 1
endfunction
function add3dsound(soundno)
array index to top sounds3d()
while array index valid(sounds3d()) = 1
if sounds3d().soundnumber = soundno then exit prompt "Sound " + str$(soundno) + " has already been added as a 3d sound!", "Check your code -.-" : end
next array index sounds3d()
endwhile
array insert at bottom sounds3d()
sounds3d().soundnumber = soundno
sounds3d().spanflag = 1
endfunction
function position3dsound(soundno, posx#, posy#, posz#)
array index to top sounds3d()
while array index valid(sounds3d()) = 1
if sounds3d().soundnumber = soundno
sounds3d().sposx# = posx#
sounds3d().sposy# = posy#
sounds3d().sposz# = posz#
exit
endif
next array index sounds3d()
endwhile
endfunction
function set3dsoundpan(soundno, flag)
if flag < 0 then flag = 0
if flag > 1 then flag = 1
array index to top sounds3d()
while array index valid(sounds3d()) = 1
if sounds3d().soundnumber = soundno then sounds3d().spanflag = flag : exit
next array index sounds3d()
endwhile
if array index valid(sounds3d()) = 0 then exit prompt "Sound " + str$(soundno) + " doesn't exit...", "Check your code -.-" : end
endfunction
function get3dsounddistance(soundno, listenno)
array index to top sounds3d()
array index to top listeners()
while array index valid (sounds3d()) = 1
if sounds3d().soundnumber = soundno
while array index valid(listeners())
if listeners().listennumber = listenno
distance# = abs(listeners().lposx# - sounds3d().sposx#) + abs(listeners().lposy# - sounds3d().sposy#) + abs(listeners().lposz# - sounds3d().sposz#)
exit
endif
next array index listeners()
endwhile
exit
endif
next array index sounds3d()
endwhile
if array index valid(listeners()) = 0 then exit prompt "Listener " + str$(listenno) + " doesn't exit...", "Check your code -.-" : end
if array index valid(sounds3d()) = 0 then exit prompt "Sound " + str$(soundno) + " doesn't exit...", "Check your code -.-" : end
endfunction distance#
function add3dlistener(listenno)
array index to top listeners()
while array index valid(listeners()) = 1
if listeners().listennumber = listenno then exit prompt "Listener " + str$(listenno) + " has already been added!", "Check your code -.-" : end
next array index listeners()
endwhile
array insert at bottom listeners()
listeners().listennumber = listenno
listeners().lscale# = 1.0
listeners().lpanflag = 1
endfunction
function position3dlistener(listenno, posx#, posy#, posz#)
array index to top listeners()
while array index valid(listeners()) = 1
if listeners().listennumber = listenno
listeners().lposx# = posx#
listeners().lposy# = posy#
listeners().lposz# = posz#
exit
endif
next array index listeners()
endwhile
if array index valid(listeners()) = 0 then exit prompt "Listener " + str$(listenno) + " doesn't exist...", "Check your code -.-" : end
endfunction
function scale3dlistener(listenno, scale#)
if scale# < 0 then scale# = 0
array index to top listeners()
while array index valid(listeners()) = 1
if listeners().listennumber = listenno then listeners().lscale# = scale# : exit
next array index listeners()
endwhile
if array index valid(listeners()) = 0 then exit prompt "Listener " + str$(listenno) + " doesn't exist...", "Check your code -.-" : end
endfunction
function set3dlistenerpan(listenno, flag)
if flag < 0 then flag = 0
if flag > 1 then flag = 1
array index to top listeners()
while array index valid(listeners()) = 1
if listeners().listennumber = listenno then listeners().lpanflag = flag : exit
next array index listeners()
endwhile
if array index valid(listeners()) = 0 then exit prompt "Listener " + str$(listenno) + " doesn't exist...", "Check your code -.-" : end
endfunction
function rotate3dlistener(listenno, angx#, angy#, angz#)
array index to top listeners()
while array index valid(listeners()) = 1
if listeners().listennumber = listenno
listeners().langx# = angx#
listeners().langy# = angy#
listeners().langz# = angz#
exit
endif
next array index listeners()
endwhile
if array index valid(listeners()) = 0 then exit prompt "Listener " + str$(listenno) + " doesn't exist...", "Check your code -.-" : end
endfunction
function update3dsound()
rem Reset variables for all the sounds
array index to top sounds3d()
for i = 0 to array count(sounds3d())
sounds3d().updated = 0
sounds3d().pannedtimes = 0
next array index sounds3d()
next i
array index to top listeners()
while array index valid(listeners()) = 1
rem If scale is zero, the listener is muted
if listeners().lscale# > 0
array index to top sounds3d()
while array index valid(sounds3d()) = 1
rem Work out volume based on distance
distance# = abs(listeners().lposx# - sounds3d().sposx#) + abs(listeners().lposy# - sounds3d().sposy#) + abs(listeners().lposz# - sounds3d().sposz#)
distance# = distance# / (listeners().lscale# * 8)
volume = 100 - distance#
rem Work out sound pan, if flag is enabled
if sounds3d().spanflag = 1 and listeners().lpanflag = 1
distx# = sounds3d().sposx# - listeners().lposx#
distz# = sounds3d().sposz# - listeners().lposz#
angle# = atanfull(distx#, distz#)
panangle# = wrap(angle# - listeners().langy#, -179, 180)
if panangle# > 90 then panangle# = 90 - (panangle# - 90)
if panangle# < -90 then panangle# = -90 - (panangle# + 90)
if panangle# < -90 then panangle# = -90 : if panangle# > 90 then panangle# = 90
rem Volume is slightly affected by pan
if abs(wrap(angle# - listeners().langy#, -179, 180)) > 80 then volume = volume - (abs(wrap(angle# - listeners().langy#, -179, 180)) -80) / 23.5
rem Set pan - check for updated first using the pannedtimes variable, which is also used to figure out the mean pan at the end
if sounds3d().pannedtimes = 0
sounds3d().pan = 15.8 * panangle#
else
sounds3d().pan = sounds3d().pan + (13.8 * panangle#)
endif
sounds3d().pannedtimes = sounds3d().pannedtimes + 1
endif
if volume < 0 then volume = 0
rem Check to see if another listener is closer before updating the volume
if sounds3d().updated = 0
sounds3d().volume = volume
else
if volume > sounds3d().volume then sounds3d().volume = volume
endif
sounds3d().updated = 1
next array index sounds3d()
endwhile
endif
next array index listeners()
endwhile
rem Update the volume and pan
array index to top sounds3d()
for i = 0 to array count(sounds3d())
set sound volume sounds3d().soundnumber, sounds3d().volume
if sounds3d().spanflag = 1 and sounds3d().pannedtimes > 0
sounds3d().pan = sounds3d().pan / sounds3d().pannedtimes
set sound pan sounds3d().soundnumber, sounds3d().pan
endif
next array index sounds3d()
next i
endfunction
And finally, a couple of bugs I know about:
+ Code Snippet> If you want the listener rotated to the same angle as an object or the camera, you need to use Euler rotation(x/y/zrotate object).
Using relative rotation commands like 'turn object left' and 'turn object right' seems to give weird angles which sometimes bug the panning.
>Panning doesn't work very well with multiple listeners at the moment, especially when one's close to a sound and the other's far away.
Feel free to use it, modify it, improve it, eat it, teach it tricks... Do whatever you want