From af6bf8ddc82559be87299813d827685590a22513 Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Mon, 1 May 2017 11:14:09 -0700 Subject: [PATCH] plumb URDF/SDF audio_source into PhysicsServerCommandProcessor, allow to play sounds on collision ! See also https://youtu.be/eppOjTfx5Jg for a first test, and this modified URDF how to add sounds: https://github.com/bulletphysics/bullet3/blob/master/data/plane_with_collision_audio.urdf Add the --audio flag to enable sound in pybullet/Bullet-C-API --- data/plane_with_collision_audio.urdf | 37 +++++ .../ExampleBrowser/OpenGLExampleBrowser.cpp | 2 +- .../ImportURDFDemo/BulletUrdfImporter.cpp | 20 ++- .../ImportURDFDemo/BulletUrdfImporter.h | 4 +- .../ImportURDFDemo/MyMultiBodyCreator.h | 2 +- .../Importers/ImportURDFDemo/SDFAudioTypes.h | 46 +++++++ .../ImportURDFDemo/URDFImporterInterface.h | 4 +- .../Importers/ImportURDFDemo/UrdfParser.cpp | 76 +++++++++++ .../Importers/ImportURDFDemo/UrdfParser.h | 3 + examples/RobotSimulator/MinitaurSetup.cpp | 1 + .../RobotSimulator/RobotSimulatorMain.cpp | 4 +- .../PhysicsServerCommandProcessor.cpp | 126 ++++++++++++------ examples/TinyAudio/b3ADSR.cpp | 14 +- examples/TinyAudio/b3ADSR.h | 4 +- examples/TinyAudio/b3SoundEngine.cpp | 27 +++- examples/TinyAudio/b3SoundEngine.h | 4 +- examples/TinyAudio/b3SoundSource.cpp | 4 +- examples/TinyAudio/b3SoundSource.h | 2 +- examples/Utils/b3HashString.h | 59 ++++++++ src/Bullet3Common/b3HashMap.h | 52 -------- .../CollisionDispatch/btCollisionObject.h | 1 + src/LinearMath/btHashMap.h | 5 + 22 files changed, 389 insertions(+), 108 deletions(-) create mode 100644 data/plane_with_collision_audio.urdf create mode 100644 examples/Importers/ImportURDFDemo/SDFAudioTypes.h create mode 100644 examples/Utils/b3HashString.h diff --git a/data/plane_with_collision_audio.urdf b/data/plane_with_collision_audio.urdf new file mode 100644 index 000000000..b3a189628 --- /dev/null +++ b/data/plane_with_collision_audio.urdf @@ -0,0 +1,37 @@ + + + + + 0.4 + 0.01 + 0.0001 + 0.5 + 0.005 + .5 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/ExampleBrowser/OpenGLExampleBrowser.cpp b/examples/ExampleBrowser/OpenGLExampleBrowser.cpp index ab6bf5c6b..4751c7be4 100644 --- a/examples/ExampleBrowser/OpenGLExampleBrowser.cpp +++ b/examples/ExampleBrowser/OpenGLExampleBrowser.cpp @@ -48,7 +48,7 @@ //quick test for file import, @todo(erwincoumans) make it more general and add other file formats #include "../Importers/ImportURDFDemo/ImportURDFSetup.h" #include "../Importers/ImportBullet/SerializeSetup.h" - +#include "../Utils/b3HashString.h" #include "Bullet3Common/b3HashMap.h" struct GL3TexLoader : public MyTextureLoader diff --git a/examples/Importers/ImportURDFDemo/BulletUrdfImporter.cpp b/examples/Importers/ImportURDFDemo/BulletUrdfImporter.cpp index 6dd01345d..9080f8954 100644 --- a/examples/Importers/ImportURDFDemo/BulletUrdfImporter.cpp +++ b/examples/Importers/ImportURDFDemo/BulletUrdfImporter.cpp @@ -1134,9 +1134,9 @@ bool BulletURDFImporter::getLinkColor(int linkIndex, btVector4& colorRGBA) const return false; } -bool BulletURDFImporter::getLinkContactInfo(int linkIndex, URDFLinkContactInfo& contactInfo ) const +bool BulletURDFImporter::getLinkContactInfo(int urdflinkIndex, URDFLinkContactInfo& contactInfo ) const { - UrdfLink* const* linkPtr = m_data->m_urdfParser.getModel().m_links.getAtIndex(linkIndex); + UrdfLink* const* linkPtr = m_data->m_urdfParser.getModel().m_links.getAtIndex(urdflinkIndex); if (linkPtr) { const UrdfLink* link = *linkPtr; @@ -1146,6 +1146,22 @@ bool BulletURDFImporter::getLinkContactInfo(int linkIndex, URDFLinkContactInfo& return false; } +bool BulletURDFImporter::getLinkAudioSource(int linkIndex, SDFAudioSource& audioSource) const +{ + UrdfLink* const* linkPtr = m_data->m_urdfParser.getModel().m_links.getAtIndex(linkIndex); + if (linkPtr) + { + const UrdfLink* link = *linkPtr; + if (link->m_audioSource.m_flags & SDFAudioSource::SDFAudioSourceValid) + { + audioSource = link->m_audioSource; + return true; + } + } + return false; +} + + void BulletURDFImporter::convertLinkVisualShapes2(int linkIndex, int urdfIndex, const char* pathPrefix, const btTransform& localInertiaFrame, class btCollisionObject* colObj, int bodyUniqueId) const { if (m_data->m_customVisualShapesConverter) diff --git a/examples/Importers/ImportURDFDemo/BulletUrdfImporter.h b/examples/Importers/ImportURDFDemo/BulletUrdfImporter.h index 76d34c7c7..60ce11847 100644 --- a/examples/Importers/ImportURDFDemo/BulletUrdfImporter.h +++ b/examples/Importers/ImportURDFDemo/BulletUrdfImporter.h @@ -41,8 +41,10 @@ public: virtual bool getLinkColor(int linkIndex, btVector4& colorRGBA) const; - virtual bool getLinkContactInfo(int linkIndex, URDFLinkContactInfo& contactInfo ) const; + virtual bool getLinkContactInfo(int urdflinkIndex, URDFLinkContactInfo& contactInfo ) const; + virtual bool getLinkAudioSource(int linkIndex, SDFAudioSource& audioSource) const; + virtual std::string getJointName(int linkIndex) const; virtual void getMassAndInertia(int linkIndex, btScalar& mass,btVector3& localInertiaDiagonal, btTransform& inertialFrame) const; diff --git a/examples/Importers/ImportURDFDemo/MyMultiBodyCreator.h b/examples/Importers/ImportURDFDemo/MyMultiBodyCreator.h index 453f97311..93ffdfa5c 100644 --- a/examples/Importers/ImportURDFDemo/MyMultiBodyCreator.h +++ b/examples/Importers/ImportURDFDemo/MyMultiBodyCreator.h @@ -4,6 +4,7 @@ #include "MultiBodyCreationInterface.h" #include "LinearMath/btAlignedObjectArray.h" +#include "LinearMath/btHashMap.h" struct GUIHelperInterface; class btMultiBody; @@ -28,7 +29,6 @@ protected: struct GUIHelperInterface* m_guiHelper; - btAlignedObjectArray m_6DofConstraints; public: diff --git a/examples/Importers/ImportURDFDemo/SDFAudioTypes.h b/examples/Importers/ImportURDFDemo/SDFAudioTypes.h new file mode 100644 index 000000000..5dd7b827c --- /dev/null +++ b/examples/Importers/ImportURDFDemo/SDFAudioTypes.h @@ -0,0 +1,46 @@ +#ifndef SDF_AUDIO_TYPES_H +#define SDF_AUDIO_TYPES_H + +#include + + + +///See audio_source element in http://sdformat.org/spec?ver=1.6&elem=link +struct SDFAudioSource +{ + enum + { + SDFAudioSourceValid=1, + SDFAudioSourceLooping=2, + }; + + int m_flags; //repeat mode (0 = no repeat, 1 = loop forever) + + std::string m_uri; //media filename of the sound, .wav file + double m_pitch; //1 = regular rate, -1 play in reverse + double m_gain; //normalized volume in range [0..1] where 0 is silent, 1 is most loud + + double m_attackRate; + double m_decayRate; + double m_sustainLevel; + double m_releaseRate; + + double m_collisionForceThreshold; //force that will trigger the audio, in Newton. If < 0, audio source is invalid + + int m_userIndex; + + SDFAudioSource() + : m_flags(0), + m_pitch(1), + m_gain(1), + m_attackRate(0.0001), + m_decayRate(0.00001), + m_sustainLevel(0.5), + m_releaseRate(0.0005), + m_collisionForceThreshold(0.5), + m_userIndex(-1) + { + } +}; + +#endif //SDF_AUDIO_TYPES_H diff --git a/examples/Importers/ImportURDFDemo/URDFImporterInterface.h b/examples/Importers/ImportURDFDemo/URDFImporterInterface.h index 220c4e588..00071c867 100644 --- a/examples/Importers/ImportURDFDemo/URDFImporterInterface.h +++ b/examples/Importers/ImportURDFDemo/URDFImporterInterface.h @@ -5,7 +5,7 @@ #include "LinearMath/btAlignedObjectArray.h" #include "LinearMath/btTransform.h" #include "URDFJointTypes.h" - +#include "SDFAudioTypes.h" class URDFImporterInterface { @@ -40,6 +40,8 @@ public: ///this API will likely change, don't override it! virtual bool getLinkContactInfo(int linkIndex, URDFLinkContactInfo& contactInfo ) const { return false;} + virtual bool getLinkAudioSource(int linkIndex, SDFAudioSource& audioSource) const { return false;} + virtual std::string getJointName(int linkIndex) const = 0; //fill mass and inertial data. If inertial data is missing, please initialize mass, inertia to sensitive values, and inertialFrame to identity. diff --git a/examples/Importers/ImportURDFDemo/UrdfParser.cpp b/examples/Importers/ImportURDFDemo/UrdfParser.cpp index cf5b9c626..f5462ceff 100644 --- a/examples/Importers/ImportURDFDemo/UrdfParser.cpp +++ b/examples/Importers/ImportURDFDemo/UrdfParser.cpp @@ -663,6 +663,82 @@ bool UrdfParser::parseLink(UrdfModel& model, UrdfLink& link, TiXmlElement *confi } } + { + //optional 'audio_source' parameters + //modified version of SDF audio_source specification in //http://sdformat.org/spec?ver=1.6&elem=link + #if 0 + + file://media/audio/cheer.mp3 + 2.0 + 1.0 + false + + collision + + + #endif + TiXmlElement* ci = config->FirstChildElement("audio_source"); + if (ci) + { + link.m_audioSource.m_flags |= SDFAudioSource::SDFAudioSourceValid; + + const char* fn = ci->Attribute("filename"); + if (fn) + { + link.m_audioSource.m_uri = fn; + } else + { + if (TiXmlElement* filename_xml = ci->FirstChildElement("uri")) + { + link.m_audioSource.m_uri = filename_xml->GetText(); + } + } + if (TiXmlElement* pitch_xml = ci->FirstChildElement("pitch")) + { + link.m_audioSource.m_pitch = urdfLexicalCast(pitch_xml->GetText()); + } + if (TiXmlElement* gain_xml = ci->FirstChildElement("gain")) + { + link.m_audioSource.m_gain = urdfLexicalCast(gain_xml->GetText()); + } + + if (TiXmlElement* attack_rate_xml = ci->FirstChildElement("attack_rate")) + { + link.m_audioSource.m_attackRate = urdfLexicalCast(attack_rate_xml->GetText()); + } + + if (TiXmlElement* decay_rate_xml = ci->FirstChildElement("decay_rate")) + { + link.m_audioSource.m_decayRate = urdfLexicalCast(decay_rate_xml->GetText()); + } + + if (TiXmlElement* sustain_level_xml = ci->FirstChildElement("sustain_level")) + { + link.m_audioSource.m_sustainLevel = urdfLexicalCast(sustain_level_xml->GetText()); + } + + if (TiXmlElement* release_rate_xml = ci->FirstChildElement("release_rate")) + { + link.m_audioSource.m_releaseRate = urdfLexicalCast(release_rate_xml->GetText()); + } + + if (TiXmlElement* loop_xml = ci->FirstChildElement("loop")) + { + std::string looptxt = loop_xml->GetText(); + if (looptxt == "true") + { + link.m_audioSource.m_flags |= SDFAudioSource::SDFAudioSourceLooping; + } + } + if (TiXmlElement* forceThreshold_xml = ci->FirstChildElement("collision_force_threshold")) + { + link.m_audioSource.m_collisionForceThreshold= urdfLexicalCast(forceThreshold_xml->GetText()); + } + //todo: see other audio_source children + //pitch, gain, loop, contact + + } + } { //optional 'contact' parameters TiXmlElement* ci = config->FirstChildElement("contact"); diff --git a/examples/Importers/ImportURDFDemo/UrdfParser.h b/examples/Importers/ImportURDFDemo/UrdfParser.h index 3cdde368b..c22745b39 100644 --- a/examples/Importers/ImportURDFDemo/UrdfParser.h +++ b/examples/Importers/ImportURDFDemo/UrdfParser.h @@ -5,6 +5,7 @@ #include "LinearMath/btAlignedObjectArray.h" #include "LinearMath/btHashMap.h" #include "URDFJointTypes.h" +#include "SDFAudioTypes.h" #define btArray btAlignedObjectArray #include @@ -140,6 +141,8 @@ struct UrdfLink URDFLinkContactInfo m_contactInfo; + SDFAudioSource m_audioSource; + UrdfLink() :m_parentLink(0), m_parentJoint(0) diff --git a/examples/RobotSimulator/MinitaurSetup.cpp b/examples/RobotSimulator/MinitaurSetup.cpp index e867d0583..9f78c0283 100644 --- a/examples/RobotSimulator/MinitaurSetup.cpp +++ b/examples/RobotSimulator/MinitaurSetup.cpp @@ -2,6 +2,7 @@ #include "b3RobotSimulatorClientAPI.h" #include "Bullet3Common/b3HashMap.h" +#include "../Utils/b3HashString.h" struct MinitaurSetupInternalData { diff --git a/examples/RobotSimulator/RobotSimulatorMain.cpp b/examples/RobotSimulator/RobotSimulatorMain.cpp index 3fafac9e8..fb1072fd0 100644 --- a/examples/RobotSimulator/RobotSimulatorMain.cpp +++ b/examples/RobotSimulator/RobotSimulatorMain.cpp @@ -16,7 +16,7 @@ int main(int argc, char* argv[]) //sim->connect(eCONNECT_UDP, "localhost", 1234); sim->configureDebugVisualizer( COV_ENABLE_GUI, 0); // sim->configureDebugVisualizer( COV_ENABLE_SHADOWS, 0);//COV_ENABLE_WIREFRAME - sim->setTimeOut(10); + sim->setTimeOut(12345); //syncBodies is only needed when connecting to an existing physics server that has already some bodies sim->syncBodies(); b3Scalar fixedTimeStep = 1./240.; @@ -33,7 +33,7 @@ int main(int argc, char* argv[]) //b3BodyInfo bodyInfo; //sim->getBodyInfo(blockId,&bodyInfo); - sim->loadURDF("plane.urdf"); + sim->loadURDF("plane_with_collision_audio.urdf"); b3RobotSimulatorLoadUrdfFileArgs args; args.m_startPosition.setValue(0,0,2); diff --git a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp index 2d58ccb1e..1fcba6111 100644 --- a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp +++ b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp @@ -119,6 +119,7 @@ struct SharedMemoryDebugDrawer : public btIDebugDraw }; + struct InteralBodyData { btMultiBody* m_multiBody; @@ -128,6 +129,9 @@ struct InteralBodyData btTransform m_rootLocalInertialFrame; btAlignedObjectArray m_linkLocalInertialFrames; +#ifdef B3_ENABLE_TINY_AUDIO + btHashMap m_audioSources; +#endif //B3_ENABLE_TINY_AUDIO InteralBodyData() :m_multiBody(0), @@ -1303,7 +1307,6 @@ struct PhysicsServerCommandProcessorInternalData TinyRendererVisualShapeConverter m_visualConverter; #ifdef B3_ENABLE_TINY_AUDIO b3SoundEngine m_soundEngine; - int m_wavIds[3]; #endif PhysicsServerCommandProcessorInternalData() @@ -1513,49 +1516,66 @@ void PhysicsServerCommandProcessor::processCollisionForces(btScalar timeStep) for (int i = 0; i < numContactManifolds; i++) { const btPersistentManifold* manifold = m_data->m_dynamicsWorld->getDispatcher()->getInternalManifoldPointer()[i]; - for (int p=0;pgetNumContacts();p++) + + bool objHasSound[2]; + objHasSound[0] = (0!=(manifold->getBody0()->getCollisionFlags() & btCollisionObject::CF_HAS_COLLISION_SOUND_TRIGGER)); + objHasSound[1] = (0!=(manifold->getBody1()->getCollisionFlags() & btCollisionObject::CF_HAS_COLLISION_SOUND_TRIGGER)); + const btCollisionObject* colObjs[2] = {manifold->getBody0(),manifold->getBody1()}; + + for (int ob = 0;ob<2;ob++) { - double imp = manifold->getContactPoint(p).getAppliedImpulse(); - //printf ("manifold %d, contact %d, lifeTime:%d, appliedImpulse:%f\n",i,p, manifold->getContactPoint(p).getLifeTime(),imp); - - if (imp>0.4 && manifold->getContactPoint(p).getLifeTime()==1) + if (objHasSound[ob]) { - int soundSourceIndex = m_data->m_soundEngine.getAvailableSoundSource(); - if (soundSourceIndex>=0) + int uid0 = -1; + int linkIndex = -2; + + const btMultiBodyLinkCollider* mblB = btMultiBodyLinkCollider::upcast(colObjs[ob]); + if (mblB && mblB->m_multiBody) { - b3SoundMessage msg; - msg.m_releaseRate = 0.0001; - msg.m_attackRate = 1.; - msg.m_type = B3_SOUND_SOURCE_WAV_FILE; - - int cardboardIndex = float(rand())/float(RAND_MAX)*2.9; + linkIndex = mblB->m_link; + uid0 = mblB->m_multiBody->getUserIndex2(); + } + const btRigidBody* bodyB = btRigidBody::upcast(colObjs[ob]); + if (bodyB) + { + uid0 = bodyB->getUserIndex2(); + linkIndex = -1; + } - msg.m_wavId = m_data->m_wavIds[cardboardIndex]; + if ((uid0<0)||(linkIndex<-1)) + continue; - float rnd = rand()% 2; - - msg.m_frequency = 100+rnd*10.; + InternalBodyHandle* bodyHandle0 = m_data->getHandle(uid0); + SDFAudioSource* audioSrc = bodyHandle0->m_audioSources[linkIndex]; + if (audioSrc==0) + continue; - m_data->m_soundEngine.startSound(soundSourceIndex,msg); - m_data->m_soundEngine.releaseSound(soundSourceIndex); + for (int p=0;pgetNumContacts();p++) + { + double imp = manifold->getContactPoint(p).getAppliedImpulse(); + //printf ("manifold %d, contact %d, lifeTime:%d, appliedImpulse:%f\n",i,p, manifold->getContactPoint(p).getLifeTime(),imp); + if (imp>audioSrc->m_collisionForceThreshold && manifold->getContactPoint(p).getLifeTime()==1) + { + int soundSourceIndex = m_data->m_soundEngine.getAvailableSoundSource(); + if (soundSourceIndex>=0) + { + b3SoundMessage msg; + msg.m_attackRate = audioSrc->m_attackRate; + msg.m_decayRate = audioSrc->m_decayRate; + msg.m_sustainLevel = audioSrc->m_sustainLevel; + msg.m_releaseRate = audioSrc->m_releaseRate; + msg.m_amplitude = audioSrc->m_gain; + msg.m_frequency = audioSrc->m_pitch; + msg.m_type = B3_SOUND_SOURCE_WAV_FILE; + msg.m_wavId = audioSrc->m_userIndex; + msg.m_autoKeyOff = true; + m_data->m_soundEngine.startSound(soundSourceIndex,msg); + } + } } } } - int linkIndexA = -1; - int linkIndexB = -1; - - int objectIndexB = -1; - - const btRigidBody* bodyB = btRigidBody::upcast(manifold->getBody1()); - if (bodyB) - { - objectIndexB = bodyB->getUserIndex2(); - } - const btMultiBodyLinkCollider* mblB = btMultiBodyLinkCollider::upcast(manifold->getBody1()); - if (mblB && mblB->m_multiBody) - { - } } #endif//B3_ENABLE_TINY_AUDIO } @@ -1623,9 +1643,6 @@ void PhysicsServerCommandProcessor::createEmptyDynamicsWorld() #ifdef B3_ENABLE_TINY_AUDIO m_data->m_soundEngine.init(16,true); - m_data->m_wavIds[0] = m_data->m_soundEngine.loadWavFile("wav/cardboardbox0.wav"); - m_data->m_wavIds[1] = m_data->m_soundEngine.loadWavFile("wav/cardboardbox1.wav"); - m_data->m_wavIds[2] = m_data->m_soundEngine.loadWavFile("wav/cardboardbox2.wav"); //we don't use those callbacks (yet), experimental // gContactAddedCallback = MyContactAddedCallback; @@ -2039,7 +2056,6 @@ bool PhysicsServerCommandProcessor::loadUrdf(const char* fileName, const btVecto BulletURDFImporter u2b(m_data->m_guiHelper, &m_data->m_visualConverter); - bool loadOk = u2b.loadURDF(fileName, useFixedBase); @@ -2111,6 +2127,22 @@ bool PhysicsServerCommandProcessor::loadUrdf(const char* fileName, const btVecto } createJointMotors(mb); +#ifdef B3_ENABLE_TINY_AUDIO + { + SDFAudioSource audioSource; + int urdfRootLink = u2b.getRootLinkIndex();//LinkIndex = creation.m_mb2urdfLink[-1]; + if (u2b.getLinkAudioSource(urdfRootLink,audioSource)) + { + int flags = mb->getBaseCollider()->getCollisionFlags(); + mb->getBaseCollider()->setCollisionFlags(flags | btCollisionObject::CF_HAS_COLLISION_SOUND_TRIGGER); + audioSource.m_userIndex = m_data->m_soundEngine.loadWavFile(audioSource.m_uri.c_str()); + if (audioSource.m_userIndex>=0) + { + bodyHandle->m_audioSources.insert(-1, audioSource); + } + } + } +#endif //serialize the btMultiBody and send the data to the client. This is one way to get the link/joint names across the (shared memory) wire UrdfLinkNameMapUtil* util = new UrdfLinkNameMapUtil; @@ -2130,6 +2162,7 @@ bool PhysicsServerCommandProcessor::loadUrdf(const char* fileName, const btVecto bodyHandle->m_linkLocalInertialFrames.reserve(mb->getNumLinks()); for (int i=0;igetNumLinks();i++) { + int link=i; //disable serialization of the collision objects util->m_memSerializer->m_skipPointers.insert(mb->getLink(i).m_collider,0); int urdfLinkIndex = creation.m_mb2urdfLink[i]; @@ -2148,8 +2181,23 @@ bool PhysicsServerCommandProcessor::loadUrdf(const char* fileName, const btVecto m_data->m_strings.push_back(jointName); util->m_memSerializer->registerNameForPointer(jointName->c_str(),jointName->c_str()); mb->getLink(i).m_jointName = jointName->c_str(); +#ifdef B3_ENABLE_TINY_AUDIO + { + SDFAudioSource audioSource; + int urdfLinkIndex = creation.m_mb2urdfLink[link]; + if (u2b.getLinkAudioSource(urdfLinkIndex,audioSource)) + { + int flags = mb->getLink(link).m_collider->getCollisionFlags(); + mb->getLink(i).m_collider->setCollisionFlags(flags | btCollisionObject::CF_HAS_COLLISION_SOUND_TRIGGER); + audioSource.m_userIndex = m_data->m_soundEngine.loadWavFile(audioSource.m_uri.c_str()); + if (audioSource.m_userIndex>=0) + { + bodyHandle->m_audioSources.insert(link, audioSource); + } + } + } } - +#endif std::string* baseName = new std::string(u2b.getLinkName(u2b.getRootLinkIndex())); m_data->m_strings.push_back(baseName); diff --git a/examples/TinyAudio/b3ADSR.cpp b/examples/TinyAudio/b3ADSR.cpp index 2a526a4c8..a96354245 100644 --- a/examples/TinyAudio/b3ADSR.cpp +++ b/examples/TinyAudio/b3ADSR.cpp @@ -23,6 +23,7 @@ b3ADSR::b3ADSR() m_releaseRate = 0.0005; m_sustainLevel = 0.5; m_state = ADSR_IDLE; + m_autoKeyOff = false; } b3ADSR::~b3ADSR() @@ -51,6 +52,10 @@ double b3ADSR::tick() { m_value = m_sustainLevel; m_state = ADSR_SUSTAIN; + if (m_autoKeyOff) + { + keyOff(); + } } } else @@ -60,6 +65,11 @@ double b3ADSR::tick() { m_value = m_sustainLevel; m_state = ADSR_SUSTAIN; + if (m_autoKeyOff) + { + keyOff(); + } + } } break; @@ -81,8 +91,9 @@ bool b3ADSR::isIdle() const return m_state == ADSR_IDLE; } -void b3ADSR::keyOn() +void b3ADSR::keyOn(bool autoKeyOff) { + m_autoKeyOff = autoKeyOff; if (m_target <= 0.0) m_target = 1.0; if (m_attackRate==1) @@ -94,6 +105,7 @@ void b3ADSR::keyOn() void b3ADSR::keyOff() { + m_autoKeyOff = false; m_target = 0.0; m_state = ADSR_RELEASE; diff --git a/examples/TinyAudio/b3ADSR.h b/examples/TinyAudio/b3ADSR.h index 3523a7e86..db3b088be 100644 --- a/examples/TinyAudio/b3ADSR.h +++ b/examples/TinyAudio/b3ADSR.h @@ -11,7 +11,7 @@ class b3ADSR double m_releaseRate; double m_releaseTime; double m_sustainLevel; - + bool m_autoKeyOff; public: b3ADSR(); @@ -19,7 +19,7 @@ public: double tick(); bool isIdle() const; - void keyOn(); + void keyOn(bool autoKeyOff); void keyOff(); void setValues(double attack,double decay,double sustain,double release) diff --git a/examples/TinyAudio/b3SoundEngine.cpp b/examples/TinyAudio/b3SoundEngine.cpp index 47f0ef4d9..478e0efc8 100644 --- a/examples/TinyAudio/b3SoundEngine.cpp +++ b/examples/TinyAudio/b3SoundEngine.cpp @@ -7,6 +7,8 @@ #include "Bullet3Common/b3AlignedObjectArray.h" #include "b3ReadWavFile.h" #include "../Utils/b3ResourcePath.h" +#include "../Utils/b3HashString.h" + #include "Bullet3Common/b3HashMap.h" // The default real-time audio input and output buffer size. If @@ -24,6 +26,8 @@ struct b3SoundEngineInternalData b3AlignedObjectArray m_soundSources; b3HashMap m_wavFiles; + b3HashMap m_name2wav; + int m_wavFileUidGenerator; b3SoundEngineInternalData() @@ -84,6 +88,19 @@ void b3SoundEngine::exit() delete m_data->m_soundSources[i]; } m_data->m_soundSources.clear(); + + for (int i=0;im_wavFiles.size();i++) + { + b3ReadWavFile** wavPtr = m_data->m_wavFiles.getAtIndex(i); + if (wavPtr && *wavPtr) + { + b3ReadWavFile* wav = *wavPtr; + delete wav; + } + } + m_data->m_wavFiles.clear(); + m_data->m_name2wav.clear(); + } int b3SoundEngine::getAvailableSoundSource() @@ -113,7 +130,7 @@ void b3SoundEngine::startSound(int soundSourceIndex, b3SoundMessage msg) soundSource->setOscillatorFrequency(0, msg.m_frequency); soundSource->setOscillatorFrequency(1, msg.m_frequency); - soundSource->startSound(); + soundSource->startSound(msg.m_autoKeyOff); break; } case B3_SOUND_SOURCE_WAV_FILE: @@ -124,7 +141,7 @@ void b3SoundEngine::startSound(int soundSourceIndex, b3SoundMessage msg) b3ReadWavFile* wavFile = *wavFilePtr; soundSource->setWavFile(0,wavFile,getSampleRate()); soundSource->setWavFile(1,wavFile,getSampleRate()); - soundSource->startSound(); + soundSource->startSound(msg.m_autoKeyOff); } break; } @@ -142,6 +159,11 @@ void b3SoundEngine::releaseSound(int soundSourceIndex) int b3SoundEngine::loadWavFile(const char* fileName) { + int* wavUidPtr = m_data->m_name2wav[fileName]; + if (wavUidPtr) + { + return *wavUidPtr; + } char resourcePath[1024]; if (b3ResourcePath::findResourcePath(fileName, resourcePath, 1024)) @@ -153,6 +175,7 @@ int b3SoundEngine::loadWavFile(const char* fileName) wavFile->normalize(1); int wavUID = m_data->m_wavFileUidGenerator++; m_data->m_wavFiles.insert(wavUID, wavFile); + m_data->m_name2wav.insert(fileName,wavUID); return wavUID; } return 0; diff --git a/examples/TinyAudio/b3SoundEngine.h b/examples/TinyAudio/b3SoundEngine.h index 9fe08ec00..daf2df6b3 100644 --- a/examples/TinyAudio/b3SoundEngine.h +++ b/examples/TinyAudio/b3SoundEngine.h @@ -16,6 +16,7 @@ struct b3SoundMessage double m_decayRate; double m_sustainLevel; double m_releaseRate; + bool m_autoKeyOff; b3SoundMessage() :m_type(B3_SOUND_SOURCE_SINE_OSCILLATOR), @@ -25,7 +26,8 @@ struct b3SoundMessage m_attackRate(0.001), m_decayRate(0.00001), m_sustainLevel(0.5), - m_releaseRate(0.0005) + m_releaseRate(0.0005), + m_autoKeyOff(false) { } }; diff --git a/examples/TinyAudio/b3SoundSource.cpp b/examples/TinyAudio/b3SoundSource.cpp index d7a2b4913..860692894 100644 --- a/examples/TinyAudio/b3SoundSource.cpp +++ b/examples/TinyAudio/b3SoundSource.cpp @@ -183,7 +183,7 @@ bool b3SoundSource::isAvailable() const return m_data->m_envelope.isIdle(); } -void b3SoundSource::startSound() +void b3SoundSource::startSound(bool autoKeyOff) { if (m_data->m_envelope.isIdle()) { @@ -209,7 +209,7 @@ void b3SoundSource::startSound() } } } - m_data->m_envelope.keyOn(); + m_data->m_envelope.keyOn(autoKeyOff); } void b3SoundSource::stopSound() diff --git a/examples/TinyAudio/b3SoundSource.h b/examples/TinyAudio/b3SoundSource.h index a532eed37..af38a2183 100644 --- a/examples/TinyAudio/b3SoundSource.h +++ b/examples/TinyAudio/b3SoundSource.h @@ -24,7 +24,7 @@ public: bool setWavFile(int oscillatorIndex, class b3ReadWavFile* wavFilePtr, int sampleRate); - void startSound(); + void startSound(bool autoKeyOff); void stopSound(); bool isAvailable() const; diff --git a/examples/Utils/b3HashString.h b/examples/Utils/b3HashString.h new file mode 100644 index 000000000..9cae98c6d --- /dev/null +++ b/examples/Utils/b3HashString.h @@ -0,0 +1,59 @@ +#ifndef B3_HASH_STRING_H +#define B3_HASH_STRING_H + +#include + +///very basic hashable string implementation, compatible with b3HashMap +struct b3HashString +{ + std::string m_string; + unsigned int m_hash; + + B3_FORCE_INLINE unsigned int getHash()const + { + return m_hash; + } + + + b3HashString(const char* name) + :m_string(name) + { + + /* magic numbers from http://www.isthe.com/chongo/tech/comp/fnv/ */ + static const unsigned int InitialFNV = 2166136261u; + static const unsigned int FNVMultiple = 16777619u; + + /* Fowler / Noll / Vo (FNV) Hash */ + unsigned int hash = InitialFNV; + + for(int i = 0; m_string[i]; i++) + { + hash = hash ^ (m_string[i]); /* xor the low 8 bits */ + hash = hash * FNVMultiple; /* multiply by the magic number */ + } + m_hash = hash; + } + + int portableStringCompare(const char* src, const char* dst) const + { + int ret = 0 ; + + while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) + ++src, ++dst; + + if ( ret < 0 ) + ret = -1 ; + else if ( ret > 0 ) + ret = 1 ; + + return( ret ); + } + + bool equals(const b3HashString& other) const + { + return (m_string == other.m_string); + } + +}; + +#endif //B3_HASH_STRING_H diff --git a/src/Bullet3Common/b3HashMap.h b/src/Bullet3Common/b3HashMap.h index 3569503fb..15281ed90 100644 --- a/src/Bullet3Common/b3HashMap.h +++ b/src/Bullet3Common/b3HashMap.h @@ -19,58 +19,6 @@ subject to the following restrictions: #include "b3AlignedObjectArray.h" -///very basic hashable string implementation, compatible with b3HashMap -struct b3HashString -{ - const char* m_string; - unsigned int m_hash; - - B3_FORCE_INLINE unsigned int getHash()const - { - return m_hash; - } - - b3HashString(const char* name) - :m_string(name) - { - /* magic numbers from http://www.isthe.com/chongo/tech/comp/fnv/ */ - static const unsigned int InitialFNV = 2166136261u; - static const unsigned int FNVMultiple = 16777619u; - - /* Fowler / Noll / Vo (FNV) Hash */ - unsigned int hash = InitialFNV; - - for(int i = 0; m_string[i]; i++) - { - hash = hash ^ (m_string[i]); /* xor the low 8 bits */ - hash = hash * FNVMultiple; /* multiply by the magic number */ - } - m_hash = hash; - } - - int portableStringCompare(const char* src, const char* dst) const - { - int ret = 0 ; - - while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) - ++src, ++dst; - - if ( ret < 0 ) - ret = -1 ; - else if ( ret > 0 ) - ret = 1 ; - - return( ret ); - } - - bool equals(const b3HashString& other) const - { - return (m_string == other.m_string) || - (0==portableStringCompare(m_string,other.m_string)); - - } - -}; const int B3_HASH_NULL=0xffffffff; diff --git a/src/BulletCollision/CollisionDispatch/btCollisionObject.h b/src/BulletCollision/CollisionDispatch/btCollisionObject.h index 598ad2229..fec831bff 100644 --- a/src/BulletCollision/CollisionDispatch/btCollisionObject.h +++ b/src/BulletCollision/CollisionDispatch/btCollisionObject.h @@ -140,6 +140,7 @@ public: CF_HAS_CONTACT_STIFFNESS_DAMPING = 128, CF_HAS_CUSTOM_DEBUG_RENDERING_COLOR = 256, CF_HAS_FRICTION_ANCHOR = 512, + CF_HAS_COLLISION_SOUND_TRIGGER = 1024 }; enum CollisionObjectTypes diff --git a/src/LinearMath/btHashMap.h b/src/LinearMath/btHashMap.h index ca6f326b4..8f723a646 100644 --- a/src/LinearMath/btHashMap.h +++ b/src/LinearMath/btHashMap.h @@ -79,6 +79,11 @@ class btHashInt { int m_uid; public: + + btHashInt() + { + } + btHashInt(int uid) :m_uid(uid) { }