mix_context.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. // Copyright 2020 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "audio_core/behavior_info.h"
  5. #include "audio_core/common.h"
  6. #include "audio_core/effect_context.h"
  7. #include "audio_core/mix_context.h"
  8. #include "audio_core/splitter_context.h"
  9. namespace AudioCore {
  10. MixContext::MixContext() = default;
  11. MixContext::~MixContext() = default;
  12. void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count,
  13. std::size_t effect_count) {
  14. info_count = mix_count;
  15. infos.resize(info_count);
  16. auto& final_mix = GetInfo(AudioCommon::FINAL_MIX);
  17. final_mix.GetInParams().mix_id = AudioCommon::FINAL_MIX;
  18. sorted_info.reserve(infos.size());
  19. for (auto& info : infos) {
  20. sorted_info.push_back(&info);
  21. }
  22. for (auto& info : infos) {
  23. info.SetEffectCount(effect_count);
  24. }
  25. // Only initialize our edge matrix and node states if splitters are supported
  26. if (behavior_info.IsSplitterSupported()) {
  27. node_states.Initialize(mix_count);
  28. edge_matrix.Initialize(mix_count);
  29. }
  30. }
  31. void MixContext::UpdateDistancesFromFinalMix() {
  32. // Set all distances to be invalid
  33. for (std::size_t i = 0; i < info_count; i++) {
  34. GetInfo(i).GetInParams().final_mix_distance = AudioCommon::NO_FINAL_MIX;
  35. }
  36. for (std::size_t i = 0; i < info_count; i++) {
  37. auto& info = GetInfo(i);
  38. auto& in_params = info.GetInParams();
  39. // Populate our sorted info
  40. sorted_info[i] = &info;
  41. if (!in_params.in_use) {
  42. continue;
  43. }
  44. auto mix_id = in_params.mix_id;
  45. // Needs to be referenced out of scope
  46. s32 distance_to_final_mix{AudioCommon::FINAL_MIX};
  47. for (; distance_to_final_mix < static_cast<s32>(info_count); distance_to_final_mix++) {
  48. if (mix_id == AudioCommon::FINAL_MIX) {
  49. // If we're at the final mix, we're done
  50. break;
  51. } else if (mix_id == AudioCommon::NO_MIX) {
  52. // If we have no more mix ids, we're done
  53. distance_to_final_mix = AudioCommon::NO_FINAL_MIX;
  54. break;
  55. } else {
  56. const auto& dest_mix = GetInfo(mix_id);
  57. const auto dest_mix_distance = dest_mix.GetInParams().final_mix_distance;
  58. if (dest_mix_distance == AudioCommon::NO_FINAL_MIX) {
  59. // If our current mix isn't pointing to a final mix, follow through
  60. mix_id = dest_mix.GetInParams().dest_mix_id;
  61. } else {
  62. // Our current mix + 1 = final distance
  63. distance_to_final_mix = dest_mix_distance + 1;
  64. break;
  65. }
  66. }
  67. }
  68. // If we're out of range for our distance, mark it as no final mix
  69. if (distance_to_final_mix >= static_cast<s32>(info_count)) {
  70. distance_to_final_mix = AudioCommon::NO_FINAL_MIX;
  71. }
  72. in_params.final_mix_distance = distance_to_final_mix;
  73. }
  74. }
  75. void MixContext::CalcMixBufferOffset() {
  76. s32 offset{};
  77. for (std::size_t i = 0; i < info_count; i++) {
  78. auto& info = GetSortedInfo(i);
  79. auto& in_params = info.GetInParams();
  80. if (in_params.in_use) {
  81. // Only update if in use
  82. in_params.buffer_offset = offset;
  83. offset += in_params.buffer_count;
  84. }
  85. }
  86. }
  87. void MixContext::SortInfo() {
  88. // Get the distance to the final mix
  89. UpdateDistancesFromFinalMix();
  90. // Sort based on the distance to the final mix
  91. std::sort(sorted_info.begin(), sorted_info.end(),
  92. [](const ServerMixInfo* lhs, const ServerMixInfo* rhs) {
  93. return lhs->GetInParams().final_mix_distance >
  94. rhs->GetInParams().final_mix_distance;
  95. });
  96. // Calculate the mix buffer offset
  97. CalcMixBufferOffset();
  98. }
  99. bool MixContext::TsortInfo(SplitterContext& splitter_context) {
  100. // If we're not using mixes, just calculate the mix buffer offset
  101. if (!splitter_context.UsingSplitter()) {
  102. CalcMixBufferOffset();
  103. return true;
  104. }
  105. // Sort our node states
  106. if (!node_states.Tsort(edge_matrix)) {
  107. return false;
  108. }
  109. // Get our sorted list
  110. const auto sorted_list = node_states.GetIndexList();
  111. std::size_t info_id{};
  112. for (auto itr = sorted_list.rbegin(); itr != sorted_list.rend(); ++itr) {
  113. // Set our sorted info
  114. sorted_info[info_id++] = &GetInfo(*itr);
  115. }
  116. // Calculate the mix buffer offset
  117. CalcMixBufferOffset();
  118. return true;
  119. }
  120. std::size_t MixContext::GetCount() const {
  121. return info_count;
  122. }
  123. ServerMixInfo& MixContext::GetInfo(std::size_t i) {
  124. ASSERT(i < info_count);
  125. return infos.at(i);
  126. }
  127. const ServerMixInfo& MixContext::GetInfo(std::size_t i) const {
  128. ASSERT(i < info_count);
  129. return infos.at(i);
  130. }
  131. ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) {
  132. ASSERT(i < info_count);
  133. return *sorted_info.at(i);
  134. }
  135. const ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) const {
  136. ASSERT(i < info_count);
  137. return *sorted_info.at(i);
  138. }
  139. ServerMixInfo& MixContext::GetFinalMixInfo() {
  140. return infos.at(AudioCommon::FINAL_MIX);
  141. }
  142. const ServerMixInfo& MixContext::GetFinalMixInfo() const {
  143. return infos.at(AudioCommon::FINAL_MIX);
  144. }
  145. EdgeMatrix& MixContext::GetEdgeMatrix() {
  146. return edge_matrix;
  147. }
  148. const EdgeMatrix& MixContext::GetEdgeMatrix() const {
  149. return edge_matrix;
  150. }
  151. ServerMixInfo::ServerMixInfo() {
  152. Cleanup();
  153. }
  154. ServerMixInfo::~ServerMixInfo() = default;
  155. const ServerMixInfo::InParams& ServerMixInfo::GetInParams() const {
  156. return in_params;
  157. }
  158. ServerMixInfo::InParams& ServerMixInfo::GetInParams() {
  159. return in_params;
  160. }
  161. bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
  162. BehaviorInfo& behavior_info, SplitterContext& splitter_context,
  163. EffectContext& effect_context) {
  164. in_params.volume = mix_in.volume;
  165. in_params.sample_rate = mix_in.sample_rate;
  166. in_params.buffer_count = mix_in.buffer_count;
  167. in_params.in_use = mix_in.in_use;
  168. in_params.mix_id = mix_in.mix_id;
  169. in_params.node_id = mix_in.node_id;
  170. for (std::size_t i = 0; i < mix_in.mix_volume.size(); i++) {
  171. std::copy(mix_in.mix_volume[i].begin(), mix_in.mix_volume[i].end(),
  172. in_params.mix_volume[i].begin());
  173. }
  174. bool require_sort = false;
  175. if (behavior_info.IsSplitterSupported()) {
  176. require_sort = UpdateConnection(edge_matrix, mix_in, splitter_context);
  177. } else {
  178. in_params.dest_mix_id = mix_in.dest_mix_id;
  179. in_params.splitter_id = AudioCommon::NO_SPLITTER;
  180. }
  181. ResetEffectProcessingOrder();
  182. const auto effect_count = effect_context.GetCount();
  183. for (std::size_t i = 0; i < effect_count; i++) {
  184. auto* effect_info = effect_context.GetInfo(i);
  185. if (effect_info->GetMixID() == in_params.mix_id) {
  186. effect_processing_order[effect_info->GetProcessingOrder()] = static_cast<s32>(i);
  187. }
  188. }
  189. // TODO(ogniK): Update effect processing order
  190. return require_sort;
  191. }
  192. bool ServerMixInfo::HasAnyConnection() const {
  193. return in_params.splitter_id != AudioCommon::NO_SPLITTER ||
  194. in_params.mix_id != AudioCommon::NO_MIX;
  195. }
  196. void ServerMixInfo::Cleanup() {
  197. in_params.volume = 0.0f;
  198. in_params.sample_rate = 0;
  199. in_params.buffer_count = 0;
  200. in_params.in_use = false;
  201. in_params.mix_id = AudioCommon::NO_MIX;
  202. in_params.node_id = 0;
  203. in_params.buffer_offset = 0;
  204. in_params.dest_mix_id = AudioCommon::NO_MIX;
  205. in_params.splitter_id = AudioCommon::NO_SPLITTER;
  206. std::memset(in_params.mix_volume.data(), 0, sizeof(float) * in_params.mix_volume.size());
  207. }
  208. void ServerMixInfo::SetEffectCount(std::size_t count) {
  209. effect_processing_order.resize(count);
  210. ResetEffectProcessingOrder();
  211. }
  212. void ServerMixInfo::ResetEffectProcessingOrder() {
  213. for (auto& order : effect_processing_order) {
  214. order = AudioCommon::NO_EFFECT_ORDER;
  215. }
  216. }
  217. s32 ServerMixInfo::GetEffectOrder(std::size_t i) const {
  218. return effect_processing_order.at(i);
  219. }
  220. bool ServerMixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
  221. SplitterContext& splitter_context) {
  222. // Mixes are identical
  223. if (in_params.dest_mix_id == mix_in.dest_mix_id &&
  224. in_params.splitter_id == mix_in.splitter_id &&
  225. ((in_params.splitter_id == AudioCommon::NO_SPLITTER) ||
  226. !splitter_context.GetInfo(in_params.splitter_id).HasNewConnection())) {
  227. return false;
  228. }
  229. // Remove current edges for mix id
  230. edge_matrix.RemoveEdges(in_params.mix_id);
  231. if (mix_in.dest_mix_id != AudioCommon::NO_MIX) {
  232. // If we have a valid destination mix id, set our edge matrix
  233. edge_matrix.Connect(in_params.mix_id, mix_in.dest_mix_id);
  234. } else if (mix_in.splitter_id != AudioCommon::NO_SPLITTER) {
  235. // Recurse our splitter linked and set our edges
  236. auto& splitter_info = splitter_context.GetInfo(mix_in.splitter_id);
  237. const auto length = splitter_info.GetLength();
  238. for (s32 i = 0; i < length; i++) {
  239. const auto* splitter_destination =
  240. splitter_context.GetDestinationData(mix_in.splitter_id, i);
  241. if (splitter_destination == nullptr) {
  242. continue;
  243. }
  244. if (splitter_destination->ValidMixId()) {
  245. edge_matrix.Connect(in_params.mix_id, splitter_destination->GetMixId());
  246. }
  247. }
  248. }
  249. in_params.dest_mix_id = mix_in.dest_mix_id;
  250. in_params.splitter_id = mix_in.splitter_id;
  251. return true;
  252. }
  253. } // namespace AudioCore