void LLAudioEngine::idle(F32 max_decode_time) { if (max_decode_time <= 0.f) { max_decode_time = default_max_decode_time; } // "Update" all of our audio sources, clean up dead ones. // Primarily does position updating, cleanup of unused audio sources. // Also does regeneration of the current priority of each audio source. S32 i; for (i = 0; i < MAX_BUFFERS; i++) { if (mBuffers[i]) { mBuffers[i]->mInUse = false; } } F32 max_priority = -1.f; LLAudioSource *max_sourcep = NULL; // Maximum priority source without a channel source_map::iterator iter; for (iter = mAllSources.begin(); iter != mAllSources.end();) { LLAudioSource *sourcep = iter->second; // Update this source sourcep->update(); sourcep->updatePriority(); if (sourcep->isDone()) { // The source is done playing, clean it up. delete sourcep; mAllSources.erase(iter++); continue; } if (sourcep->isMuted()) { ++iter; continue; } if (!sourcep->getChannel() && sourcep->getCurrentBuffer()) { // We could potentially play this sound if its priority is high enough. if (sourcep->getPriority() > max_priority) { max_priority = sourcep->getPriority(); max_sourcep = sourcep; } } // Move on to the next source iter++; } // Now, do priority-based organization of audio sources. // All channels used, check priorities. // Find channel with lowest priority if (max_sourcep) { LLAudioChannel *channelp = getFreeChannel(max_priority); if (channelp) { //llinfos << "Replacing source in channel due to priority!" << llendl; max_sourcep->setChannel(channelp); channelp->setSource(max_sourcep); if (max_sourcep->isSyncSlave()) { // A sync slave, it doesn't start playing until it's synced up with the master. // Flag this channel as waiting for sync, and return true. channelp->setWaiting(true); } else { channelp->setWaiting(false); channelp->play(); } } } // Do this BEFORE we update the channels // Update the channels to sync up with any changes that the source made, // such as changing what sound was playing. updateChannels(); // Update queued sounds (switch to next queued data if the current has finished playing) for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter) { // This is lame, instead of this I could actually iterate through all the sources // attached to each channel, since only those with active channels // can have anything interesting happen with their queue? (Maybe not true) LLAudioSource *sourcep = iter->second; if (!sourcep->mQueuedDatap || sourcep->isMuted()) { // Muted, or nothing queued, so we don't care. continue; } LLAudioChannel *channelp = sourcep->getChannel(); if (!channelp) { // This sound isn't playing, so we just process move the queue sourcep->mCurrentDatap = sourcep->mQueuedDatap; sourcep->mQueuedDatap = NULL; // Reset the timer so the source doesn't die. sourcep->mAgeTimer.reset(); // Make sure we have the buffer set up if we just decoded the data if (sourcep->mCurrentDatap) { updateBufferForData(sourcep->mCurrentDatap); } // Actually play the associated data. sourcep->setupChannel(); channelp = sourcep->getChannel(); if (channelp) { channelp->updateBuffer(); sourcep->getChannel()->play(); } continue; } else { // Check to see if the current sound is done playing, or looped. if (!channelp->isPlaying()) { sourcep->mCurrentDatap = sourcep->mQueuedDatap; sourcep->mQueuedDatap = NULL; // Reset the timer so the source doesn't die. sourcep->mAgeTimer.reset(); // Make sure we have the buffer set up if we just decoded the data if (sourcep->mCurrentDatap) { updateBufferForData(sourcep->mCurrentDatap); } // Actually play the associated data. sourcep->setupChannel(); channelp->updateBuffer(); sourcep->getChannel()->play(); } else if (sourcep->isLoop()) { // It's a loop, we need to check and see if we're done with it. if (channelp->mLoopedThisFrame) { sourcep->mCurrentDatap = sourcep->mQueuedDatap; sourcep->mQueuedDatap = NULL; // Actually, should do a time sync so if we're a loop master/slave // we don't drift away. sourcep->setupChannel(); sourcep->getChannel()->play(); } } } } // Lame, update the channels AGAIN. // Update the channels to sync up with any changes that the source made, // such as changing what sound was playing. updateChannels(); // Hack! For now, just use a global sync master; LLAudioSource *sync_masterp = NULL; LLAudioChannel *master_channelp = NULL; F32 max_sm_priority = -1.f; for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter) { LLAudioSource *sourcep = iter->second; if (sourcep->isMuted()) { continue; } if (sourcep->isSyncMaster()) { if (sourcep->getPriority() > max_sm_priority) { sync_masterp = sourcep; master_channelp = sync_masterp->getChannel(); max_sm_priority = sourcep->getPriority(); } } } if (master_channelp && master_channelp->mLoopedThisFrame) { // Synchronize loop slaves with their masters // Update queued sounds (switch to next queued data if the current has finished playing) for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter) { LLAudioSource *sourcep = iter->second; if (!sourcep->isSyncSlave()) { // Not a loop slave, we don't need to do anything continue; } LLAudioChannel *channelp = sourcep->getChannel(); if (!channelp) { // Not playing, don't need to bother. continue; } if (!channelp->isPlaying()) { // Now we need to check if our loop master has just looped, and // start playback if that's the case. if (sync_masterp->getChannel()) { channelp->playSynced(master_channelp); channelp->setWaiting(false); } } } } // Sync up everything that the audio engine needs done. commitDeferredChanges(); // Flush unused buffers that are stale enough for (i = 0; i < MAX_BUFFERS; i++) { if (mBuffers[i]) { if (!mBuffers[i]->mInUse && mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > 30.f) { //llinfos << "Flushing unused buffer!" << llendl; mBuffers[i]->mAudioDatap->mBufferp = NULL; delete mBuffers[i]; mBuffers[i] = NULL; } } } // Clear all of the looped flags for the channels for (i = 0; i < MAX_CHANNELS; i++) { if (mChannels[i]) { mChannels[i]->mLoopedThisFrame = false; } } // Decode audio files gAudioDecodeMgrp->processQueue(max_decode_time); // Call this every frame, just in case we somehow // missed picking it up in all the places that can add // or request new data. startNextTransfer(); updateInternetStream(); }
void CSyncThread::transferCompleted(const SyncFileItem &item) { qDebug() << Q_FUNC_INFO << item._file << item._status << item._errorString; /* Update the _syncedItems vector */ // Search for the item in the starting from _iterator because it should be a bit before it. // This works because SyncFileItem::operator== only compare the file name; int idx = _syncedItems.lastIndexOf(item, _iterator); if (idx >= 0) { _syncedItems[idx]._instruction = item._instruction; _syncedItems[idx]._errorString = item._errorString; _syncedItems[idx]._status = item._status; } /* Remember deleted directory */ if (item._isDirectory && item._instruction == CSYNC_INSTRUCTION_DELETED) { _lastDeleted = item._file; } else { _lastDeleted.clear(); } /* Update the database */ if (item._instruction == CSYNC_INSTRUCTION_DELETED) { _journal->deleteFileRecord(item._originalFile); if (!item._renameTarget.isEmpty()) { SyncJournalFileRecord record(item, _localPath + item._renameTarget); record._path = item._renameTarget; _journal->setFileRecord(record); } } else if(item._instruction == CSYNC_INSTRUCTION_ERROR) { // Don't update parents directories _directoriesToUpdate.clear(); } else if (item._isDirectory) { // directory must not be saved to the db before we finished processing them. SyncJournalFileRecord record(item, _localPath + item._file); _directoriesToUpdate.push(record); } else if(item._instruction == CSYNC_INSTRUCTION_UPDATED) { SyncJournalFileRecord record(item, _localPath + item._file); _journal->setFileRecord(record); slotProgress((item._dir != SyncFileItem::Up) ? Progress::EndDownload : Progress::EndUpload, item._file, item._size, item._size); _progressInfo.current_file_no++; _progressInfo.overall_current_bytes += item._size; } /* Start the transfer of the next file or abort if there is an error */ if (item._status != SyncFileItem::FatalError) { startNextTransfer(); } else { emit treeWalkResult(_syncedItems); emit csyncError(item._errorString); emit finished(); csync_commit(_csync_ctx); _syncMutex.unlock(); thread()->quit(); } }
void LLAudioEngine::idle(F32 max_decode_time) { if (max_decode_time <= 0.f) { max_decode_time = default_max_decode_time; } // "Update" all of our audio sources, clean up dead ones. // Primarily does position updating, cleanup of unused audio sources. // Also does regeneration of the current priority of each audio source. S32 i; for (i = 0; i < MAX_BUFFERS; i++) { if (mBuffers[i]) { mBuffers[i]->mInUse = false; } } //Iterate down all queued sources. Remove finished ones, sort active ones by priority. Find highest priority 'master' source if present. LLAudioSource *sync_masterp = NULL; //Highest priority syncmaster LLAudioSource *sync_slavep = NULL; //Highest priority syncslave //Priority queue that will be filled with soundsources that have a high likeleyhood of being able to start [re]playing this idle tick. //Sort order: Primary: priority. Secondary: syncmaster. Tertiary: syncslave std::priority_queue<LLAudioSource*,std::vector<LLAudioSource*,boost::pool_allocator<LLAudioSource*> >,SourcePriorityComparator> queue; //Have to put syncslaves into a temporary list until the syncmaster state is determined. //If the syncmaster might be started, or just looped, insert all pending/looping syncslaves into the priority queue. //If the there is no active syncmaster, then stop any currently looping syncslaves and add none to the priority queue. //This is necessary as any looping soundsources to be stopped, MUST be stopped before iterating down the priority queue. static std::vector<LLAudioSource*> slave_list; slave_list.clear(); //Iterate over all sources. Update their decode or 'done' state, as well as their priorities. //Also add sources that might be able to start playing to the priority queue. //Only sources without channels should be added to priority queue. //Syncslaves must put into the slave list instead of the priority queue. for (source_map::iterator iter = mAllSources.begin(); iter != mAllSources.end();) { LLAudioSource *sourcep = iter->second; //Load/decode/queue pop sourcep->update(); //Done after update, as failure to load might mark source as corrupt, which causes isDone to return true. if (sourcep->isDone()) { // The source is done playing, clean it up. delete sourcep; mAllSources.erase(iter++); continue; } // Increment iter here (it is not used anymore), so we can use continue below to move on to the next source. ++iter; if(!sourcep->isLoop() && sourcep->mPlayedOnce && (!sourcep->mChannelp || !sourcep->mChannelp->isPlaying())) { continue; } LLAudioData *adp = sourcep->getCurrentData(); //If there is no current data at all, or if it hasn't loaded, we must skip this source. if (!adp || !adp->getBuffer()) { continue; } sourcep->updatePriority(); //Calculates current priority. 1.f=ambient. 0.f=muted. Otherwise based off of distance. if (sourcep->getPriority() < F_APPROXIMATELY_ZERO) { // Muted, or nothing queued, or too far, so we don't care. continue; } else if(sourcep->isSyncMaster()) { if(!sync_masterp || sourcep->getPriority() > sync_masterp->getPriority()) { if(sync_masterp && !sync_masterp->getChannel()) queue.push(sync_masterp); //Add lower-priority soundmaster to the queue as a normal sound. sync_masterp = sourcep; //Don't add master to the queue yet. //Add it after highest-priority sound slave is found so we can outrank its priority. continue; } //Else fall through like a normal sound. } else if(sourcep->isSyncSlave()) { if(!sync_slavep || sourcep->getPriority() > sync_slavep->getPriority()) { sync_slavep = sourcep; } //Don't add to the priority queue quite yet. Best primary syncmaster candidate may not have been determined yet. slave_list.push_back(sourcep); continue; } if(sourcep->getChannel()) { //If this is just a regular sound and is currently playing then do nothing special. continue; } //Add to our queue. Highest priority will be at the front. queue.push(sourcep); } // Do this BEFORE we update the channels // Update the channels to sync up with any changes that the source made, // such as changing what sound was playing. updateChannels(); //Loop over all syncsaves. If no syncmaster, stop looping ones, else add ones that need [re]started to the priority queue. //Normally there are zero to one syncslaves, so this loop isn't typically expensive. for(std::vector<LLAudioSource*>::iterator iter=slave_list.begin();iter!=slave_list.end();++iter) { if(!sync_masterp) { //Stop any looping syncslaves. I'm not sure this behavior is correct, but letting looping syncslaves run //off on their own seems wrong. The lsl documentation is unclear. if((*iter)->getChannel() && (*iter)->isLoop()) { (*iter)->getChannel()->cleanup(); } } //If the syncmaster already has a channel and hasn't looped, then we can skip syncslaves this idle tick (there's nothing to do). else if((!sync_masterp->getChannel() || sync_masterp->getChannel()->mLoopedThisFrame)) { //Add syncslaves that we might want to [re]start. if(!(*iter)->getChannel() || (*iter)->isLoop()) queue.push((*iter)); } } if(sync_masterp) { //Syncmaster must have priority greater than or equal to priority of highest priority syncslave. //The case of slave priority equaling master priority is handled in the comparator (SourcePriorityComparator). if(sync_slavep) sync_masterp->setPriority(sync_slavep->getPriority()); if(!sync_masterp->getChannel()) { queue.push(sync_masterp); } } // Dispatch all soundsources. bool syncmaster_started = sync_masterp && sync_masterp->getChannel() && sync_masterp->getChannel()->mLoopedThisFrame; while(!queue.empty()) { LLAudioSource *sourcep = queue.top(); queue.pop(); //If the syncmaster hasn't just started playing, or hasn't just looped then skip this soundslave, //as there's nothing to do with it yet. if (sourcep->isSyncSlave() && !syncmaster_started) { continue; } LLAudioChannel *channelp = sourcep->getChannel(); if (!channelp) { //No more channels. We can break here, as in theory, any lower priority sounds should have had their //channel already stolen. There should be nothing playing, nor playable, when iterating beyond this point. if(!(channelp = getFreeChannel(sourcep->getPriority()))) { break; } //If this is the primary syncmaster that we just started, then [re]start syncslaves further down in the priority queue. //Due to sorting and priority fudgery, the syncmaster is ALWAYS before any syncslaves in this loop. syncmaster_started |= (sourcep == sync_masterp); //setSource calls updateBuffer and update3DPosition, and resets the source mAgeTimer channelp->setSource(sourcep); } if(sourcep->isSyncSlave()) channelp->playSynced(sync_masterp->getChannel()); else channelp->play(); } // Sync up everything that the audio engine needs done. commitDeferredChanges(); // Flush unused buffers that are stale enough for (i = 0; i < MAX_BUFFERS; i++) { if (mBuffers[i]) { if (!mBuffers[i]->mInUse && mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > 30.f) { LL_DEBUGS("AudioEngine") << "Flushing unused buffer!" << LL_ENDL; mBuffers[i]->mAudioDatap->mBufferp = NULL; delete mBuffers[i]; mBuffers[i] = NULL; } } } // Clear all of the looped flags for the channels for (i = 0; i < MAX_CHANNELS; i++) { if (mChannels[i]) { mChannels[i]->mLoopedThisFrame = false; } } // Decode audio files gAudioDecodeMgrp->processQueue(max_decode_time); // Just call here every frame. It makes little sense to call elsewhere, // as it's throttled to one active preloading loading sound at a time anyhow startNextTransfer(); updateInternetStream(); }
void CSyncThread::startSync() { if (!_syncMutex.tryLock()) { qDebug() << Q_FUNC_INFO << "WARNING: Another sync seems to be running. Not starting a new one."; return; } if( ! _csync_ctx ) { qDebug() << "XXXXXXXXXXXXXXXX FAIL: do not have csync_ctx!"; } qDebug() << Q_FUNC_INFO << "Sync started"; qDebug() << "starting to sync " << qApp->thread() << QThread::currentThread(); _syncedItems.clear(); _mutex.lock(); _needsUpdate = false; if (!_abortRequested.fetchAndAddRelease(0)) { csync_resume(_csync_ctx); } _mutex.unlock(); // maybe move this somewhere else where it can influence a running sync? MirallConfigFile cfg; int fileRecordCount = 0; if (!_journal->exists()) { qDebug() << "=====sync looks new (no DB exists), activating recursive PROPFIND if csync supports it"; bool no_recursive_propfind = false; csync_set_module_property(_csync_ctx, "no_recursive_propfind", &no_recursive_propfind); } else if ((fileRecordCount = _journal->getFileRecordCount()) < 50) { qDebug() << "=====sync DB has only" << fileRecordCount << "items, enable recursive PROPFIND if csync supports it"; bool no_recursive_propfind = false; csync_set_module_property(_csync_ctx, "no_recursive_propfind", &no_recursive_propfind); } else { qDebug() << "=====sync with existing DB"; } csync_set_module_property(_csync_ctx, "csync_context", _csync_ctx); csync_set_userdata(_csync_ctx, this); // TODO: This should be a part of this method, but we don't have // any way to get "session_key" module property from csync. Had we // have it, then we could keep this code and remove it from // AbstractCredentials implementations. cfg.getCredentials()->syncContextPreStart(_csync_ctx); // if (_lastAuthCookies.length() > 0) { // // Stuff cookies inside csync, then we can avoid the intermediate HTTP 401 reply // // when https://github.com/owncloud/core/pull/4042 is merged. // QString cookiesAsString; // foreach(QNetworkCookie c, _lastAuthCookies) { // cookiesAsString += c.name(); // cookiesAsString += '='; // cookiesAsString += c.value(); // cookiesAsString += "; "; // } // csync_set_module_property(_csync_ctx, "session_key", cookiesAsString.to // } // csync_set_auth_callback( _csync_ctx, getauth ); csync_set_log_callback( csyncLogCatcher ); csync_set_log_level( 11 ); _syncTime.start(); QElapsedTimer updateTime; updateTime.start(); qDebug() << "#### Update start #################################################### >>"; if( csync_update(_csync_ctx) < 0 ) { handleSyncError(_csync_ctx, "csync_update"); return; } qDebug() << "<<#### Update end #################################################### " << updateTime.elapsed(); if( csync_reconcile(_csync_ctx) < 0 ) { handleSyncError(_csync_ctx, "csync_reconcile"); return; } _progressInfo = Progress::Info(); _hasFiles = false; bool walkOk = true; if( csync_walk_local_tree(_csync_ctx, &treewalkLocal, 0) < 0 ) { qDebug() << "Error in local treewalk."; walkOk = false; } if( walkOk && csync_walk_remote_tree(_csync_ctx, &treewalkRemote, 0) < 0 ) { qDebug() << "Error in remote treewalk."; } // Adjust the paths for the renames. for (SyncFileItemVector::iterator it = _syncedItems.begin(); it != _syncedItems.end(); ++it) { it->_file = adjustRenamedPath(it->_file); } qSort(_syncedItems); if (!_hasFiles && !_syncedItems.isEmpty()) { qDebug() << Q_FUNC_INFO << "All the files are going to be removed, asking the user"; bool cancel = false; emit aboutToRemoveAllFiles(_syncedItems.first()._dir, &cancel); if (cancel) { qDebug() << Q_FUNC_INFO << "Abort sync"; return; } } if (_needsUpdate) emit(started()); ne_session_s *session = 0; // that call to set property actually is a get which will return the session csync_set_module_property(_csync_ctx, "get_dav_session", &session); Q_ASSERT(session); _propagator.reset(new OwncloudPropagator (session, _localPath, _remotePath, _journal, &_abortRequested)); connect(_propagator.data(), SIGNAL(completed(SyncFileItem)), this, SLOT(transferCompleted(SyncFileItem)), Qt::QueuedConnection); connect(_propagator.data(), SIGNAL(progress(Progress::Kind,QString,quint64,quint64)), this, SLOT(slotProgress(Progress::Kind,QString,quint64,quint64))); _iterator = 0; int downloadLimit = 0; if (cfg.useDownloadLimit()) { downloadLimit = cfg.downloadLimit() * 1000; } _propagator->_downloadLimit = downloadLimit; int uploadLimit = -75; // 75% int useUpLimit = cfg.useUploadLimit(); if ( useUpLimit >= 1) { uploadLimit = cfg.uploadLimit() * 1000; } else if (useUpLimit == 0) { uploadLimit = 0; } _propagator->_uploadLimit = uploadLimit; slotProgress(Progress::StartSync, QString(), 0, 0); startNextTransfer(); }