Posted: 8th Dec 2013 1:27
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 Snippet
SYSTEM 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 Snippet
type 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