dsp.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // Copyright 2016 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <array>
  5. #include <memory>
  6. #include "audio_core/hle/dsp.h"
  7. #include "audio_core/hle/mixers.h"
  8. #include "audio_core/hle/pipe.h"
  9. #include "audio_core/hle/source.h"
  10. #include "audio_core/sink.h"
  11. #include "audio_core/time_stretch.h"
  12. namespace DSP {
  13. namespace HLE {
  14. // Region management
  15. std::array<SharedMemory, 2> g_regions;
  16. static size_t CurrentRegionIndex() {
  17. // The region with the higher frame counter is chosen unless there is wraparound.
  18. // This function only returns a 0 or 1.
  19. if (g_regions[0].frame_counter == 0xFFFFu && g_regions[1].frame_counter != 0xFFFEu) {
  20. // Wraparound has occured.
  21. return 1;
  22. }
  23. if (g_regions[1].frame_counter == 0xFFFFu && g_regions[0].frame_counter != 0xFFFEu) {
  24. // Wraparound has occured.
  25. return 0;
  26. }
  27. return (g_regions[0].frame_counter > g_regions[1].frame_counter) ? 0 : 1;
  28. }
  29. static SharedMemory& ReadRegion() {
  30. return g_regions[CurrentRegionIndex()];
  31. }
  32. static SharedMemory& WriteRegion() {
  33. return g_regions[1 - CurrentRegionIndex()];
  34. }
  35. // Audio processing and mixing
  36. static std::array<Source, num_sources> sources = {
  37. Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), Source(6), Source(7),
  38. Source(8), Source(9), Source(10), Source(11), Source(12), Source(13), Source(14), Source(15),
  39. Source(16), Source(17), Source(18), Source(19), Source(20), Source(21), Source(22), Source(23),
  40. };
  41. static Mixers mixers;
  42. static StereoFrame16 GenerateCurrentFrame() {
  43. SharedMemory& read = ReadRegion();
  44. SharedMemory& write = WriteRegion();
  45. std::array<QuadFrame32, 3> intermediate_mixes = {};
  46. // Generate intermediate mixes
  47. for (size_t i = 0; i < num_sources; i++) {
  48. write.source_statuses.status[i] =
  49. sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]);
  50. for (size_t mix = 0; mix < 3; mix++) {
  51. sources[i].MixInto(intermediate_mixes[mix], mix);
  52. }
  53. }
  54. // Generate final mix
  55. write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples,
  56. write.intermediate_mix_samples, intermediate_mixes);
  57. StereoFrame16 output_frame = mixers.GetOutput();
  58. // Write current output frame to the shared memory region
  59. for (size_t samplei = 0; samplei < output_frame.size(); samplei++) {
  60. for (size_t channeli = 0; channeli < output_frame[0].size(); channeli++) {
  61. write.final_samples.pcm16[samplei][channeli] = s16_le(output_frame[samplei][channeli]);
  62. }
  63. }
  64. return output_frame;
  65. }
  66. // Audio output
  67. static bool perform_time_stretching = true;
  68. static std::unique_ptr<AudioCore::Sink> sink;
  69. static AudioCore::TimeStretcher time_stretcher;
  70. static void FlushResidualStretcherAudio() {
  71. time_stretcher.Flush();
  72. while (true) {
  73. std::vector<s16> residual_audio = time_stretcher.Process(sink->SamplesInQueue());
  74. if (residual_audio.empty())
  75. break;
  76. sink->EnqueueSamples(residual_audio.data(), residual_audio.size() / 2);
  77. }
  78. }
  79. static void OutputCurrentFrame(const StereoFrame16& frame) {
  80. if (perform_time_stretching) {
  81. time_stretcher.AddSamples(&frame[0][0], frame.size());
  82. std::vector<s16> stretched_samples = time_stretcher.Process(sink->SamplesInQueue());
  83. sink->EnqueueSamples(stretched_samples.data(), stretched_samples.size() / 2);
  84. } else {
  85. constexpr size_t maximum_sample_latency = 2048; // about 64 miliseconds
  86. if (sink->SamplesInQueue() > maximum_sample_latency) {
  87. // This can occur if we're running too fast and samples are starting to back up.
  88. // Just drop the samples.
  89. return;
  90. }
  91. sink->EnqueueSamples(&frame[0][0], frame.size());
  92. }
  93. }
  94. void EnableStretching(bool enable) {
  95. if (perform_time_stretching == enable)
  96. return;
  97. if (!enable) {
  98. FlushResidualStretcherAudio();
  99. }
  100. perform_time_stretching = enable;
  101. }
  102. // Public Interface
  103. void Init() {
  104. DSP::HLE::ResetPipes();
  105. for (auto& source : sources) {
  106. source.Reset();
  107. }
  108. mixers.Reset();
  109. time_stretcher.Reset();
  110. if (sink) {
  111. time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate());
  112. }
  113. }
  114. void Shutdown() {
  115. if (perform_time_stretching) {
  116. FlushResidualStretcherAudio();
  117. }
  118. }
  119. bool Tick() {
  120. StereoFrame16 current_frame = {};
  121. // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to
  122. // shared memory region)
  123. current_frame = GenerateCurrentFrame();
  124. OutputCurrentFrame(current_frame);
  125. return true;
  126. }
  127. void SetSink(std::unique_ptr<AudioCore::Sink> sink_) {
  128. sink = std::move(sink_);
  129. time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate());
  130. }
  131. } // namespace HLE
  132. } // namespace DSP