Real 3D Sound

BlitzMax Forums/MiniB3D Module/Real 3D Sound

ziggy(Posted 2009) [#1]
I've set up a google code page for my latest project, wich is a module to provide real 3D sound to games being made with miniB3D. It works in a very similar way to the 3D sound routines that where present in B3D, but I've added some changes in order to benefit more of the OpenAL library.

This module is a forked version of the previous work made by Klepto2 (minisound3d) and Mark sibly (the OpenAL max driver). but my version has some differences on the classes design, has had several fixes and improvements, and let's you use some of the features OpenAL has (such as real per-entity doppler effect).

The name of this semi-new module is sound3D, It has been tested to work pretty well on latest minib3d version, and I've released it under the MIT license, so if anyone wants to join, change it, fork it or whatever, development is open!

It is more or less a finished work, but I will be happy to fix any issue that may appear.

This is the link of the project: http://sound3d.googlecode.com

It has a downloadable version, but I will not be creating a download for every revision commited, so if you like this module and use it, feel free to get tortoise SVN or any other subversion client.


outsider(Posted 2009) [#2]
Hi, I have little stupid question :/ how to install and use it?


Ked(Posted 2009) [#3]
http://code.google.com/p/sound3d/downloads/list

Then compile. Then rebuild documentation.


outsider(Posted 2009) [#4]
Thank you.


ziggy(Posted 2009) [#5]
Example, based on a bradford6 old example, updated to work with Sound3d. It shows a very basic setup, so it is a good starting point for people willing to use this.
(hope Bradford6 don't mind me posting it here)
[bbcode]'Based on a bradford6 example, modified to work with Sound3D
Strict
Import sidesign.minib3d
Import blide.sound3d

If Not OpenALInstalled()
OpenURL("http://www.openal.org/downloads.html")
runtimeerror "OpenAL is not installed on this system, "

endif

Graphics3D 800, 600, 0

Local Cam:TCamera = CreateCamera()
Local CentralSphere:TEntity = CreateSphere()
'We associate the CentralSphere entity with the Sound3D listener:
SetListenerTargetEntity(CentralSphere)

Local cube:TMesh = CreateCube()
PositionEntity(cube , 0 , 0 , 10)

MoveEntity cam,0,10,20 ' this is 180 degrees
PointEntity cam, CentralSphere
Local Sound:T3DSound = Load3DSound("fire.wav")

Local time:Float = 0
Const Radius:Int = 10

While Not (KeyHit(KEY_ESCAPE) Or AppTerminate())

Local Z:Float = -Sin(Time)* radius
Local X:Float = Cos(Time) * radius
time:+.25
PositionEntity cube,x,0,z

If KeyHit(KEY_SPACE) Then EmitSound(Sound, Cube, False)

RenderWorld()

BeginMax2D()
DrawText("Press SPACE to make the cube emit a sound", 0, 0)
EndMax2D()

Flip
Wend[/bbcode]

Place a wav file called fire.wav in the same folder as this example. remember that 3D positioning in OpenAL only work on SINGLE CHANNEL AUDIO FILES. Stereo files will not be 3D-located.

You can get a mono sample file here: http://www.blide.org/posts/fire.wav

[EDIT:]Source code modified to make it even simpler and Strict

Last edited show_time('2012-05-20 11:46:40')


outsider(Posted 2009) [#6]
Thanks ziggy, it's work very well :D


ziggy(Posted 2009) [#7]
forgot to mention that if you have a 7.1 set-up with an audio card that supports it... you'll get surround sound, wich is amazing.

by the way, you should also thank Klepto2, I've only updated-mantained and modified slightly his work, adding a bit of functionality.


outsider(Posted 2009) [#8]
I have 7.1 card, but I have only 2 speakers (yet).

One more time thanks you ziggy and Klepto2 for this piece of code :)


ziggy(Posted 2009) [#9]
I've just commited some updates with small fixes, if anyone is using this, you better get latest SVN version.

EDIT: You'll need the OpenAL library installed in your machine. Otherwise it won't work.


Kryzon(Posted 2010) [#10]
Hello, I just noticed that the direction of the listener is set up by attaching two child Pivots to the listener Entity and later on retrieving the delta's of their positions to use as vectors.

When updating the listener's position, all it's child entities run the UpdateMat() function from miniB3D, which incurs in the following:
format_code('
'mat is the entity's matrix
mat.Translate(px,py,pz)
mat.Rotate(rx,ry,rz)
mat.Scale(sx,sy,sz)
')These are 3 transforms.

If you were to call TFormVector to grab the AT and UP vectors, you would save a few of those transforms as the TFormVector function is but the following:
format_code('
mat.Overwrite(src_ent.mat)

mat.grid[3,0]=0
mat.grid[3,1]=0
mat.grid[3,2]=0
mat.grid[3,3]=1
mat.grid[0,3]=0
mat.grid[1,3]=0
mat.grid[2,3]=0

mat.Translate(x#,y#,-z#)
x#=mat.grid[3,0]
y#=mat.grid[3,1]
z#=-mat.grid[3,2]

tformed_x#=x#
tformed_y#=y#
tformed_z#=z#
')
With Translate being the only [almost free] transform (Overwrite is simply retrieving values). This of course, when used to extract the AT and UP vectors from the Listener (so the Dest entity of TFormVector is Null).

Therefore, I suggest to anyone who'd like to squeeze some more performance juice, to exchange the parts of the Core.BMX code of this module that involve the Up and At Dummies, for the TFormVector equivalents.


ziggy(Posted 2010) [#11]
If you have any modified version, I can update the repository at googlecode.


Kryzon(Posted 2010) [#12]
I'll see what I can do, hold on tight! EDIT: look below


Kryzon(Posted 2010) [#13]
Here's what needs to be modified:

CORE.BMX

@ sListener3D declaration
format_code('
Type sListener3D Abstract
Global ent:TEntity
'Global atDummy:TEntity -- no need for this, can erase it.
'Global upDummy:TEntity -- no need for this, can erase it.

[...]

Function TargetEntity(ent:TEntity)
sListener3D.ent = ent ' -- function TargetEntity just needs this line. No more pivot creation.
End Function

[...]

Function Update() '-- exchanged the parts about pivots for the TFormVector equivalents.
If ent <> Null Then
alListener3f(AL_POSITION, EntityX(ent, True) , EntityY(ent, True) , EntityZ(ent, True))
Local O:Float[6]
'at
TEntity.TFormVector(0,0,1,ent,Null)
O[0] = ent.tformed_x#
O[1] = ent.tformed_y#
O[2] = ent.tformed_z#
'up
TEntity.TFormVector(0,1,0,ent,Null)
O[3] = ent.tformed_x#
O[4] = ent.tformed_y#
O[5] = ent.tformed_z#
alListenerfv(AL_ORIENTATION, O)
EndIf
End Function

[...]

End Type
')


ziggy(Posted 2010) [#14]
I'll update them tomorrow. Thanks for sharing!


BLaBZ(Posted 2011) [#15]
Is there a way to adjust the volume?! Or the strength of the emitter?


BLaBZ(Posted 2011) [#16]
Nvm, just had to change the ScaleFactor :)


RifRaf(Posted 2011) [#17]
is this the best sound solution for minib3d ?


Kryzon(Posted 2011) [#18]
This is the most tangible one, if that's what you mean. It uses the BlitzMax native audio module with the OpenAL driver, so it's cross-platform and gives 3D sound support.

Other solutions (such as FMod or IrrKLang) could offer more options but would need to be wrapped.


ziggy(Posted 2011) [#19]
@RofRaf: It's open source, you're free to improve it if you need anything else. I'm not improving it any more, as it does what I wanted it to do properly. If anyone wants to fork it in order to improve it or whatever, don't hesitate to do it, and remember to share...


Paul "Taiphoz"(Posted 2011) [#20]
Ah this is really cool. nice work.


BLaBZ(Posted 2011) [#21]
Hey Ziggy, I'm having a real hard time with this piece of code

format_code('
Try
SetPosition(TVector.Create(EntityX(Source, True) , EntityY(Source, True) , EntityZ(Source, True)))
Local ED:Float = Abs(EntityDistance(source, sListener3D.ent)) * sListener3D.ScaleFactor
If ED <.5 Then ED =.5
Channel.SetVolume(100:Float * _logicalvolume / ED)
Catch o:Object
'DebugStop() 'Somethimes the hook is fired before a died item is collected from the tlist!
End Try
')

Seems to work in debug mode but not in release


ziggy(Posted 2012) [#22]
@BLaBZ: Do you have any source code sample that replicates the issue? Are you using a threaded compilation of your game? It seems something related to the GC, we could add some additional checks to ensure enything is not null before it's referended inside this try/catch block


BLaBZ(Posted 2012) [#23]
Found out I had to call TSource3d.Free() if I remove the mesh, otherwise the try catch failed and I get an exception access violation.


Though now I'm getting a memory leak, I have around 250 simultaneous sounds, and calling Free() doesn't seem to free all of the resources :/


ziggy(Posted 2012) [#24]
I've changed to repository to Mercurial. Feel free to make a clone and pull any modifications that may be required. What makes you think you have a mem leak?


Zethrax(Posted 2012) [#25]
I'm having trouble getting the 'SetVolume' method of the TSource3D Type to work in the code below. I've tested and confirmed that SetVolume is being called when it should be and is being fed the correct values. The problem seem to lie with SetVolume itself. Basically the sound plays fine but I just can't change the volume level.

format_codebox('
' - Update avatar thruster sound.
' Note that the thruster sound will automaticlly stop playing if the avatar dies or runs out of fuel as the 'thruster_thrust_applied' flag is nulled at the start of the update loop.
If Self.thrust_on And thruster_thrust_applied
Local channel_restarted_flag = False
' If the channel is not playing then start a new one.
If Self.thruster_sound_channel <> Null ' If a channel exists.
If Not Self.thruster_sound_channel.isPlaying() ' If the channel has stopped playing
Self.thruster_sound_channel.Pause( False )
channel_restarted_flag = True
EndIf
Else ' The channel does not exist so create a new one.
Self.thruster_sound_channel = EmitSound( G_avatar_thruster_sound, Self.feet_pivot, True ) ' Sound will loop.
channel_restarted_flag = True
EndIf
' Select the volume level based on the thruster speed.
Select Self.speed_mode
Case 0 ' Low speed mode.
thruster_channel_volume:Float = 0.25
Case 1 ' High speed mode.
thruster_channel_volume:Float = 0.5
End Select
' Set the volume level if the speed has changed or the channel has been re-started.
If ( thruster_channel_volume:Float <> Self.thruster_sound_channel_volume:Float ) Or channel_restarted_flag
Self.thruster_sound_channel_volume:Float = thruster_channel_volume:Float
Self.thruster_sound_channel.SetVolume( Self.thruster_sound_channel_volume:Float )
EndIf
Else
' If no thrust is being applied then pause the thruster sound.
If Self.thruster_sound_channel <> Null ' If the thruster sound channel exists.
If Self.thruster_sound_channel.isPlaying() Then Self.thruster_sound_channel.Pause( True )
Self.thruster_sound_channel = Null ' Null the sound channel variable to indicate that the sound has been stopped.
EndIf
EndIf
'---
')

Last edited show_time('2012-12-03 00:19:54')


Zethrax(Posted 2012) [#26]
There seems to be an error with this module, or at least that's what the OpenAL error function alGetError() is telling me on my system.

core.bmx > Type TSource3D > Method Play > Line: alSourcef T3DChannel(Channel)._source._id, AL_MAX_GAIN, 100.0

This line causes an AL_INVALID_VALUE error to be returned from the OpenAL error function alGetError(). Changing the AL_MAX_GAIN value from 100.0 to 1.0 gets rid of the error.

To test, replace the error line with (AL_MAX_GAIN set to original value of 100.0):-

DebugLog "1: " + alGetError() ; alSourcef T3DChannel(Channel)._source._id, AL_MAX_GAIN, 100.0 ; DebugLog "2: " + alGetError()

And then change it to (AL_MAX_GAIN set to 1.0:-

DebugLog "1: " + alGetError() ; alSourcef T3DChannel(Channel)._source._id, AL_MAX_GAIN, 1.0 ; DebugLog "2: " + alGetError()

Don't forget to remove the debuglog statements when you're done.

This doesn't fix my problem with changing volume not working though.


Captain Wicker (crazy hillbilly)(Posted 2013) [#27]
@ziggy
the module is broken. i tried on Ubuntu 12.04, OSX 10.8, Windows XP SP3, and Windows 7.


dw817(Posted 2016) [#28]
Captain Wicker, my last name is Wicker so I had to check your Email. As it's a domain I checked it too. And ... on page 2 of your intro it says, "... that solve porblems ..."

You definitely wanna change that. :)

As for your tagline, I see boiled peanuts for sale in the store. Never had them, are they any good compared to roasted ?