/* * Copyright (C) 2012 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifndef _WIN32 #include #include #include #endif #include #ifdef HAVE_OPENAL #ifdef __APPLE__ # include # include # include #else # include # include # include #endif #endif #include "gazebo/common/CommonIface.hh" #include "gazebo/common/Console.hh" #include "gazebo/common/Exception.hh" #include "gazebo/common/AudioDecoder.hh" #include "gazebo/util/OpenALPrivate.hh" #include "gazebo/util/OpenAL.hh" using namespace gazebo; using namespace util; #ifdef HAVE_OPENAL ///////////////////////////////////////////////// OpenAL::OpenAL() : dataPtr(new OpenALPrivate) { this->dataPtr->context = NULL; this->dataPtr->audioDevice = NULL; } ///////////////////////////////////////////////// OpenAL::~OpenAL() { this->Fini(); } ///////////////////////////////////////////////// bool OpenAL::Load(sdf::ElementPtr _sdf) { std::string deviceName = "default"; if (_sdf && _sdf->HasElement("device")) deviceName = _sdf->Get("device"); // Open the default audio device if (deviceName == "default") this->dataPtr->audioDevice = alcOpenDevice(NULL); else { auto deviceList = this->DeviceList(); if (deviceList.empty() || deviceList.find(deviceName) == deviceList.end()) return false; this->dataPtr->audioDevice = alcOpenDevice(deviceName.c_str()); } // Make sure that we could open the audio device if (this->dataPtr->audioDevice == NULL) { gzerr << "Unable to open audio device[" << deviceName << "]\n Audio will be disabled.\n"; return false; } // Create the audio context this->dataPtr->context = alcCreateContext(this->dataPtr->audioDevice, NULL); if (this->dataPtr->context == NULL) { gzerr << "Unable to create OpenAL Context.\nAudio will be disabled.\n"; return false; } // Make the context current alcMakeContextCurrent(this->dataPtr->context); // Clear error code alGetError(); alDistanceModel(AL_EXPONENT_DISTANCE); return true; } ///////////////////////////////////////////////// void OpenAL::Fini() { if (this->dataPtr->audioDevice) { alcCloseDevice(this->dataPtr->audioDevice); this->dataPtr->audioDevice = NULL; } if (this->dataPtr->context) { this->dataPtr->context = alcGetCurrentContext(); alcMakeContextCurrent(NULL); alcDestroyContext(this->dataPtr->context); this->dataPtr->context = NULL; } this->dataPtr->sink.reset(); } ///////////////////////////////////////////////// OpenALSinkPtr OpenAL::CreateSink(sdf::ElementPtr /*_sdf*/) { OpenALSinkPtr result; if (this->dataPtr->sink == NULL) { this->dataPtr->sink.reset(new OpenALSink); result = this->dataPtr->sink; } else gzerr << "An OpenALSink has already been created." << "Only one is allowed.\n"; return result; } ///////////////////////////////////////////////// OpenALSourcePtr OpenAL::CreateSource(sdf::ElementPtr _sdf) { OpenALSourcePtr source; // Make sure the audio device has been opened if (!this->dataPtr->audioDevice) { gzerr << "Audio device not open\n"; return source; } // Create a source source.reset(new OpenALSource()); // Load the source if (!source->Load(_sdf)) { gzerr << "Unable to load OpenAL source from SDF\n"; source.reset(); } // Return a pointer to the source return source; } ///////////////////////////////////////////////// std::set OpenAL::DeviceList() const { std::set deviceList; const ALCchar *devices = alcGetString(NULL, ALC_DEVICE_SPECIFIER); while (*devices != '\0') { std::string str(devices); deviceList.emplace(str); devices += str.size() + 1; } return deviceList; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // OPENAL LISTENER //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////// OpenALSink::OpenALSink() { } ///////////////////////////////////////////////// OpenALSink::~OpenALSink() { } ///////////////////////////////////////////////// bool OpenALSink::SetPose(const ignition::math::Pose3d &_pose) { ALenum error; // Clear error state alGetError(); alListener3f(AL_POSITION, _pose.Pos().X(), _pose.Pos().Y(), _pose.Pos().Z()); if ((error = alGetError()) != AL_NO_ERROR) { gzerr << " Unable to set pose. Error code[" << error << "]\n"; return false; } ignition::math::Matrix3d rot(_pose.Rot()); // The first three values are the direction vector values. // The second three value are the up vector values. ALfloat orient[] = {static_cast(rot(0, 0)), static_cast(rot(0, 1)), static_cast(rot(0, 2)), static_cast(rot(2, 0)), static_cast(rot(2, 1)), static_cast(rot(2, 2))}; // Clear error state alGetError(); alListenerfv(AL_ORIENTATION, orient); if ((error = alGetError()) != AL_NO_ERROR) { gzerr << " Unable to set pose. Error code[" << error << "]\n"; return false; } return true; } ///////////////////////////////////////////////// bool OpenALSink::SetVelocity(const ignition::math::Vector3d &_vel) { ALenum error; // Clear error state alGetError(); alListener3f(AL_VELOCITY, _vel.X(), _vel.Y(), _vel.Z()); if ((error = alGetError()) != AL_NO_ERROR) { gzerr << " Unable to set velocity. Error code[" << error << "]\n"; return false; } return true; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // OPENAL SOURCE //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////// OpenALSource::OpenALSource() : dataPtr(new OpenALSourcePrivate) { // Create 1 source alGenSources(1, &this->dataPtr->alSource); // Create 1 buffer alGenBuffers(1, &this->dataPtr->alBuffer); } ///////////////////////////////////////////////// OpenALSource::~OpenALSource() { alDeleteSources(1, &this->dataPtr->alSource); alDeleteBuffers(1, &this->dataPtr->alBuffer); } ///////////////////////////////////////////////// bool OpenALSource::Load(sdf::ElementPtr _sdf) { if (_sdf && _sdf->HasElement("uri")) this->FillBufferFromFile(_sdf->Get("uri")); else { gzerr << " is missing ... element\n"; return false; } bool result = true; if (_sdf->HasElement("pitch")) result = result && this->SetPitch(_sdf->Get("pitch")); if (_sdf->HasElement("gain")) result = result && this->SetGain(_sdf->Get("gain")); if (_sdf->HasElement("loop")) result = result && this->SetLoop(_sdf->Get("loop")); if (_sdf->HasElement("contact")) { sdf::ElementPtr collisionElem = _sdf->GetElement("contact")->GetElement("collision"); while (collisionElem) { this->dataPtr->collisionNames.push_back( collisionElem->Get()); collisionElem = collisionElem->GetNextElement("collision"); } result = result && !this->dataPtr->collisionNames.empty(); } else this->Play(); return result; } ///////////////////////////////////////////////// bool OpenALSource::SetPose(const ignition::math::Pose3d &_pose) { ALfloat p[3] = {static_cast(_pose.Pos().X()), static_cast(_pose.Pos().Y()), static_cast(_pose.Pos().Z())}; ALenum error; // Clear error state alGetError(); alSourcefv(this->dataPtr->alSource, AL_POSITION, p); if ((error = alGetError()) != AL_NO_ERROR) { gzerr << " Unable to set position. Error code[" << error << "]\n"; return false; } return true; } ///////////////////////////////////////////////// bool OpenALSource::SetVelocity(const ignition::math::Vector3d &_vel) { ALenum error; ALfloat v[3] = {static_cast(_vel.X()), static_cast(_vel.Y()), static_cast(_vel.Z())}; // Clear error state alGetError(); alSourcefv(this->dataPtr->alSource, AL_VELOCITY, v); if ((error = alGetError()) != AL_NO_ERROR) { gzerr << " Unable to set velocity. Error code[" << error << "]\n"; return false; } return true; } ///////////////////////////////////////////////// bool OpenALSource::SetPitch(float _pitch) { ALenum error; // clear error state alGetError(); alSourcef(this->dataPtr->alSource, AL_PITCH, _pitch); if ((error = alGetError()) != AL_NO_ERROR) { gzerr << " Unable to set pitch. Error code[" << error << "]\n"; return false; } return true; } ///////////////////////////////////////////////// bool OpenALSource::SetGain(float _gain) { ALenum error; // clear error state alGetError(); alSourcef(this->dataPtr->alSource, AL_GAIN, _gain); if ((error = alGetError()) != AL_NO_ERROR) { gzerr << " Unable to set gain. Error code[" << error << "]\n"; return false; } return true; } ///////////////////////////////////////////////// bool OpenALSource::SetLoop(bool _state) { ALenum error; // clear error state alGetError(); // Set looping state alSourcei(this->dataPtr->alSource, AL_LOOPING, _state); if ((error = alGetError()) != AL_NO_ERROR) { gzerr << " Unable to set loop. Error code[" << error << "]\n"; return false; } return true; } ///////////////////////////////////////////////// std::vector OpenALSource::GetCollisionNames() const { return this->CollisionNames(); } ///////////////////////////////////////////////// std::vector OpenALSource::CollisionNames() const { return this->dataPtr->collisionNames; } ///////////////////////////////////////////////// bool OpenALSource::GetOnContact() const { return this->OnContact(); } ///////////////////////////////////////////////// bool OpenALSource::OnContact() const { return !this->dataPtr->collisionNames.empty(); } ///////////////////////////////////////////////// void OpenALSource::Play() { int sourceState; alGetSourcei(this->dataPtr->alSource, AL_SOURCE_STATE, &sourceState); // Play the source, if it's not already playing if (sourceState != AL_PLAYING) alSourcePlay(this->dataPtr->alSource); } ///////////////////////////////////////////////// void OpenALSource::Pause() { int sourceState; alGetSourcei(this->dataPtr->alSource, AL_SOURCE_STATE, &sourceState); // Pause the source if it playing if (sourceState == AL_PLAYING) alSourcePause(this->dataPtr->alSource); } ///////////////////////////////////////////////// void OpenALSource::Stop() { int sourceState; alGetSourcei(this->dataPtr->alSource, AL_SOURCE_STATE, &sourceState); // Stop the source if it is not already stopped if (sourceState != AL_STOPPED) alSourceStop(this->dataPtr->alSource); } ///////////////////////////////////////////////// void OpenALSource::Rewind() { alSourceRewind(this->dataPtr->alSource); } ///////////////////////////////////////////////// bool OpenALSource::IsPlaying() { int sourceState; alGetSourcei(this->dataPtr->alSource, AL_SOURCE_STATE, &sourceState); return sourceState == AL_PLAYING; } ///////////////////////////////////////////////// bool OpenALSource::FillBufferFromPCM(uint8_t *_pcmData, unsigned int _dataCount, int _sampleRate) { // First detach the buffer alSourcei(this->dataPtr->alSource, AL_BUFFER, 0); // Copy raw buffer into AL buffer // AL_FORMAT_MONO8, AL_FORMAT_MONO16, AL_FORMAT_STEREO8, // AL_FORMAT_STEREO16 alBufferData(this->dataPtr->alBuffer, AL_FORMAT_MONO16, _pcmData, _dataCount, _sampleRate); // Attach buffer to source alSourcei(this->dataPtr->alSource, AL_BUFFER, this->dataPtr->alBuffer); if (alGetError() != AL_NO_ERROR) { gzerr << "Unable to copy audio data into openAL buffer.\n"; return false; } return true; } ///////////////////////////////////////////////// void OpenALSource::FillBufferFromFile(const std::string &_audioFile) { std::string fullPathAudioFile = common::find_file(_audioFile); // Try to open the audio file in the current directory FILE *testFile = fopen(fullPathAudioFile.c_str(), "r"); // If the audio file couldn't be opened, try the gazebo paths if (testFile == NULL) { gzerr << "Unable to open audio file[" << _audioFile << "]\n"; } uint8_t *dataBuffer = NULL; #ifdef HAVE_FFMPEG unsigned int dataBufferSize; // Create an audio decoder common::AudioDecoder audioDecoder; // Set the audio file to decode audioDecoder.SetFile(fullPathAudioFile); audioDecoder.Decode(&dataBuffer, &dataBufferSize); // Copy raw buffer into AL buffer // AL_FORMAT_MONO8, AL_FORMAT_MONO16, AL_FORMAT_STEREO8, // AL_FORMAT_STEREO16 this->FillBufferFromPCM(dataBuffer, dataBufferSize, audioDecoder.GetSampleRate()); #else std::cerr << "No FFMPEG audio decoder. Missing FFMPEG libraries.\n"; #endif if (dataBuffer) delete [] dataBuffer; fclose(testFile); } ///////////////////////////////////////////////// bool OpenALSource::HasCollisionName(const std::string &_name) const { for (std::vector::const_iterator iter = this->dataPtr->collisionNames.begin(); iter != this->dataPtr->collisionNames.end(); ++iter) { if (*iter == _name) return true; } return false; } #endif