audioengine.cpp

Go to the documentation of this file.
00001 
00033 #include "linden_common.h"
00034 
00035 #include "audioengine.h"
00036 
00037 #include "llerror.h"
00038 #include "llmath.h"
00039 
00040 #include "sound_ids.h"  // temporary hack for min/max distances
00041 
00042 #include "llvfs.h"
00043 #include "lldir.h"
00044 #include "llaudiodecodemgr.h"
00045 #include "llassetstorage.h"
00046 
00047 // necessary for grabbing sounds from sim (implemented in viewer)       
00048 extern void request_sound(const LLUUID &sound_guid);
00049 
00050 LLAudioEngine* gAudiop = NULL;
00051 
00052 // Maximum amount of time we wait for a transfer to complete before starting
00053 // off another one.
00054 const F32 MAX_CURRENT_TRANSFER_TIME = 60.f;
00055 
00056 //
00057 // LLAudioEngine implementation
00058 //
00059 
00060 
00061 LLAudioEngine::LLAudioEngine()
00062 {
00063 }
00064 
00065 
00066 LLAudioEngine::~LLAudioEngine()
00067 {
00068 }
00069 
00070 
00071 BOOL LLAudioEngine::init(const S32 num_channels, void* userdata)
00072 {
00073         mMuted = FALSE;
00074         mMasterGain = 1.f;
00075         mInternetStreamGain = 0.125f;
00076         mUserData = userdata;
00077         mLastStatus = 0;
00078 
00079         mNumChannels = num_channels;
00080         
00081 
00082         mMaxWindGain = 1.0;
00083 
00084         mEnableWind = FALSE;
00085 
00086         S32 i;
00087         for (i = 0; i < MAX_BUFFERS; i++)
00088         {
00089                 mBuffers[i] = NULL;
00090         }
00091         for (i = 0; i < num_channels; i++)
00092         {
00093                 mChannels[i] = NULL;
00094         }
00095         
00096         allocateListener();
00097 
00098         // Initialize the decode manager
00099         gAudioDecodeMgrp = new LLAudioDecodeMgr;
00100 
00101         return TRUE;
00102 }
00103 
00104 
00105 void LLAudioEngine::shutdown()
00106 {
00107         // Clean up decode manager
00108         delete gAudioDecodeMgrp;
00109         gAudioDecodeMgrp = NULL;
00110 
00111         // Clean up audio sources
00112         source_map::iterator iter_src;
00113         for (iter_src = mAllSources.begin(); iter_src != mAllSources.end(); iter_src++)
00114         {
00115                 delete iter_src->second;
00116         }
00117 
00118 
00119         // Clean up audio data
00120         data_map::iterator iter_data;
00121         for (iter_data = mAllData.begin(); iter_data != mAllData.end(); iter_data++)
00122         {
00123                 delete iter_data->second;
00124         }
00125 
00126 
00127         // Clean up channels
00128         S32 i;
00129         for (i = 0; i < MAX_CHANNELS; i++)
00130         {
00131                 if (mChannels[i])
00132                 {
00133                         delete mChannels[i];
00134                         mChannels[i] = NULL;
00135                 }
00136         }
00137 
00138         // Clean up buffers
00139         for (i = 0; i < MAX_BUFFERS; i++)
00140         {
00141                 if (mBuffers[i])
00142                 {
00143                         delete mBuffers[i];
00144                         mBuffers[i] = NULL;
00145                 }
00146         }
00147 }
00148 
00149 
00150 void LLAudioEngine::updateChannels()
00151 {
00152         S32 i;
00153         for (i = 0; i < MAX_CHANNELS; i++)
00154         {
00155                 if (mChannels[i])
00156                 {
00157                         mChannels[i]->updateBuffer();
00158                         mChannels[i]->update3DPosition();
00159                         mChannels[i]->updateLoop();
00160                 }
00161         }
00162 }
00163 
00164 static const F32 default_max_decode_time = .002f; // 2 ms
00165 void LLAudioEngine::idle(F32 max_decode_time)
00166 {
00167         if (max_decode_time <= 0.f)
00168         {
00169                 max_decode_time = default_max_decode_time;
00170         }
00171         
00172         // "Update" all of our audio sources, clean up dead ones.
00173         // Primarily does position updating, cleanup of unused audio sources.
00174         // Also does regeneration of the current priority of each audio source.
00175 
00176         if (getMuted())
00177         {
00178                 setInternalGain(0.f);
00179         }
00180         else
00181         {
00182                 setInternalGain(getMasterGain());
00183         }
00184 
00185         S32 i;
00186         for (i = 0; i < MAX_BUFFERS; i++)
00187         {
00188                 if (mBuffers[i])
00189                 {
00190                         mBuffers[i]->mInUse = FALSE;
00191                 }
00192         }
00193 
00194         F32 max_priority = -1.f;
00195         LLAudioSource *max_sourcep = NULL; // Maximum priority source without a channel
00196         source_map::iterator iter;
00197         for (iter = mAllSources.begin(); iter != mAllSources.end();)
00198         {
00199                 LLAudioSource *sourcep = iter->second;
00200 
00201                 // Update this source
00202                 sourcep->update();
00203                 sourcep->updatePriority();
00204 
00205                 if (sourcep->isDone())
00206                 {
00207                         // The source is done playing, clean it up.
00208                         delete sourcep;
00209                         mAllSources.erase(iter++);
00210                         continue;
00211                 }
00212 
00213                 if (!sourcep->getChannel() && sourcep->getCurrentBuffer())
00214                 {
00215                         // We could potentially play this sound if its priority is high enough.
00216                         if (sourcep->getPriority() > max_priority)
00217                         {
00218                                 max_priority = sourcep->getPriority();
00219                                 max_sourcep = sourcep;
00220                         }
00221                 }
00222 
00223                 // Move on to the next source
00224                 iter++;
00225         }
00226 
00227         // Now, do priority-based organization of audio sources.
00228         // All channels used, check priorities.
00229         // Find channel with lowest priority
00230         if (max_sourcep)
00231         {
00232                 LLAudioChannel *channelp = getFreeChannel(max_priority);
00233                 if (channelp)
00234                 {
00235                         //llinfos << "Replacing source in channel due to priority!" << llendl;
00236                         max_sourcep->setChannel(channelp);
00237                         channelp->setSource(max_sourcep);
00238                         if (max_sourcep->isSyncSlave())
00239                         {
00240                                 // A sync slave, it doesn't start playing until it's synced up with the master.
00241                                 // Flag this channel as waiting for sync, and return true.
00242                                 channelp->setWaiting(TRUE);
00243                         }
00244                         else
00245                         {
00246                                 channelp->setWaiting(FALSE);
00247                                 channelp->play();
00248                         }
00249                 }
00250         }
00251 
00252         
00253         // Do this BEFORE we update the channels
00254         // Update the channels to sync up with any changes that the source made,
00255         // such as changing what sound was playing.
00256         updateChannels();
00257 
00258         // Update queued sounds (switch to next queued data if the current has finished playing)
00259         for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter)
00260         {
00261                 // This is lame, instead of this I could actually iterate through all the sources
00262                 // attached to each channel, since only those with active channels
00263                 // can have anything interesting happen with their queue? (Maybe not true)
00264                 LLAudioSource *sourcep = iter->second;
00265                 if (!sourcep->mQueuedDatap)
00266                 {
00267                         // Nothing queued, so we don't care.
00268                         continue;
00269                 }
00270 
00271                 LLAudioChannel *channelp = sourcep->getChannel();
00272                 if (!channelp)
00273                 {
00274                         // This sound isn't playing, so we just process move the queue
00275                         sourcep->mCurrentDatap = sourcep->mQueuedDatap;
00276                         sourcep->mQueuedDatap = NULL;
00277 
00278                         // Reset the timer so the source doesn't die.
00279                         sourcep->mAgeTimer.reset();
00280                         // Make sure we have the buffer set up if we just decoded the data
00281                         if (sourcep->mCurrentDatap)
00282                         {
00283                                 updateBufferForData(sourcep->mCurrentDatap);
00284                         }
00285 
00286                         // Actually play the associated data.
00287                         sourcep->setupChannel();
00288                         channelp = sourcep->getChannel();
00289                         if (channelp)
00290                         {
00291                                 channelp->updateBuffer();
00292                                 sourcep->getChannel()->play();
00293                         }
00294                         continue;
00295                 }
00296                 else
00297                 {
00298                         // Check to see if the current sound is done playing, or looped.
00299                         if (!channelp->isPlaying())
00300                         {
00301                                 sourcep->mCurrentDatap = sourcep->mQueuedDatap;
00302                                 sourcep->mQueuedDatap = NULL;
00303 
00304                                 // Reset the timer so the source doesn't die.
00305                                 sourcep->mAgeTimer.reset();
00306 
00307                                 // Make sure we have the buffer set up if we just decoded the data
00308                                 if (sourcep->mCurrentDatap)
00309                                 {
00310                                         updateBufferForData(sourcep->mCurrentDatap);
00311                                 }
00312 
00313                                 // Actually play the associated data.
00314                                 sourcep->setupChannel();
00315                                 channelp->updateBuffer();
00316                                 sourcep->getChannel()->play();
00317                         }
00318                         else if (sourcep->isLoop())
00319                         {
00320                                 // It's a loop, we need to check and see if we're done with it.
00321                                 if (channelp->mLoopedThisFrame)
00322                                 {
00323                                         sourcep->mCurrentDatap = sourcep->mQueuedDatap;
00324                                         sourcep->mQueuedDatap = NULL;
00325 
00326                                         // Actually, should do a time sync so if we're a loop master/slave
00327                                         // we don't drift away.
00328                                         sourcep->setupChannel();
00329                                         sourcep->getChannel()->play();
00330                                 }
00331                         }
00332                 }
00333         }
00334 
00335         // Lame, update the channels AGAIN.
00336         // Update the channels to sync up with any changes that the source made,
00337         // such as changing what sound was playing.
00338         updateChannels();
00339         
00340         // Hack!  For now, just use a global sync master;
00341         LLAudioSource *sync_masterp = NULL;
00342         LLAudioChannel *master_channelp = NULL;
00343         F32 max_sm_priority = -1.f;
00344         for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter)
00345         {
00346                 LLAudioSource *sourcep = iter->second;
00347                 if (sourcep->isSyncMaster())
00348                 {
00349                         if (sourcep->getPriority() > max_sm_priority)
00350                         {
00351                                 sync_masterp = sourcep;
00352                                 master_channelp = sync_masterp->getChannel();
00353                                 max_sm_priority = sourcep->getPriority();
00354                         }
00355                 }
00356         }
00357 
00358         if (master_channelp && master_channelp->mLoopedThisFrame)
00359         {
00360                 // Synchronize loop slaves with their masters
00361                 // Update queued sounds (switch to next queued data if the current has finished playing)
00362                 for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter)
00363                 {
00364                         LLAudioSource *sourcep = iter->second;
00365 
00366                         if (!sourcep->isSyncSlave())
00367                         {
00368                                 // Not a loop slave, we don't need to do anything
00369                                 continue;
00370                         }
00371 
00372                         LLAudioChannel *channelp = sourcep->getChannel();
00373                         if (!channelp)
00374                         {
00375                                 // Not playing, don't need to bother.
00376                                 continue;
00377                         }
00378 
00379                         if (!channelp->isPlaying())
00380                         {
00381                                 // Now we need to check if our loop master has just looped, and
00382                                 // start playback if that's the case.
00383                                 if (sync_masterp->getChannel())
00384                                 {
00385                                         channelp->playSynced(master_channelp);
00386                                         channelp->setWaiting(FALSE);
00387                                 }
00388                         }
00389                 }
00390         }
00391 
00392         // Sync up everything that the audio engine needs done.
00393         commitDeferredChanges();
00394         
00395         // Flush unused buffers that are stale enough
00396         for (i = 0; i < MAX_BUFFERS; i++)
00397         {
00398                 if (mBuffers[i])
00399                 {
00400                         if (!mBuffers[i]->mInUse && mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > 30.f)
00401                         {
00402                                 //llinfos << "Flushing unused buffer!" << llendl;
00403                                 mBuffers[i]->mAudioDatap->mBufferp = NULL;
00404                                 delete mBuffers[i];
00405                                 mBuffers[i] = NULL;
00406                         }
00407                 }
00408         }
00409 
00410 
00411         // Clear all of the looped flags for the channels
00412         for (i = 0; i < MAX_CHANNELS; i++)
00413         {
00414                 if (mChannels[i])
00415                 {
00416                         mChannels[i]->mLoopedThisFrame = FALSE;
00417                 }
00418         }
00419 
00420         // Decode audio files
00421         gAudioDecodeMgrp->processQueue(max_decode_time);
00422         
00423         // Call this every frame, just in case we somehow
00424         // missed picking it up in all the places that can add
00425         // or request new data.
00426         startNextTransfer();
00427 }
00428 
00429 BOOL LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid)
00430 {
00431         if (!adp)
00432         {
00433                 return FALSE;
00434         }
00435 
00436         // Update the audio buffer first - load a sound if we have it.
00437         // Note that this could potentially cause us to waste time updating buffers
00438         // for sounds that actually aren't playing, although this should be mitigated
00439         // by the fact that we limit the number of buffers, and we flush buffers based
00440         // on priority.
00441         if (!adp->getBuffer())
00442         {
00443                 if (adp->hasDecodedData())
00444                 {
00445                         adp->load();
00446                 }
00447                 else if (adp->hasLocalData())
00448                 {
00449                         if (audio_uuid.notNull())
00450                         {
00451                                 gAudioDecodeMgrp->addDecodeRequest(audio_uuid);
00452                         }
00453                 }
00454                 else
00455                 {
00456                         return FALSE;
00457                 }
00458         }
00459         return TRUE;
00460 }
00461 
00462 
00463 void LLAudioEngine::enableWind(BOOL enable)
00464 {
00465         if (enable && (!mEnableWind))
00466         {
00467                 initWind();
00468                 mEnableWind = enable;
00469         }
00470         else if (mEnableWind && (!enable))
00471         {
00472                 mEnableWind = enable;
00473                 cleanupWind();
00474         }
00475 }
00476 
00477 
00478 LLAudioBuffer *LLAudioEngine::getFreeBuffer()
00479 {
00480         S32 i;
00481         for (i = 0; i < MAX_BUFFERS; i++)
00482         {
00483                 if (!mBuffers[i])
00484                 {
00485                         mBuffers[i] = createBuffer();
00486                         return mBuffers[i];
00487                 }
00488         }
00489 
00490 
00491         // Grab the oldest unused buffer
00492         F32 max_age = -1.f;
00493         S32 buffer_id = -1;
00494         for (i = 0; i < MAX_BUFFERS; i++)
00495         {
00496                 if (mBuffers[i])
00497                 {
00498                         if (!mBuffers[i]->mInUse)
00499                         {
00500                                 if (mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > max_age)
00501                                 {
00502                                         max_age = mBuffers[i]->mLastUseTimer.getElapsedTimeF32();
00503                                         buffer_id = i;
00504                                 }
00505                         }
00506                 }
00507         }
00508 
00509         if (buffer_id >= 0)
00510         {
00511                 llinfos << "Taking over unused buffer " << buffer_id << llendl;
00512                 //llinfos << "Flushing unused buffer!" << llendl;
00513                 mBuffers[buffer_id]->mAudioDatap->mBufferp = NULL;
00514                 delete mBuffers[buffer_id];
00515                 mBuffers[buffer_id] = createBuffer();
00516                 return mBuffers[buffer_id];
00517         }
00518         return NULL;
00519 }
00520 
00521 
00522 LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority)
00523 {
00524         S32 i;
00525         for (i = 0; i < mNumChannels; i++)
00526         {
00527                 if (!mChannels[i])
00528                 {
00529                         // No channel allocated here, use it.
00530                         mChannels[i] = createChannel();
00531                         return mChannels[i];
00532                 }
00533                 else
00534                 {
00535                         // Channel is allocated but not playing right now, use it.
00536                         if (!mChannels[i]->isPlaying() && !mChannels[i]->isWaiting())
00537                         {
00538                                 mChannels[i]->cleanup();
00539                                 if (mChannels[i]->getSource())
00540                                 {
00541                                         mChannels[i]->getSource()->setChannel(NULL);
00542                                 }
00543                                 return mChannels[i];
00544                         }
00545                 }
00546         }
00547 
00548         // All channels used, check priorities.
00549         // Find channel with lowest priority and see if we want to replace it.
00550         F32 min_priority = 10000.f;
00551         LLAudioChannel *min_channelp = NULL;
00552 
00553         for (i = 0; i < mNumChannels; i++)
00554         {
00555                 LLAudioChannel *channelp = mChannels[i];
00556                 LLAudioSource *sourcep = channelp->getSource();
00557                 if (sourcep->getPriority() < min_priority)
00558                 {
00559                         min_channelp = channelp;
00560                         min_priority = sourcep->getPriority();
00561                 }
00562         }
00563 
00564         if (min_priority > priority || !min_channelp)
00565         {
00566                 // All playing channels have higher priority, return.
00567                 return NULL;
00568         }
00569 
00570         // Flush the minimum priority channel, and return it.
00571         min_channelp->cleanup();
00572         min_channelp->getSource()->setChannel(NULL);
00573         return min_channelp;
00574 }
00575 
00576 
00577 void LLAudioEngine::cleanupBuffer(LLAudioBuffer *bufferp)
00578 {
00579         S32 i;
00580         for (i = 0; i < MAX_BUFFERS; i++)
00581         {
00582                 if (mBuffers[i] == bufferp)
00583                 {
00584                         delete mBuffers[i];
00585                         mBuffers[i] = NULL;
00586                 }
00587         }
00588 }
00589 
00590 
00591 BOOL LLAudioEngine::preloadSound(const LLUUID &uuid)
00592 {
00593         gAudiop->getAudioData(uuid);    // We don't care about the return value, this is just to make sure
00594                                                                         // that we have an entry, which will mean that the audio engine knows about this
00595 
00596         if (gAudioDecodeMgrp->addDecodeRequest(uuid))
00597         {
00598                 // This means that we do have a local copy, and we're working on decoding it.
00599                 return TRUE;
00600         }
00601 
00602         // At some point we need to have the audio/asset system check the static VFS
00603         // before it goes off and fetches stuff from the server.
00604         //llwarns << "Used internal preload for non-local sound" << llendl;
00605         return FALSE;
00606 }
00607 
00608 
00609 BOOL LLAudioEngine::isWindEnabled()
00610 {
00611         return mEnableWind;
00612 }
00613 
00614 
00615 void LLAudioEngine::setMuted(BOOL muted)
00616 {
00617         mMuted = muted;
00618         enableWind(!mMuted);
00619 }
00620 
00621 
00622 void LLAudioEngine::setMasterGain(const F32 gain)
00623 {
00624         mMasterGain = gain;
00625         setInternalGain(gain);
00626 }
00627 
00628 F32 LLAudioEngine::getMasterGain()
00629 {
00630         return mMasterGain;
00631 }
00632 
00633 F32 LLAudioEngine::getInternetStreamGain()
00634 {
00635         return mInternetStreamGain;
00636 }
00637 
00638 void LLAudioEngine::setMaxWindGain(F32 gain)
00639 {
00640         mMaxWindGain = gain;
00641 }
00642 
00643 
00644 F64 LLAudioEngine::mapWindVecToGain(LLVector3 wind_vec)
00645 {
00646         F64 gain = 0.0;
00647         
00648         gain = wind_vec.magVec();
00649 
00650         if (gain)
00651         {
00652                 if (gain > 20)
00653                 {
00654                         gain = 20;
00655                 }
00656                 gain = gain/20.0;
00657         }
00658 
00659         return (gain);
00660 } 
00661 
00662 
00663 F64 LLAudioEngine::mapWindVecToPitch(LLVector3 wind_vec)
00664 {
00665         LLVector3 listen_right;
00666         F64 theta;
00667   
00668         // Wind frame is in listener-relative coordinates
00669         LLVector3 norm_wind = wind_vec;
00670         norm_wind.normVec();
00671         listen_right.setVec(1.0,0.0,0.0);
00672 
00673         // measure angle between wind vec and listener right axis (on 0,PI)
00674         theta = acos(norm_wind * listen_right);
00675 
00676         // put it on 0, 1
00677         theta /= F_PI;                                  
00678 
00679         // put it on [0, 0.5, 0]
00680         if (theta > 0.5) theta = 1.0-theta;
00681         if (theta < 0) theta = 0;
00682 
00683         return (theta);
00684 }
00685 
00686 
00687 F64 LLAudioEngine::mapWindVecToPan(LLVector3 wind_vec)
00688 {
00689         LLVector3 listen_right;
00690         F64 theta;
00691   
00692         // Wind frame is in listener-relative coordinates
00693         listen_right.setVec(1.0,0.0,0.0);
00694 
00695         LLVector3 norm_wind = wind_vec;
00696         norm_wind.normVec();
00697 
00698         // measure angle between wind vec and listener right axis (on 0,PI)
00699         theta = acos(norm_wind * listen_right);
00700 
00701         // put it on 0, 1
00702         theta /= F_PI;                                  
00703 
00704         return (theta);
00705 }
00706 
00707 
00708 void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain, const LLVector3d &pos_global)
00709 {
00710         // Create a new source (since this can't be associated with an existing source.
00711         //llinfos << "Localized: " << audio_uuid << llendl;
00712 
00713         if (mMuted)
00714         {
00715                 return;
00716         }
00717 
00718         LLUUID source_id;
00719         source_id.generate();
00720 
00721         LLAudioSource *asp = new LLAudioSource(source_id, owner_id, gain);
00722         gAudiop->addAudioSource(asp);
00723         if (pos_global.isExactlyZero())
00724         {
00725                 asp->setAmbient(TRUE);
00726         }
00727         else
00728         {
00729                 asp->setPositionGlobal(pos_global);
00730         }
00731         asp->updatePriority();
00732         asp->play(audio_uuid);
00733 }
00734 
00735 
00736 void LLAudioEngine::setListenerPos(LLVector3 aVec)
00737 {
00738         mListenerp->setPosition(aVec);  
00739 }
00740 
00741 
00742 LLVector3 LLAudioEngine::getListenerPos()
00743 {
00744         if (mListenerp)
00745         {
00746                 return(mListenerp->getPosition());  
00747         }
00748         else
00749         {
00750                 return(LLVector3::zero);
00751         }
00752 }
00753 
00754 
00755 void LLAudioEngine::setListenerVelocity(LLVector3 aVec)
00756 {
00757         mListenerp->setVelocity(aVec);  
00758 }
00759 
00760 
00761 void LLAudioEngine::translateListener(LLVector3 aVec)
00762 {
00763         mListenerp->translate(aVec);    
00764 }
00765 
00766 
00767 void LLAudioEngine::orientListener(LLVector3 up, LLVector3 at)
00768 {
00769         mListenerp->orient(up, at);  
00770 }
00771 
00772 
00773 void LLAudioEngine::setListener(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at)
00774 {
00775         mListenerp->set(pos,vel,up,at);  
00776 }
00777 
00778 
00779 void LLAudioEngine::setDopplerFactor(F32 factor)
00780 {
00781         if (mListenerp)
00782         {
00783                 mListenerp->setDopplerFactor(factor);  
00784         }
00785 }
00786 
00787 
00788 F32 LLAudioEngine::getDopplerFactor()
00789 {
00790         if (mListenerp)
00791         {
00792                 return mListenerp->getDopplerFactor();
00793         }
00794         else
00795         {
00796                 return 0.f;
00797         }
00798 }
00799 
00800 
00801 void LLAudioEngine::setDistanceFactor(F32 factor)
00802 {
00803         if (mListenerp)
00804         {
00805                 mListenerp->setDistanceFactor(factor);  
00806         }
00807 }
00808 
00809 
00810 F32 LLAudioEngine::getDistanceFactor()
00811 {
00812         if (mListenerp)
00813         {
00814                 return mListenerp->getDistanceFactor();
00815         }
00816         else
00817         {
00818                 return 0.f;
00819         }
00820 }
00821 
00822 
00823 void LLAudioEngine::setRolloffFactor(F32 factor)
00824 {
00825         if (mListenerp)
00826         {
00827                 mListenerp->setRolloffFactor(factor);  
00828         }
00829 }
00830 
00831 
00832 F32 LLAudioEngine::getRolloffFactor()
00833 {
00834         if (mListenerp)
00835         {
00836                 return mListenerp->getRolloffFactor();  
00837         }
00838         else
00839         {
00840                 return 0.f;
00841         }
00842 }
00843 
00844 
00845 void LLAudioEngine::commitDeferredChanges()
00846 {
00847         mListenerp->commitDeferredChanges();  
00848 }
00849 
00850 
00851 LLAudioSource *LLAudioEngine::findAudioSource(const LLUUID &source_id)
00852 {
00853         source_map::iterator iter;
00854         iter = mAllSources.find(source_id);
00855 
00856         if (iter == mAllSources.end())
00857         {
00858                 return NULL;
00859         }
00860         else
00861         {
00862                 return iter->second;
00863         }
00864 }
00865 
00866 
00867 LLAudioData *LLAudioEngine::getAudioData(const LLUUID &audio_uuid)
00868 {
00869         data_map::iterator iter;
00870         iter = mAllData.find(audio_uuid);
00871         if (iter == mAllData.end())
00872         {
00873                 // Create the new audio data
00874                 LLAudioData *adp = new LLAudioData(audio_uuid);
00875                 mAllData[audio_uuid] = adp;
00876                 return adp;
00877         }
00878         else
00879         {
00880                 return iter->second;
00881         }
00882 }
00883 
00884 void LLAudioEngine::addAudioSource(LLAudioSource *asp)
00885 {
00886         mAllSources[asp->getID()] = asp;
00887 }
00888 
00889 
00890 void LLAudioEngine::cleanupAudioSource(LLAudioSource *asp)
00891 {
00892         source_map::iterator iter;
00893         iter = mAllSources.find(asp->getID());
00894         if (iter == mAllSources.end())
00895         {
00896                 llwarns << "Cleaning up unknown audio source!" << llendl;
00897                 return;
00898         }
00899         delete asp;
00900         mAllSources.erase(iter);
00901 }
00902 
00903 
00904 BOOL LLAudioEngine::hasDecodedFile(const LLUUID &uuid)
00905 {
00906         char uuid_str[UUID_STR_LENGTH];                 /*Flawfinder: ignore*/
00907         uuid.toString(uuid_str);
00908 
00909         std::string wav_path;
00910         wav_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str);
00911         wav_path += ".dsf";
00912 
00913         if (gDirUtilp->fileExists(wav_path))
00914         {
00915                 return TRUE;
00916         }
00917         else
00918         {
00919                 return FALSE;
00920         }
00921 }
00922 
00923 
00924 BOOL LLAudioEngine::hasLocalFile(const LLUUID &uuid)
00925 {
00926         // See if it's in the VFS.
00927         return gVFS->getExists(uuid, LLAssetType::AT_SOUND);
00928 }
00929 
00930 
00931 void LLAudioEngine::startNextTransfer()
00932 {
00933         //llinfos << "LLAudioEngine::startNextTransfer()" << llendl;
00934         if (mCurrentTransfer.notNull() || getMuted())
00935         {
00936                 //llinfos << "Transfer in progress, aborting" << llendl;
00937                 return;
00938         }
00939 
00940         // Get the ID for the next asset that we want to transfer.
00941         // Pick one in the following order:
00942         LLUUID asset_id;
00943         S32 i;
00944         LLAudioSource *asp = NULL;
00945         LLAudioData *adp = NULL;
00946         data_map::iterator data_iter;
00947 
00948         // Check all channels for currently playing sounds.
00949         F32 max_pri = -1.f;
00950         for (i = 0; i < MAX_CHANNELS; i++)
00951         {
00952                 if (!mChannels[i])
00953                 {
00954                         continue;
00955                 }
00956 
00957                 asp = mChannels[i]->getSource();
00958                 if (!asp)
00959                 {
00960                         continue;
00961                 }
00962                 if (asp->getPriority() <= max_pri)
00963                 {
00964                         continue;
00965                 }
00966 
00967                 if (asp->getPriority() <= max_pri)
00968                 {
00969                         continue;
00970                 }
00971 
00972                 adp = asp->getCurrentData();
00973                 if (!adp)
00974                 {
00975                         continue;
00976                 }
00977 
00978                 if (!adp->hasLocalData() && adp->hasValidData())
00979                 {
00980                         asset_id = adp->getID();
00981                         max_pri = asp->getPriority();
00982                 }
00983         }
00984 
00985         // Check all channels for currently queued sounds.
00986         if (asset_id.isNull())
00987         {
00988                 max_pri = -1.f;
00989                 for (i = 0; i < MAX_CHANNELS; i++)
00990                 {
00991                         if (!mChannels[i])
00992                         {
00993                                 continue;
00994                         }
00995 
00996                         LLAudioSource *asp;
00997                         asp = mChannels[i]->getSource();
00998                         if (!asp)
00999                         {
01000                                 continue;
01001                         }
01002 
01003                         if (asp->getPriority() <= max_pri)
01004                         {
01005                                 continue;
01006                         }
01007 
01008                         adp = asp->getQueuedData();
01009                         if (!adp)
01010                         {
01011                                 continue;
01012                         }
01013 
01014                         if (!adp->hasLocalData() && adp->hasValidData())
01015                         {
01016                                 asset_id = adp->getID();
01017                                 max_pri = asp->getPriority();
01018                         }
01019                 }
01020         }
01021 
01022         // Check all live channels for other sounds (preloads).
01023         if (asset_id.isNull())
01024         {
01025                 max_pri = -1.f;
01026                 for (i = 0; i < MAX_CHANNELS; i++)
01027                 {
01028                         if (!mChannels[i])
01029                         {
01030                                 continue;
01031                         }
01032 
01033                         LLAudioSource *asp;
01034                         asp = mChannels[i]->getSource();
01035                         if (!asp)
01036                         {
01037                                 continue;
01038                         }
01039 
01040                         if (asp->getPriority() <= max_pri)
01041                         {
01042                                 continue;
01043                         }
01044 
01045 
01046                         for (data_iter = asp->mPreloadMap.begin(); data_iter != asp->mPreloadMap.end(); data_iter++)
01047                         {
01048                                 LLAudioData *adp = data_iter->second;
01049                                 if (!adp)
01050                                 {
01051                                         continue;
01052                                 }
01053 
01054                                 if (!adp->hasLocalData() && adp->hasValidData())
01055                                 {
01056                                         asset_id = adp->getID();
01057                                         max_pri = asp->getPriority();
01058                                 }
01059                         }
01060                 }
01061         }
01062 
01063         // Check all sources
01064         if (asset_id.isNull())
01065         {
01066                 max_pri = -1.f;
01067                 source_map::iterator source_iter;
01068                 for (source_iter = mAllSources.begin(); source_iter != mAllSources.end(); source_iter++)
01069                 {
01070                         asp = source_iter->second;
01071                         if (!asp)
01072                         {
01073                                 continue;
01074                         }
01075 
01076                         if (asp->getPriority() <= max_pri)
01077                         {
01078                                 continue;
01079                         }
01080 
01081                         adp = asp->getCurrentData();
01082                         if (adp && !adp->hasLocalData() && adp->hasValidData())
01083                         {
01084                                 asset_id = adp->getID();
01085                                 max_pri = asp->getPriority();
01086                                 continue;
01087                         }
01088 
01089                         adp = asp->getQueuedData();
01090                         if (adp && !adp->hasLocalData() && adp->hasValidData())
01091                         {
01092                                 asset_id = adp->getID();
01093                                 max_pri = asp->getPriority();
01094                                 continue;
01095                         }
01096 
01097                         for (data_iter = asp->mPreloadMap.begin(); data_iter != asp->mPreloadMap.end(); data_iter++)
01098                         {
01099                                 LLAudioData *adp = data_iter->second;
01100                                 if (!adp)
01101                                 {
01102                                         continue;
01103                                 }
01104 
01105                                 if (!adp->hasLocalData() && adp->hasValidData())
01106                                 {
01107                                         asset_id = adp->getID();
01108                                         max_pri = asp->getPriority();
01109                                         break;
01110                                 }
01111                         }
01112                 }
01113         }
01114 
01115         if (asset_id.notNull())
01116         {
01117                 llinfos << "Getting asset data for: " << asset_id << llendl;
01118                 gAudiop->mCurrentTransfer = asset_id;
01119                 gAudiop->mCurrentTransferTimer.reset();
01120                 gAssetStorage->getAssetData(asset_id, LLAssetType::AT_SOUND,
01121                                                                         assetCallback, NULL);
01122         }
01123         else
01124         {
01125                 //llinfos << "No pending transfers?" << llendl;
01126         }
01127 }
01128 
01129 
01130 // static
01131 void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code, LLExtStat ext_status)
01132 {
01133         if (result_code)
01134         {
01135                 llinfos << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << llendl;
01136                 // Need to mark data as bad to avoid constant rerequests.
01137                 LLAudioData *adp = gAudiop->getAudioData(uuid);
01138                 if (adp)
01139         {
01140                         adp->setHasValidData(FALSE);
01141                         adp->setHasLocalData(FALSE);
01142                         adp->setHasDecodedData(FALSE);
01143                 }
01144         }
01145         else
01146         {
01147                 LLAudioData *adp = gAudiop->getAudioData(uuid);
01148                 if (!adp)
01149         {
01150                         // Should never happen
01151                         llwarns << "Got asset callback without audio data for " << uuid << llendl;
01152         }
01153                 else
01154                 {
01155                         adp->setHasValidData(TRUE);
01156                     adp->setHasLocalData(TRUE);
01157                     gAudioDecodeMgrp->addDecodeRequest(uuid);
01158                 }
01159         }
01160         gAudiop->mCurrentTransfer = LLUUID::null;
01161         gAudiop->startNextTransfer();
01162 }
01163 
01164 
01165 //
01166 // LLAudioSource implementation
01167 //
01168 
01169 
01170 LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32 gain)
01171 :       mID(id),
01172         mOwnerID(owner_id),
01173         mPriority(0.f),
01174         mGain(gain),
01175         mAmbient(FALSE),
01176         mLoop(FALSE),
01177         mSyncMaster(FALSE),
01178         mSyncSlave(FALSE),
01179         mQueueSounds(FALSE),
01180         mPlayedOnce(FALSE),
01181         mChannelp(NULL),
01182         mCurrentDatap(NULL),
01183         mQueuedDatap(NULL)
01184 {
01185 }
01186 
01187 
01188 LLAudioSource::~LLAudioSource()
01189 {
01190         if (mChannelp)
01191         {
01192                 // Stop playback of this sound
01193                 mChannelp->setSource(NULL);
01194                 mChannelp = NULL;
01195         }
01196 }
01197 
01198 
01199 void LLAudioSource::setChannel(LLAudioChannel *channelp)
01200 {
01201         if (channelp == mChannelp)
01202         {
01203                 return;
01204         }
01205 
01206         mChannelp = channelp;
01207 }
01208 
01209 
01210 void LLAudioSource::update()
01211 {
01212         if (!getCurrentBuffer())
01213         {
01214                 if (getCurrentData())
01215                 {
01216                         // Hack - try and load the sound.  Will do this as a callback
01217                         // on decode later.
01218                         if (getCurrentData()->load())
01219                         {
01220                                 play(getCurrentData()->getID());
01221                         }                       
01222                 }
01223         }
01224 }
01225 
01226 void LLAudioSource::updatePriority()
01227 {
01228         if (isAmbient())
01229         {
01230                 mPriority = 1.f;
01231         }
01232         else
01233         {
01234                 // Priority is based on distance
01235                 LLVector3 dist_vec;
01236                 dist_vec.setVec(getPositionGlobal());
01237                 dist_vec -= gAudiop->getListenerPos();
01238                 F32 dist_squared = llmax(1.f, dist_vec.magVecSquared());
01239 
01240                 mPriority = mGain / dist_squared;
01241         }
01242 }
01243 
01244 BOOL LLAudioSource::setupChannel()
01245 {
01246         LLAudioData *adp = getCurrentData();
01247 
01248         if (!adp->getBuffer())
01249         {
01250                 // We're not ready to play back the sound yet, so don't try and allocate a channel for it.
01251                 //llwarns << "Aborting, no buffer" << llendl;
01252                 return FALSE;
01253         }
01254 
01255 
01256         if (!mChannelp)
01257         {
01258                 // Update the priority, in case we need to push out another channel.
01259                 updatePriority();
01260 
01261                 setChannel(gAudiop->getFreeChannel(getPriority()));
01262         }
01263 
01264         if (!mChannelp)
01265         {
01266                 // Ugh, we don't have any free channels.
01267                 // Now we have to reprioritize.
01268                 // For now, just don't play the sound.
01269                 //llwarns << "Aborting, no free channels" << llendl;
01270                 return FALSE;
01271         }
01272 
01273         mChannelp->setSource(this);
01274         return TRUE;
01275 }
01276 
01277 
01278 BOOL LLAudioSource::play(const LLUUID &audio_uuid)
01279 {
01280         if (audio_uuid.isNull())
01281         {
01282                 if (getChannel())
01283                 {
01284                         getChannel()->setSource(NULL);
01285                         setChannel(NULL);
01286                         addAudioData(NULL, TRUE);
01287                 }
01288         }
01289         // Reset our age timeout if someone attempts to play the source.
01290         mAgeTimer.reset();
01291 
01292         LLAudioData *adp = gAudiop->getAudioData(audio_uuid);
01293 
01294         BOOL has_buffer = gAudiop->updateBufferForData(adp, audio_uuid);
01295 
01296 
01297         addAudioData(adp);
01298 
01299         if (!has_buffer)
01300         {
01301                 // Don't bother trying to set up a channel or anything, we don't have an audio buffer.
01302                 return FALSE;
01303         }
01304 
01305         if (!setupChannel())
01306         {
01307                 return FALSE;
01308         }
01309 
01310         if (isSyncSlave())
01311         {
01312                 // A sync slave, it doesn't start playing until it's synced up with the master.
01313                 // Flag this channel as waiting for sync, and return true.
01314                 getChannel()->setWaiting(TRUE);
01315                 return TRUE;
01316         }
01317 
01318         getChannel()->play();
01319         return TRUE;
01320 }
01321 
01322 
01323 BOOL LLAudioSource::isDone()
01324 {
01325         const F32 MAX_AGE = 60.f;
01326         const F32 MAX_UNPLAYED_AGE = 15.f;
01327         if (isLoop())
01328         {
01329                 // Looped sources never die on their own.
01330                 return FALSE;
01331         }
01332 
01333 
01334         if (hasPendingPreloads())
01335         {
01336                 return FALSE;
01337         }
01338 
01339         if (mQueuedDatap)
01340         {
01341                 // Don't kill this sound if we've got something queued up to play.
01342                 return FALSE;
01343         }
01344 
01345         F32 elapsed = mAgeTimer.getElapsedTimeF32();
01346 
01347         // This is a single-play source
01348         if (!mChannelp)
01349         {
01350                 if ((elapsed > MAX_UNPLAYED_AGE) || mPlayedOnce)
01351                 {
01352                         // We don't have a channel assigned, and it's been
01353                         // over 5 seconds since we tried to play it.  Don't bother.
01354                         //llinfos << "No channel assigned, source is done" << llendl;
01355                         return TRUE;
01356                 }
01357                 else
01358                 {
01359                         return FALSE;
01360                 }
01361         }
01362 
01363         if (mChannelp->isPlaying())
01364         {
01365                 if (elapsed > MAX_AGE)
01366                 {
01367                         // Arbitarily cut off non-looped sounds when they're 20 seconds old.
01368                         return TRUE;
01369                 }
01370                 else
01371                 {
01372                         // Sound is still playing and we haven't timed out, don't kill it.
01373                         return FALSE;
01374                 }
01375         }
01376 
01377         if ((elapsed > MAX_UNPLAYED_AGE) || mPlayedOnce)
01378         {
01379                 // The sound isn't playing back after 5 seconds or we're already done playing it, kill it.
01380                 return TRUE;
01381         }
01382 
01383         return FALSE;
01384 }
01385 
01386 
01387 void LLAudioSource::addAudioData(LLAudioData *adp, const BOOL set_current)
01388 {
01389         // Only handle a single piece of audio data associated with a source right now,
01390         // until I implement prefetch.
01391         if (set_current)
01392         {
01393                 if (!mCurrentDatap)
01394                 {
01395                         mCurrentDatap = adp;
01396                         if (mChannelp)
01397                         {
01398                                 mChannelp->updateBuffer();
01399                                 mChannelp->play();
01400                         }
01401 
01402                         // Make sure the audio engine knows that we want to request this sound.
01403                         gAudiop->startNextTransfer();
01404                         return;
01405                 }
01406                 else if (mQueueSounds)
01407                 {
01408                         // If we have current data, and we're queuing, put
01409                         // the object onto the queue.
01410                         if (mQueuedDatap)
01411                         {
01412                                 // We only queue one sound at a time, and it's a FIFO.
01413                                 // Don't put it onto the queue.
01414                                 return;
01415                         }
01416 
01417                         if (adp == mCurrentDatap && isLoop())
01418                         {
01419                                 // No point in queueing the same sound if
01420                                 // we're looping.
01421                                 return;
01422                         }
01423                         mQueuedDatap = adp;
01424 
01425                         // Make sure the audio engine knows that we want to request this sound.
01426                         gAudiop->startNextTransfer();
01427                 }
01428                 else
01429                 {
01430                         if (mCurrentDatap != adp)
01431                         {
01432                                 // Right now, if we're currently playing this sound in a channel, we
01433                                 // update the buffer that the channel's associated with
01434                                 // and play it.  This may not be the correct behavior.
01435                                 mCurrentDatap = adp;
01436                                 if (mChannelp)
01437                                 {
01438                                         mChannelp->updateBuffer();
01439                                         mChannelp->play();
01440                                 }
01441                                 // Make sure the audio engine knows that we want to request this sound.
01442                                 gAudiop->startNextTransfer();
01443                         }
01444                 }
01445         }
01446         else
01447         {
01448                 // Add it to the preload list.
01449                 mPreloadMap[adp->getID()] = adp;
01450                 gAudiop->startNextTransfer();
01451         }
01452 }
01453 
01454 
01455 BOOL LLAudioSource::hasPendingPreloads() const
01456 {
01457         // Check to see if we've got any preloads on deck for this source
01458         data_map::const_iterator iter;
01459         for (iter = mPreloadMap.begin(); iter != mPreloadMap.end(); iter++)
01460         {
01461                 LLAudioData *adp = iter->second;
01462                 if (!adp->hasDecodedData())
01463                 {
01464                         // This source is still waiting for a preload
01465                         return TRUE;
01466                 }
01467         }
01468 
01469         return FALSE;
01470 }
01471 
01472 
01473 LLAudioData *LLAudioSource::getCurrentData()
01474 {
01475         return mCurrentDatap;
01476 }
01477 
01478 LLAudioData *LLAudioSource::getQueuedData()
01479 {
01480         return mQueuedDatap;
01481 }
01482 
01483 LLAudioBuffer *LLAudioSource::getCurrentBuffer()
01484 {
01485         if (!mCurrentDatap)
01486         {
01487                 return NULL;
01488         }
01489 
01490         return mCurrentDatap->getBuffer();
01491 }
01492 
01493 
01494 
01495 
01496 //
01497 // LLAudioChannel implementation
01498 //
01499 
01500 
01501 LLAudioChannel::LLAudioChannel() :
01502         mCurrentSourcep(NULL),
01503         mCurrentBufferp(NULL),
01504         mLoopedThisFrame(FALSE),
01505         mWaiting(FALSE)
01506 {
01507 }
01508 
01509 
01510 LLAudioChannel::~LLAudioChannel()
01511 {
01512         // Need to disconnect any sources which are using this channel.
01513         //llinfos << "Cleaning up audio channel" << llendl;
01514         if (mCurrentSourcep)
01515         {
01516                 mCurrentSourcep->setChannel(NULL);
01517         }
01518         mCurrentBufferp = NULL;
01519 }
01520 
01521 
01522 void LLAudioChannel::setSource(LLAudioSource *sourcep)
01523 {
01524         //llinfos << this << ": setSource(" << sourcep << ")" << llendl;
01525 
01526         if (!sourcep)
01527         {
01528                 // Clearing the source for this channel, don't need to do anything.
01529                 //llinfos << "Clearing source for channel" << llendl;
01530                 cleanup();
01531                 mCurrentSourcep = NULL;
01532                 mWaiting = FALSE;
01533                 return;
01534         }
01535 
01536         if (sourcep == mCurrentSourcep)
01537         {
01538                 // Don't reallocate the channel, this will make FMOD goofy.
01539                 //llinfos << "Calling setSource with same source!" << llendl;
01540         }
01541 
01542         mCurrentSourcep = sourcep;
01543         updateBuffer();
01544         update3DPosition();
01545 }
01546 
01547 
01548 BOOL LLAudioChannel::updateBuffer()
01549 {
01550         if (!mCurrentSourcep)
01551         {
01552                 // This channel isn't associated with any source, nothing
01553                 // to be updated
01554                 return FALSE;
01555         }
01556 
01557         LLAudioBuffer *bufferp = mCurrentSourcep->getCurrentBuffer();
01558         if (bufferp == mCurrentBufferp)
01559         {
01560                 if (bufferp)
01561                 {
01562                         // The source hasn't changed what buffer it's playing
01563                         bufferp->mLastUseTimer.reset();
01564                         bufferp->mInUse = TRUE;
01565                 }
01566                 return FALSE;
01567         }
01568 
01569         //
01570         // The source changed what buffer it's playing.  Whe need to clean up the
01571         // existing fmod channel
01572         //
01573         cleanup();
01574 
01575         mCurrentBufferp = bufferp;
01576         if (bufferp)
01577         {
01578                 bufferp->mLastUseTimer.reset();
01579                 bufferp->mInUse = TRUE;
01580         }
01581 
01582         if (!mCurrentBufferp)
01583         {
01584                 // There's no new buffer to be played, so we just abort.
01585                 return FALSE;
01586         }
01587 
01588         return TRUE;
01589 }
01590 
01591 
01592 
01593 
01594 //
01595 // LLAudioData implementation
01596 //
01597 
01598 
01599 LLAudioData::LLAudioData(const LLUUID &uuid) :
01600         mID(uuid),
01601         mBufferp(NULL),
01602         mHasLocalData(FALSE),
01603         mHasDecodedData(FALSE),
01604         mHasValidData(TRUE)
01605 {
01606         if (uuid.isNull())
01607         {
01608                 // This is a null sound.
01609                 return;
01610         }
01611 
01612         if (gAudiop->hasDecodedFile(uuid))
01613         {
01614                 // Already have a decoded version, don't need to decode it.
01615                 mHasLocalData = TRUE;
01616                 mHasDecodedData = TRUE;
01617         }
01618         else if (gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
01619         {
01620                 mHasLocalData = TRUE;
01621         }
01622 }
01623 
01624 
01625 BOOL LLAudioData::load()
01626 {
01627         // For now, just assume we're going to use one buffer per audiodata.
01628         if (mBufferp)
01629         {
01630                 // We already have this sound in a buffer, don't do anything.
01631                 llinfos << "Already have a buffer for this sound, don't bother loading!" << llendl;
01632                 return TRUE;
01633         }
01634         
01635         mBufferp = gAudiop->getFreeBuffer();
01636         if (!mBufferp)
01637         {
01638                 // No free buffers, abort.
01639                 llinfos << "Not able to allocate a new audio buffer, aborting." << llendl;
01640                 return FALSE;
01641         }
01642 
01643         char uuid_str[UUID_STR_LENGTH];                 /*Flawfinder: ignore*/
01644         char wav_path[MAX_PATH];                                /*Flawfinder: ignore*/
01645         mID.toString(uuid_str);
01646         snprintf(wav_path, MAX_PATH, "%s.dsf",gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str());  /* Flawfinder: ignore */
01647 
01648         if (!mBufferp->loadWAV(wav_path))
01649         {
01650                 // Hrm.  Right now, let's unset the buffer, since it's empty.
01651                 gAudiop->cleanupBuffer(mBufferp);
01652                 mBufferp = NULL;
01653 
01654                 return FALSE;
01655         }
01656         mBufferp->mAudioDatap = this;
01657         return TRUE;
01658 }
01659 
01660 

Generated on Thu Jul 1 06:08:15 2010 for Second Life Viewer by  doxygen 1.4.7