Mercurial > foo_out_sdl
comparison foosdk/sdk/foobar2000/helpers/track_property_callback_impl.cpp @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #include "StdAfx.h" | |
| 2 | |
| 3 #include <list> | |
| 4 #include <memory> | |
| 5 | |
| 6 #include "track_property_callback_impl.h" | |
| 7 #include <SDK/threadPool.h> | |
| 8 #include <SDK/track_property.h> | |
| 9 | |
| 10 void track_property_callback_impl::set_property(const char * p_group, double p_sortpriority, const char * p_name, const char * p_value) { | |
| 11 propertyname_container temp; | |
| 12 temp.m_name = p_name; | |
| 13 temp.m_priority = p_sortpriority; | |
| 14 | |
| 15 pfc::string8 fixEOL; | |
| 16 if (m_cutMultiLine && strchr(p_value, '\n') != nullptr) { | |
| 17 fixEOL = p_value; fixEOL.fix_eol(); p_value = fixEOL; | |
| 18 } | |
| 19 | |
| 20 m_entries.find_or_add(p_group).set(temp, p_value); | |
| 21 } | |
| 22 | |
| 23 bool track_property_callback_impl::is_group_wanted(const char * p_group) { | |
| 24 if (m_groupFilter) return m_groupFilter(p_group); | |
| 25 return true; | |
| 26 } | |
| 27 | |
| 28 void track_property_callback_impl::merge(track_property_callback_impl const & other) { | |
| 29 for (auto iterGroup = other.m_entries.first(); iterGroup.is_valid(); ++iterGroup) { | |
| 30 auto & in = iterGroup->m_value; | |
| 31 auto & out = m_entries[iterGroup->m_key]; | |
| 32 for (auto iterEntry = in.first(); iterEntry.is_valid(); ++iterEntry) { | |
| 33 out.set(iterEntry->m_key, iterEntry->m_value); | |
| 34 } | |
| 35 } | |
| 36 } | |
| 37 | |
| 38 static bool is_filtered_info_field(const char * p_name) { | |
| 39 for (auto ptr : track_property_provider::enumerate()) { | |
| 40 if (ptr->is_our_tech_info(p_name)) return true; | |
| 41 } | |
| 42 return false; | |
| 43 } | |
| 44 | |
| 45 static const char strGroupOther[] = "Other"; | |
| 46 | |
| 47 static pfc::string8 encloseInfoName(const char* name) { | |
| 48 pfc::string8 temp; | |
| 49 temp << "<"; | |
| 50 uAddStringUpper(temp, name); | |
| 51 temp << ">"; | |
| 52 return temp; | |
| 53 } | |
| 54 | |
| 55 static void enumOtherHere(track_property_callback_impl & callback, metadb_info_container::ptr info_) { | |
| 56 if (info_.is_empty()) return; | |
| 57 const file_info * infoptr = &info_->info(); | |
| 58 for (t_size n = 0, m = infoptr->info_get_count(); n < m; n++) { | |
| 59 const char * name = infoptr->info_enum_name(n); | |
| 60 if (!is_filtered_info_field(name)) { | |
| 61 callback.set_property(strGroupOther, 0, encloseInfoName(name), infoptr->info_enum_value(n)); | |
| 62 } | |
| 63 } | |
| 64 } | |
| 65 | |
| 66 static trackInfoContainer::ptr getInfoHelper(track_property_provider_v3_info_source* source, size_t idx) { | |
| 67 return source->get_info(idx); | |
| 68 } | |
| 69 | |
| 70 static trackInfoContainer::ptr getInfoHelper(track_property_provider_v5_info_source* source, size_t idx) { | |
| 71 return source->get_info(idx).info; | |
| 72 } | |
| 73 | |
| 74 template<typename infoSource_t> | |
| 75 static void enumOther( track_property_callback_impl & callback, metadb_handle_list_cref items, infoSource_t* infoSource ) { | |
| 76 | |
| 77 const size_t itemCount = items.get_count(); | |
| 78 if (itemCount == 1 ) { | |
| 79 enumOtherHere(callback, getInfoHelper(infoSource,0) ); | |
| 80 return; | |
| 81 } | |
| 82 | |
| 83 typedef file_info::field_name_comparator field_name_comparator_t; | |
| 84 typedef pfc::comparator_stricmp_ascii value_comparator_t; | |
| 85 | |
| 86 typedef pfc::avltree_t< pfc::string8, field_name_comparator_t > field_mask_t; | |
| 87 | |
| 88 struct stats_t { | |
| 89 size_t count = 0; | |
| 90 double totalDuration = 0; | |
| 91 }; | |
| 92 typedef pfc::map_t<pfc::string8,stats_t,value_comparator_t > field_results_t; | |
| 93 typedef pfc::map_t<pfc::string8, field_results_t, field_name_comparator_t> results_t; | |
| 94 results_t results; | |
| 95 | |
| 96 field_mask_t fieldsIgnore, fieldsUse; | |
| 97 bool useDuration = true; | |
| 98 double totalDuration = 0; | |
| 99 | |
| 100 for (size_t itemWalk = 0; itemWalk < itemCount; ++itemWalk) { | |
| 101 auto info_ = getInfoHelper(infoSource, itemWalk); | |
| 102 if (info_.is_empty()) continue; | |
| 103 const file_info * infoptr = &info_->info(); | |
| 104 const size_t numInfo = infoptr->info_get_count(); | |
| 105 const double duration = infoptr->get_length(); | |
| 106 if (duration > 0) totalDuration += duration; | |
| 107 else useDuration = false; | |
| 108 for (size_t infoWalk = 0; infoWalk < numInfo; ++infoWalk) { | |
| 109 const char * name = infoptr->info_enum_name(infoWalk); | |
| 110 if ( fieldsIgnore.contains( name )) continue; | |
| 111 if (!fieldsUse.contains(name)) { | |
| 112 const bool bUse = !is_filtered_info_field( name ); | |
| 113 if ( bUse ) fieldsUse += name; | |
| 114 else { fieldsIgnore += name; continue; } | |
| 115 } | |
| 116 | |
| 117 const char * value = infoptr->info_enum_value(infoWalk); | |
| 118 auto & stats = results[name][value]; | |
| 119 ++ stats.count; | |
| 120 if ( duration > 0 ) { | |
| 121 stats.totalDuration += duration; | |
| 122 } | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 for (auto iter = results.first(); iter.is_valid(); ++iter) { | |
| 127 const auto key = encloseInfoName(iter->m_key); | |
| 128 pfc::string8 out; | |
| 129 if (iter->m_value.get_count() == 1 && iter->m_value.first()->m_value.count == itemCount) { | |
| 130 out = iter->m_value.first()->m_key; | |
| 131 } else { | |
| 132 for (auto iterValue = iter->m_value.first(); iterValue.is_valid(); ++iterValue) { | |
| 133 double percentage; | |
| 134 if ( useDuration ) percentage = iterValue->m_value.totalDuration / totalDuration; | |
| 135 else percentage = (double) iterValue->m_value.count / (double) itemCount; | |
| 136 if (!out.is_empty()) out << "; "; | |
| 137 out << iterValue->m_key << " (" << pfc::format_fixedpoint( pfc::rint64( percentage * 1000.0), 1) << "%)"; | |
| 138 } | |
| 139 } | |
| 140 callback.set_property(strGroupOther, 0, key, out); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 template<typename source_t> static void enumerateTrackProperties_(track_property_callback_impl& callback, std::function< metadb_handle_list_cref() > itemsSource, source_t infoSource, std::function<abort_callback& () > abortSource) { | |
| 145 if (core_api::is_main_thread()) { | |
| 146 // should not get here like this | |
| 147 // but that does make our job easier | |
| 148 auto& items = itemsSource(); | |
| 149 auto info = infoSource(); | |
| 150 for (auto ptr : track_property_provider::enumerate()) { | |
| 151 ptr->enumerate_properties_helper(items, info, callback, abortSource()); | |
| 152 } | |
| 153 if (callback.is_group_wanted(strGroupOther)) { | |
| 154 enumOther(callback, items, info); | |
| 155 } | |
| 156 return; | |
| 157 } | |
| 158 | |
| 159 std::list<std::shared_ptr<pfc::event> > lstWaitFor; | |
| 160 std::list<std::shared_ptr< track_property_callback_impl > > lstMerge; | |
| 161 for (auto ptr : track_property_provider::enumerate()) { | |
| 162 auto evt = std::make_shared<pfc::event>(); | |
| 163 auto cb = std::make_shared< track_property_callback_impl >(callback); // clone watched group info | |
| 164 auto work = [ptr, itemsSource, evt, cb, infoSource, abortSource] { | |
| 165 try { | |
| 166 ptr->enumerate_properties_helper(itemsSource(), infoSource(), *cb, abortSource()); | |
| 167 } catch (...) {} | |
| 168 evt->set_state(true); | |
| 169 }; | |
| 170 | |
| 171 track_property_provider_v4::ptr v4; | |
| 172 if (v4 &= ptr) { | |
| 173 // Supports v4 = split a worker thread, work in parallel | |
| 174 fb2k::inCpuWorkerThread(work); | |
| 175 } else { | |
| 176 // No v4 = delegate to main thread. Ugly but gets the job done. | |
| 177 fb2k::inMainThread(work); | |
| 178 } | |
| 179 | |
| 180 lstWaitFor.push_back(std::move(evt)); | |
| 181 lstMerge.push_back(std::move(cb)); | |
| 182 } | |
| 183 | |
| 184 if (callback.is_group_wanted(strGroupOther)) { | |
| 185 enumOther(callback, itemsSource(), infoSource()); | |
| 186 } | |
| 187 | |
| 188 for (auto& i : lstWaitFor) { | |
| 189 abortSource().waitForEvent(*i, -1); | |
| 190 } | |
| 191 for (auto& i : lstMerge) { | |
| 192 callback.merge(*i); | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 void enumerateTrackProperties(track_property_callback_impl& callback, std::function< metadb_handle_list_cref() > itemsSource, std::function<track_property_provider_v3_info_source* ()> infoSource, std::function<abort_callback& () > abortSource) { | |
| 197 enumerateTrackProperties_(callback, itemsSource, infoSource, abortSource); | |
| 198 } | |
| 199 | |
| 200 void enumerateTrackProperties_v5(track_property_callback_impl& callback, std::function< metadb_handle_list_cref() > itemsSource, std::function<track_property_provider_v5_info_source* ()> infoSource, std::function<abort_callback& () > abortSource) { | |
| 201 enumerateTrackProperties_(callback, itemsSource, infoSource, abortSource); | |
| 202 } |
