//------------------------------------------------------------------------------ // bool gosFX::Effect::Execute(ExecuteInfo *info) { Check_Object(this); Check_Pointer(info); Verify(IsExecuted()); gos_PushCurrentHeap(Heap); // //----------------------------------------------------- // If a new seed is provided, override the current seed //----------------------------------------------------- // if (info->m_seed != -1.0f) { Verify(info->m_seed>=0.0f && info->m_seed<1.0f); m_seed = info->m_seed; } // //-------------------------------------------- // Figure out the new age and clear the bounds //-------------------------------------------- // Stuff::Scalar age = m_age + static_cast<Stuff::Scalar>(info->m_time - m_lastRan) * m_ageRate; Verify(age >= 0.0f && age >= m_age); *info->m_bounds = Stuff::OBB::Identity; // //-------------------------------- // Update the effectToWorld matrix //-------------------------------- // Check_Object(info->m_parentToWorld); m_localToWorld.Multiply(m_localToParent, *info->m_parentToWorld); // //-------------------------------------------------- // Check to see if the top event needs to be handled //-------------------------------------------------- // Check_Object(m_specification); Event *event; while ((event = m_event.GetCurrent()) != NULL) { Check_Object(event); if (event->m_time > m_age) break; // //------------------------------------------------------------- // This event needs to go, so spawn and bump the effect pointer //------------------------------------------------------------- // unsigned flags = ExecuteFlag; if ((event->m_flags&SimulationModeMask) == ParentSimulationMode) { Verify((m_flags&SimulationModeMask) != ParentSimulationMode); flags |= m_flags&SimulationModeMask; } else flags |= event->m_flags&SimulationModeMask; Effect* effect = EffectLibrary::Instance->MakeEffect( event->m_effectID, flags ); Register_Object(effect); m_children.Add(effect); m_event.Next(); // //--------------------------------------------- // Now set the info for starting the new effect //--------------------------------------------- // effect->m_localToParent = event->m_localToParent; Stuff::Scalar min_seed = m_specification->m_minimumChildSeed.ComputeValue(m_age, m_seed); Stuff::Scalar seed_range = m_specification->m_maximumChildSeed.ComputeValue(m_age, m_seed) - min_seed; Stuff::Scalar seed = Stuff::Random::GetFraction()*seed_range + min_seed; Clamp(seed, 0.0f, 1.0f); ExecuteInfo local_info( info->m_time, &m_localToWorld, NULL, seed ); effect->Start(&local_info); } // //------------------------------------------------------------ // Execute all the children. If any of them finish, kill them //------------------------------------------------------------ // Stuff::ChainIteratorOf<gosFX::Effect*> children(&m_children); gosFX::Effect *child; Stuff::OBB child_obb = Stuff::OBB::Identity; ExecuteInfo child_info( info->m_time, &m_localToWorld, &child_obb ); child_info.m_bounds = &child_obb; while ((child = children.ReadAndNext()) != NULL) { Check_Object(child); if (!child->Execute(&child_info)) { Unregister_Object(child); delete child; } // //-------------------------------------------------------------- // Merge the bounding sphere of the child into the bounds of the // parent //-------------------------------------------------------------- // Stuff::OBB parent_bounds; parent_bounds.Multiply(child_obb, m_localToParent); info->m_bounds->Union(*info->m_bounds, parent_bounds); } Check_Object(info->m_bounds); // //---------------------------------------------------------------------- // Set the new time, then if we have run the course of the effect, start // over if we loop, otherwise wait for our children to finish before // killing ourselves //---------------------------------------------------------------------- // m_lastRan = info->m_time; m_age = age; if (m_age >= 1.0f) { if (IsLooped()) Start(info); else if (HasFinished()) Kill(); } // //---------------------------------- // Tell our parent if we need to die //---------------------------------- // gos_PopCurrentHeap(); return IsExecuted(); }
void LibraryWatcher::ScanSubdirectory(const QString& path, const Subdirectory& subdir, ScanTransaction* t, bool force_noincremental) { QFileInfo path_info(path); QDir path_dir(path); // Do not scan symlinked dirs that are already in collection if (path_info.isSymLink()) { QString real_path = path_info.symLinkTarget(); for (const Directory& dir : watched_dirs_) { if (real_path.startsWith(dir.path)) { t->AddToProgress(1); return; } } } // Do not scan directories containing a .nomedia or .nomusic file if (path_dir.exists(kNoMediaFile) || path_dir.exists(kNoMusicFile)) { t->AddToProgress(1); return; } if (!t->ignores_mtime() && !force_noincremental && t->is_incremental() && subdir.mtime == path_info.lastModified().toTime_t()) { // The directory hasn't changed since last time t->AddToProgress(1); return; } QMap<QString, QStringList> album_art; QStringList files_on_disk; SubdirectoryList my_new_subdirs; // If a directory is moved then only its parent gets a changed notification, // so we need to look and see if any of our children don't exist any more. // If one has been removed, "rescan" it to get the deleted songs SubdirectoryList previous_subdirs = t->GetImmediateSubdirs(path); for (const Subdirectory& subdir : previous_subdirs) { if (!QFile::exists(subdir.path) && subdir.path != path) { t->AddToProgressMax(1); ScanSubdirectory(subdir.path, subdir, t, true); } } // First we "quickly" get a list of the files in the directory that we // think might be music. While we're here, we also look for new // subdirectories // and possible album artwork. QDirIterator it( path, QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot); while (it.hasNext()) { if (stop_requested_) return; QString child(it.next()); QFileInfo child_info(child); if (child_info.isDir()) { if (!child_info.isHidden() && !t->HasSeenSubdir(child)) { // We haven't seen this subdirectory before - add it to a list and // later we'll tell the backend about it and scan it. Subdirectory new_subdir; new_subdir.directory_id = -1; new_subdir.path = child; new_subdir.mtime = child_info.lastModified().toTime_t(); my_new_subdirs << new_subdir; } } else { QString ext_part(ExtensionPart(child)); QString dir_part(DirectoryPart(child)); if (sValidImages.contains(ext_part)) album_art[dir_part] << child; else if (!child_info.isHidden()) files_on_disk << child; } } if (stop_requested_) return; // Ask the database for a list of files in this directory SongList songs_in_db = t->FindSongsInSubdirectory(path); QSet<QString> cues_processed; // Now compare the list from the database with the list of files on disk for (const QString& file : files_on_disk) { if (stop_requested_) return; // associated cue QString matching_cue = NoExtensionPart(file) + ".cue"; Song matching_song; if (FindSongByPath(songs_in_db, file, &matching_song)) { uint matching_cue_mtime = GetMtimeForCue(matching_cue); // The song is in the database and still on disk. // Check the mtime to see if it's been changed since it was added. QFileInfo file_info(file); if (!file_info.exists()) { // Partially fixes race condition - if file was removed between being // added to the list and now. files_on_disk.removeAll(file); continue; } // cue sheet's path from library (if any) QString song_cue = matching_song.cue_path(); uint song_cue_mtime = GetMtimeForCue(song_cue); bool cue_deleted = song_cue_mtime == 0 && matching_song.has_cue(); bool cue_added = matching_cue_mtime != 0 && !matching_song.has_cue(); // watch out for cue songs which have their mtime equal to // qMax(media_file_mtime, cue_sheet_mtime) bool changed = (matching_song.mtime() != qMax(file_info.lastModified().toTime_t(), song_cue_mtime)) || cue_deleted || cue_added; // Also want to look to see whether the album art has changed QString image = ImageForSong(file, album_art); if ((matching_song.art_automatic().isEmpty() && !image.isEmpty()) || (!matching_song.art_automatic().isEmpty() && !matching_song.has_embedded_cover() && !QFile::exists(matching_song.art_automatic()))) { changed = true; } // the song's changed - reread the metadata from file if (t->ignores_mtime() || changed) { qLog(Debug) << file << "changed"; // if cue associated... if (!cue_deleted && (matching_song.has_cue() || cue_added)) { UpdateCueAssociatedSongs(file, path, matching_cue, image, t); // if no cue or it's about to lose it... } else { UpdateNonCueAssociatedSong(file, matching_song, image, cue_deleted, t); } } // nothing has changed - mark the song available without re-scanning if (matching_song.is_unavailable()) t->readded_songs << matching_song; } else { // The song is on disk but not in the DB SongList song_list = ScanNewFile(file, path, matching_cue, &cues_processed); if (song_list.isEmpty()) { continue; } qLog(Debug) << file << "created"; // choose an image for the song(s) QString image = ImageForSong(file, album_art); for (Song song : song_list) { song.set_directory_id(t->dir()); if (song.art_automatic().isEmpty()) song.set_art_automatic(image); t->new_songs << song; } } } // Look for deleted songs for (const Song& song : songs_in_db) { if (!song.is_unavailable() && !files_on_disk.contains(song.url().toLocalFile())) { qLog(Debug) << "Song deleted from disk:" << song.url().toLocalFile(); t->deleted_songs << song; } } // Add this subdir to the new or touched list Subdirectory updated_subdir; updated_subdir.directory_id = t->dir(); updated_subdir.mtime = path_info.exists() ? path_info.lastModified().toTime_t() : 0; updated_subdir.path = path; if (subdir.directory_id == -1) t->new_subdirs << updated_subdir; else t->touched_subdirs << updated_subdir; t->AddToProgress(1); // Recurse into the new subdirs that we found t->AddToProgressMax(my_new_subdirs.count()); for (const Subdirectory& my_new_subdir : my_new_subdirs) { if (stop_requested_) return; ScanSubdirectory(my_new_subdir.path, my_new_subdir, t, true); } }