/** * @file audioengine.h * @brief Definition of LLAudioEngine base class abstracting the audio support * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LL_AUDIOENGINE_H #define LL_AUDIOENGINE_H #include #include #include #include "v3math.h" #include "v3dmath.h" #include "lltimer.h" #include "lluuid.h" #include "llframetimer.h" #include "llassettype.h" #include "llextendedstatus.h" #include "lllistener.h" const F32 LL_WIND_UPDATE_INTERVAL = 0.1f; const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f; const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f; const F32 DEFAULT_MIN_DISTANCE = 2.0f; #define LL_MAX_AUDIO_CHANNELS 30 #define LL_MAX_AUDIO_BUFFERS 40 // Some extra for preloading, maybe? class LLAudioSource; class LLAudioData; class LLAudioChannel; class LLAudioChannelOpenAL; class LLAudioBuffer; class LLStreamingAudioInterface; struct SoundData; // // LLAudioEngine definition // class LLAudioEngine { friend class LLAudioChannelOpenAL; // bleh. channel needs some listener methods. public: enum LLAudioType { AUDIO_TYPE_NONE = 0, AUDIO_TYPE_SFX = 1, AUDIO_TYPE_UI = 2, AUDIO_TYPE_AMBIENT = 3, AUDIO_TYPE_COUNT = 4 // last }; enum LLAudioPlayState { // isInternetStreamPlaying() returns an *S32*, with // 0 = stopped, 1 = playing, 2 = paused. AUDIO_STOPPED = 0, AUDIO_PLAYING = 1, AUDIO_PAUSED = 2 }; LLAudioEngine(); virtual ~LLAudioEngine(); // initialization/startup/shutdown virtual bool init(void *userdata, const std::string &app_title); virtual std::string getDriverName(bool verbose) = 0; virtual LLStreamingAudioInterface *createDefaultStreamingAudioImpl() const = 0; virtual void shutdown(); // Used by the mechanics of the engine //virtual void processQueue(const LLUUID &sound_guid); virtual void setListener(LLVector3 pos,LLVector3 vel,LLVector3 up,LLVector3 at); virtual void updateWind(LLVector3 direction, F32 camera_height_above_water) = 0; virtual void idle(); virtual void updateChannels(); // // "End user" functionality // virtual bool isWindEnabled(); virtual void enableWind(bool state_b); // Use these for temporarily muting the audio system. // Does not change buffers, initialization, etc. but // stops playing new sounds. void setMuted(bool muted); bool getMuted() const { return mMuted; } #ifdef USE_PLUGIN_MEDIA LLPluginClassMedia* initializeMedia(const std::string& media_type); #endif F32 getMasterGain(); void setMasterGain(F32 gain); F32 getSecondaryGain(S32 type); void setSecondaryGain(S32 type, F32 gain); F32 getInternetStreamGain(); virtual void setDopplerFactor(F32 factor); virtual F32 getDopplerFactor(); virtual void setRolloffFactor(F32 factor); virtual F32 getRolloffFactor(); virtual void setMaxWindGain(F32 gain); // Methods actually related to setting up and removing sounds // Owner ID is the owner of the object making the request void triggerSound(const LLUUID &sound_id, const LLUUID& owner_id, const F32 gain, const S32 type = LLAudioEngine::AUDIO_TYPE_NONE, const LLVector3d &pos_global = LLVector3d::zero); void triggerSound(SoundData& soundData); bool preloadSound(const LLUUID &id); void addAudioSource(LLAudioSource *asp); void cleanupAudioSource(LLAudioSource *asp); LLAudioSource *findAudioSource(const LLUUID &source_id); LLAudioData *getAudioData(const LLUUID &audio_uuid); // Internet stream implementation manipulation LLStreamingAudioInterface *getStreamingAudioImpl(); void setStreamingAudioImpl(LLStreamingAudioInterface *impl); // Internet stream methods - these will call down into the *mStreamingAudioImpl if it exists void startInternetStream(const std::string& url); void stopInternetStream(); void pauseInternetStream(S32 pause); void updateInternetStream(); // expected to be called often LLAudioPlayState isInternetStreamPlaying(); // use a value from 0.0 to 1.0, inclusive void setInternetStreamGain(F32 vol); std::string getInternetStreamURL(); // For debugging usage virtual LLVector3 getListenerPos(); LLAudioBuffer *getFreeBuffer(); // Get a free buffer, or flush an existing one if you have to. LLAudioChannel *getFreeChannel(const F32 priority); // Get a free channel or flush an existing one if your priority is higher void cleanupBuffer(LLAudioBuffer *bufferp); bool hasDecodedFile(const LLUUID &uuid); bool hasLocalFile(const LLUUID &uuid); bool updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid = LLUUID::null); // Asset callback when we're retrieved a sound from the asset server. void startNextTransfer(); static void assetCallback(const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code, LLExtStat ext_status); friend class LLPipeline; // For debugging public: F32 mMaxWindGain; // Hack. Public to set before fade in? protected: virtual LLAudioBuffer *createBuffer() = 0; virtual LLAudioChannel *createChannel() = 0; virtual bool initWind() = 0; virtual void cleanupWind() = 0; virtual void setInternalGain(F32 gain) = 0; void commitDeferredChanges(); virtual void allocateListener() = 0; // listener methods virtual void setListenerPos(LLVector3 vec); virtual void setListenerVelocity(LLVector3 vec); virtual void orientListener(LLVector3 up, LLVector3 at); virtual void translateListener(LLVector3 vec); F64 mapWindVecToGain(LLVector3 wind_vec); F64 mapWindVecToPitch(LLVector3 wind_vec); F64 mapWindVecToPan(LLVector3 wind_vec); protected: LLListener *mListenerp; bool mMuted; void* mUserData; S32 mLastStatus; bool mEnableWind; LLUUID mCurrentTransfer; // Audio file currently being transferred by the system LLFrameTimer mCurrentTransferTimer; // A list of all audio sources that are known to the viewer at this time. // This is most likely a superset of the ones that we actually have audio // data for, or are playing back. typedef std::map source_map; typedef std::map data_map; source_map mAllSources; data_map mAllData; std::array mChannels; // Buffers needs to change into a different data structure, as the number of buffers // that we have active should be limited by RAM usage, not count. std::array mBuffers; F32 mMasterGain; F32 mInternalGain; // Actual gain set; either mMasterGain or 0 when mMuted is true. F32 mSecondaryGain[AUDIO_TYPE_COUNT]; F32 mNextWindUpdate; LLFrameTimer mWindUpdateTimer; private: void setDefaults(); LLStreamingAudioInterface *mStreamingAudioImpl; }; // // Standard audio source. Can be derived from for special sources, such as those attached to objects. // class LLAudioSource { public: // owner_id is the id of the agent responsible for making this sound // play, for example, the owner of the object currently playing it LLAudioSource(const LLUUID &id, const LLUUID& owner_id, const F32 gain, const S32 type = LLAudioEngine::AUDIO_TYPE_NONE); virtual ~LLAudioSource(); virtual void update(); // Update this audio source void updatePriority(); void preload(const LLUUID &audio_id); // Only used for preloading UI sounds, now. void addAudioData(LLAudioData *adp, bool set_current = true); void setForcedPriority(const bool ambient) { mForcedPriority = ambient; } bool isForcedPriority() const { return mForcedPriority; } void setLoop(const bool loop) { mLoop = loop; } bool isLoop() const { return mLoop; } void setSyncMaster(const bool master) { mSyncMaster = master; } bool isSyncMaster() const { return mSyncMaster; } void setSyncSlave(const bool slave) { mSyncSlave = slave; } bool isSyncSlave() const { return mSyncSlave; } void setQueueSounds(const bool queue) { mQueueSounds = queue; } bool isQueueSounds() const { return mQueueSounds; } void setPlayedOnce(const bool played_once) { mPlayedOnce = played_once; } void setType(S32 type) { mType = type; } S32 getType() { return mType; } void setPositionGlobal(const LLVector3d &position_global) { mPositionGlobal = position_global; } LLVector3d getPositionGlobal() const { return mPositionGlobal; } LLVector3 getVelocity() const { return mVelocity; } F32 getPriority() const { return mPriority; } // Gain should always be clamped between 0 and 1. F32 getGain() const { return mGain; } virtual void setGain(const F32 gain) { mGain = llclamp(gain, 0.f, 1.f); } const LLUUID &getID() const { return mID; } bool isDone() const; bool isMuted() const { return mSourceMuted; } LLAudioData *getCurrentData(); LLAudioData *getQueuedData(); LLAudioBuffer *getCurrentBuffer(); bool setupChannel(); // Stop the audio source, reset audio id even if muted void stop(); // Start the audio source playing, // takes mute into account to preserve previous id if nessesary bool play(const LLUUID &audio_id); bool hasPendingPreloads() const; // Has preloads that haven't been done yet friend class LLAudioEngine; friend class LLAudioChannel; protected: void setChannel(LLAudioChannel *channelp); LLAudioChannel *getChannel() const { return mChannelp; } protected: LLUUID mID; // The ID of the source is that of the object if it's attached to an object. LLUUID mOwnerID; // owner of the object playing the sound F32 mPriority; F32 mGain; bool mSourceMuted; bool mForcedPriority; // ignore mute, set high priority, researved for sound preview and UI bool mLoop; bool mSyncMaster; bool mSyncSlave; bool mQueueSounds; bool mPlayedOnce; bool mCorrupted; S32 mType; LLVector3d mPositionGlobal; LLVector3 mVelocity; //LLAudioSource *mSyncMasterp; // If we're a slave, the source that we're synced to. LLAudioChannel *mChannelp; // If we're currently playing back, this is the channel that we're assigned to. LLAudioData *mCurrentDatap; LLAudioData *mQueuedDatap; typedef std::map data_map; data_map mPreloadMap; LLFrameTimer mAgeTimer; }; // // Generic metadata about a particular piece of audio data. // The actual data is handled by the derived LLAudioBuffer classes which are // derived for each audio engine. // class LLAudioData { public: LLAudioData(const LLUUID &uuid); bool load(); LLUUID getID() const { return mID; } LLAudioBuffer *getBuffer() const { return mBufferp; } bool hasLocalData() const { return mHasLocalData; } bool hasDecodedData() const { return mHasDecodedData; } bool hasCompletedDecode() const { return mHasCompletedDecode; } bool hasDecodeFailed() const { return mHasDecodeFailed; } bool hasWAVLoadFailed() const { return mHasWAVLoadFailed; } void setHasLocalData(const bool hld) { mHasLocalData = hld; } void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; } void setHasCompletedDecode(const bool hcd) { mHasCompletedDecode = hcd; } void setHasDecodeFailed(const bool hdf) { mHasDecodeFailed = hdf; } void setHasWAVLoadFailed(const bool hwlf) { mHasWAVLoadFailed = hwlf; } friend class LLAudioEngine; // Severe laziness, bad. protected: LLUUID mID; LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here. bool mHasLocalData; // Set true if the encoded sound asset file is available locally bool mHasDecodedData; // Set true if the decoded sound file is available on disk bool mHasCompletedDecode; // Set true when the sound is decoded bool mHasDecodeFailed; // Set true if decoding failed, meaning the sound asset is bad bool mHasWAVLoadFailed; // Set true if loading the decoded WAV file failed, meaning the sound asset should be decoded instead if // possible }; // // Base class for an audio channel, i.e. a channel which is capable of playing back a sound. // Management of channels is done generically, methods for actually manipulating the channel // are derived for each audio engine. // class LLAudioChannel { public: LLAudioChannel(); virtual ~LLAudioChannel(); virtual void setSource(LLAudioSource *sourcep); LLAudioSource *getSource() const { return mCurrentSourcep; } void setSecondaryGain(F32 gain) { mSecondaryGain = gain; } F32 getSecondaryGain() { return mSecondaryGain; } friend class LLAudioEngine; friend class LLAudioSource; protected: virtual void play() = 0; virtual void playSynced(LLAudioChannel *channelp) = 0; virtual void cleanup() = 0; virtual bool isPlaying() = 0; void setWaiting(const bool waiting) { mWaiting = waiting; } bool isWaiting() const { return mWaiting; } virtual bool updateBuffer(); // Check to see if the buffer associated with the source changed, and update if necessary. virtual void update3DPosition() = 0; virtual void updateLoop() = 0; // Update your loop/completion status, for use by queueing/syncing. protected: LLAudioSource *mCurrentSourcep; LLAudioBuffer *mCurrentBufferp; bool mLoopedThisFrame; bool mWaiting; // Waiting for sync. F32 mSecondaryGain; }; // Basically an interface class to the engine-specific implementation // of audio data that's ready for playback. // Will likely get more complex as we decide to do stuff like real streaming audio. class LLAudioBuffer { public: virtual ~LLAudioBuffer() {}; virtual bool loadWAV(const std::string& filename) = 0; virtual U32 getLength() = 0; friend class LLAudioEngine; friend class LLAudioChannel; friend class LLAudioData; protected: bool mInUse; LLAudioData *mAudioDatap; LLFrameTimer mLastUseTimer; }; struct SoundData { LLUUID audio_uuid; LLUUID owner_id; F32 gain; S32 type; LLVector3d pos_global; SoundData(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain, const S32 type = LLAudioEngine::AUDIO_TYPE_NONE, const LLVector3d &pos_global = LLVector3d::zero) : audio_uuid(audio_uuid), owner_id(owner_id), gain(gain), type(type), pos_global(pos_global) { } }; extern LLAudioEngine* gAudiop; #endif