diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/audio_core/audio_core.cpp | 4 | ||||
-rw-r--r-- | src/audio_core/audio_core.h | 3 | ||||
-rw-r--r-- | src/audio_core/hle/dsp.cpp | 45 | ||||
-rw-r--r-- | src/audio_core/hle/dsp.h | 8 | ||||
-rw-r--r-- | src/audio_core/null_sink.h | 2 | ||||
-rw-r--r-- | src/audio_core/sdl2_sink.cpp | 6 | ||||
-rw-r--r-- | src/audio_core/sdl2_sink.h | 2 | ||||
-rw-r--r-- | src/audio_core/sink.h | 5 | ||||
-rw-r--r-- | src/citra/config.cpp | 1 | ||||
-rw-r--r-- | src/citra/default_ini.h | 6 | ||||
-rw-r--r-- | src/citra_qt/config.cpp | 2 | ||||
-rw-r--r-- | src/citra_qt/configure_audio.cpp | 3 | ||||
-rw-r--r-- | src/citra_qt/configure_audio.ui | 10 | ||||
-rw-r--r-- | src/core/hle/service/apt/apt.cpp | 7 | ||||
-rw-r--r-- | src/core/hle/service/apt/bcfnt/bcfnt.cpp | 79 | ||||
-rw-r--r-- | src/core/hle/service/apt/bcfnt/bcfnt.h | 12 | ||||
-rw-r--r-- | src/core/settings.cpp | 1 | ||||
-rw-r--r-- | src/core/settings.h | 1 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 1 |
19 files changed, 152 insertions, 46 deletions
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index d42249ebd..8e19ec0c4 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp @@ -71,6 +71,10 @@ void SelectSink(std::string sink_id) { DSP::HLE::SetSink(iter->factory()); } +void EnableStretching(bool enable) { + DSP::HLE::EnableStretching(enable); +} + void Shutdown() { CoreTiming::UnscheduleEvent(tick_event, 0); DSP::HLE::Shutdown(); diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index f618361f3..7e678aba5 100644 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h @@ -23,6 +23,9 @@ void AddAddressSpace(Kernel::VMManager& vm_manager); /// Select the sink to use based on sink id. void SelectSink(std::string sink_id); +/// Enable/Disable stretching. +void EnableStretching(bool enable); + /// Shutdown Audio Core void Shutdown(); diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp index 0640e1eff..0cddeb82a 100644 --- a/src/audio_core/hle/dsp.cpp +++ b/src/audio_core/hle/dsp.cpp @@ -85,12 +85,45 @@ static StereoFrame16 GenerateCurrentFrame() { // Audio output +static bool perform_time_stretching = true; static std::unique_ptr<AudioCore::Sink> sink; static AudioCore::TimeStretcher time_stretcher; +static void FlushResidualStretcherAudio() { + time_stretcher.Flush(); + while (true) { + std::vector<s16> residual_audio = time_stretcher.Process(sink->SamplesInQueue()); + if (residual_audio.empty()) + break; + sink->EnqueueSamples(residual_audio.data(), residual_audio.size() / 2); + } +} + static void OutputCurrentFrame(const StereoFrame16& frame) { - time_stretcher.AddSamples(&frame[0][0], frame.size()); - sink->EnqueueSamples(time_stretcher.Process(sink->SamplesInQueue())); + if (perform_time_stretching) { + time_stretcher.AddSamples(&frame[0][0], frame.size()); + std::vector<s16> stretched_samples = time_stretcher.Process(sink->SamplesInQueue()); + sink->EnqueueSamples(stretched_samples.data(), stretched_samples.size() / 2); + } else { + constexpr size_t maximum_sample_latency = 1024; // about 32 miliseconds + if (sink->SamplesInQueue() > maximum_sample_latency) { + // This can occur if we're running too fast and samples are starting to back up. + // Just drop the samples. + return; + } + + sink->EnqueueSamples(&frame[0][0], frame.size()); + } +} + +void EnableStretching(bool enable) { + if (perform_time_stretching == enable) + return; + + if (!enable) { + FlushResidualStretcherAudio(); + } + perform_time_stretching = enable; } // Public Interface @@ -111,12 +144,8 @@ void Init() { } void Shutdown() { - time_stretcher.Flush(); - while (true) { - std::vector<s16> residual_audio = time_stretcher.Process(sink->SamplesInQueue()); - if (residual_audio.empty()) - break; - sink->EnqueueSamples(residual_audio); + if (perform_time_stretching) { + FlushResidualStretcherAudio(); } } diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h index 9275cd7de..565f20b6f 100644 --- a/src/audio_core/hle/dsp.h +++ b/src/audio_core/hle/dsp.h @@ -544,5 +544,13 @@ bool Tick(); */ void SetSink(std::unique_ptr<AudioCore::Sink> sink); +/** + * Enables/Disables audio-stretching. + * Audio stretching is an enhancement that stretches audio to match emulation + * speed to prevent stuttering at the cost of some audio latency. + * @param enable true to enable, false to disable. + */ +void EnableStretching(bool enable); + } // namespace HLE } // namespace DSP diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h index faf0ee4e1..9931c4778 100644 --- a/src/audio_core/null_sink.h +++ b/src/audio_core/null_sink.h @@ -19,7 +19,7 @@ public: return native_sample_rate; } - void EnqueueSamples(const std::vector<s16>&) override {} + void EnqueueSamples(const s16*, size_t) override {} size_t SamplesInQueue() const override { return 0; diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp index dc75c04ee..311dd5b59 100644 --- a/src/audio_core/sdl2_sink.cpp +++ b/src/audio_core/sdl2_sink.cpp @@ -71,14 +71,12 @@ unsigned int SDL2Sink::GetNativeSampleRate() const { return impl->sample_rate; } -void SDL2Sink::EnqueueSamples(const std::vector<s16>& samples) { +void SDL2Sink::EnqueueSamples(const s16* samples, size_t sample_count) { if (impl->audio_device_id <= 0) return; - ASSERT_MSG(samples.size() % 2 == 0, "Samples must be in interleaved stereo PCM16 format (size must be a multiple of two)"); - SDL_LockAudioDevice(impl->audio_device_id); - impl->queue.emplace_back(samples); + impl->queue.emplace_back(samples, samples + sample_count * 2); SDL_UnlockAudioDevice(impl->audio_device_id); } diff --git a/src/audio_core/sdl2_sink.h b/src/audio_core/sdl2_sink.h index 0f296b673..b13827214 100644 --- a/src/audio_core/sdl2_sink.h +++ b/src/audio_core/sdl2_sink.h @@ -18,7 +18,7 @@ public: unsigned int GetNativeSampleRate() const override; - void EnqueueSamples(const std::vector<s16>& samples) override; + void EnqueueSamples(const s16* samples, size_t sample_count) override; size_t SamplesInQueue() const override; diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h index 1c881c3d2..a06fc3dcc 100644 --- a/src/audio_core/sink.h +++ b/src/audio_core/sink.h @@ -23,9 +23,10 @@ public: /** * Feed stereo samples to sink. - * @param samples Samples in interleaved stereo PCM16 format. Size of vector must be multiple of two. + * @param samples Samples in interleaved stereo PCM16 format. + * @param sample_count Number of samples. */ - virtual void EnqueueSamples(const std::vector<s16>& samples) = 0; + virtual void EnqueueSamples(const s16* samples, size_t sample_count) = 0; /// Samples enqueued that have not been played yet. virtual std::size_t SamplesInQueue() const = 0; diff --git a/src/citra/config.cpp b/src/citra/config.cpp index d71045646..110b883fb 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -79,6 +79,7 @@ void Config::ReadValues() { // Audio Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); + Settings::values.enable_audio_stretching = sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); // Data Storage Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index b10700e62..2031620a5 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -70,6 +70,12 @@ bg_green = # auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available) output_engine = +# Whether or not to enable the audio-stretching post-processing effect. +# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter, +# at the cost of increasing audio latency. +# 0: No, 1 (default): Yes +enable_audio_stretching = + [Data Storage] # Whether to create a virtual SD card. # 1 (default): Yes, 0: No diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index ff7af445e..fa3fa210c 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -57,6 +57,7 @@ void Config::ReadValues() { qt_config->beginGroup("Audio"); Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); + Settings::values.enable_audio_stretching = qt_config->value("enable_audio_stretching", true).toBool(); qt_config->endGroup(); qt_config->beginGroup("Data Storage"); @@ -150,6 +151,7 @@ void Config::SaveValues() { qt_config->beginGroup("Audio"); qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); + qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching); qt_config->endGroup(); qt_config->beginGroup("Data Storage"); diff --git a/src/citra_qt/configure_audio.cpp b/src/citra_qt/configure_audio.cpp index cedfa2f2a..7100be158 100644 --- a/src/citra_qt/configure_audio.cpp +++ b/src/citra_qt/configure_audio.cpp @@ -36,9 +36,12 @@ void ConfigureAudio::setConfiguration() { } } ui->output_sink_combo_box->setCurrentIndex(new_sink_index); + + ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); } void ConfigureAudio::applyConfiguration() { Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()).toStdString(); + Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked(); Settings::Apply(); } diff --git a/src/citra_qt/configure_audio.ui b/src/citra_qt/configure_audio.ui index d7f6946ca..3e2b4635f 100644 --- a/src/citra_qt/configure_audio.ui +++ b/src/citra_qt/configure_audio.ui @@ -25,6 +25,16 @@ </item> </layout> </item> + <item> + <widget class="QCheckBox" name="toggle_audio_stretching"> + <property name="text"> + <string>Enable audio stretching</string> + </property> + <property name="toolTip"> + <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 5af0d49ba..4d2956638 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -81,13 +81,8 @@ void GetSharedFont(Service::Interface* self) { // The shared font has to be relocated to the new address before being passed to the application. VAddr target_address = Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address); - // The shared font dumped by 3dsutils (https://github.com/citra-emu/3dsutils) uses this address as base, - // so we relocate it from there to our real address. - // TODO(Subv): This address is wrong if the shared font is dumped from a n3DS, - // we need a way to automatically calculate the original address of the font from the file. - static const VAddr SHARED_FONT_VADDR = 0x18000000; if (!shared_font_relocated) { - BCFNT::RelocateSharedFont(shared_font_mem, SHARED_FONT_VADDR, target_address); + BCFNT::RelocateSharedFont(shared_font_mem, target_address); shared_font_relocated = true; } cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); diff --git a/src/core/hle/service/apt/bcfnt/bcfnt.cpp b/src/core/hle/service/apt/bcfnt/bcfnt.cpp index b0d39d4a5..57eb39d75 100644 --- a/src/core/hle/service/apt/bcfnt/bcfnt.cpp +++ b/src/core/hle/service/apt/bcfnt/bcfnt.cpp @@ -9,60 +9,97 @@ namespace Service { namespace APT { namespace BCFNT { -void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address) { +void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr new_address) { static const u32 SharedFontStartOffset = 0x80; - u8* data = shared_font->GetPointer(SharedFontStartOffset); + const u8* cfnt_ptr = shared_font->GetPointer(SharedFontStartOffset); CFNT cfnt; - memcpy(&cfnt, data, sizeof(cfnt)); + memcpy(&cfnt, cfnt_ptr, sizeof(cfnt)); - // Advance past the header - data = shared_font->GetPointer(SharedFontStartOffset + cfnt.header_size); + u32 assumed_cmap_offset = 0; + u32 assumed_cwdh_offset = 0; + u32 assumed_tglp_offset = 0; + u32 first_cmap_offset = 0; + u32 first_cwdh_offset = 0; + u32 first_tglp_offset = 0; + // First discover the location of sections so that the rebase offset can be auto-detected + u32 current_offset = SharedFontStartOffset + cfnt.header_size; for (unsigned block = 0; block < cfnt.num_blocks; ++block) { + const u8* data = shared_font->GetPointer(current_offset); + + SectionHeader section_header; + memcpy(§ion_header, data, sizeof(section_header)); + + if (first_cmap_offset == 0 && memcmp(section_header.magic, "CMAP", 4) == 0) { + first_cmap_offset = current_offset; + } else if (first_cwdh_offset == 0 && memcmp(section_header.magic, "CWDH", 4) == 0) { + first_cwdh_offset = current_offset; + } else if (first_tglp_offset == 0 && memcmp(section_header.magic, "TGLP", 4) == 0) { + first_tglp_offset = current_offset; + } else if (memcmp(section_header.magic, "FINF", 4) == 0) { + BCFNT::FINF finf; + memcpy(&finf, data, sizeof(finf)); + + assumed_cmap_offset = finf.cmap_offset - sizeof(SectionHeader); + assumed_cwdh_offset = finf.cwdh_offset - sizeof(SectionHeader); + assumed_tglp_offset = finf.tglp_offset - sizeof(SectionHeader); + } + + current_offset += section_header.section_size; + } + + u32 previous_base = assumed_cmap_offset - first_cmap_offset; + ASSERT(previous_base == assumed_cwdh_offset - first_cwdh_offset); + ASSERT(previous_base == assumed_tglp_offset - first_tglp_offset); + + u32 offset = new_address - previous_base; + + // Reset pointer back to start of sections and do the actual rebase + current_offset = SharedFontStartOffset + cfnt.header_size; + for (unsigned block = 0; block < cfnt.num_blocks; ++block) { + u8* data = shared_font->GetPointer(current_offset); + + SectionHeader section_header; + memcpy(§ion_header, data, sizeof(section_header)); - u32 section_size = 0; - if (memcmp(data, "FINF", 4) == 0) { + if (memcmp(section_header.magic, "FINF", 4) == 0) { BCFNT::FINF finf; memcpy(&finf, data, sizeof(finf)); - section_size = finf.section_size; // Relocate the offsets in the FINF section - finf.cmap_offset += new_address - previous_address; - finf.cwdh_offset += new_address - previous_address; - finf.tglp_offset += new_address - previous_address; + finf.cmap_offset += offset; + finf.cwdh_offset += offset; + finf.tglp_offset += offset; memcpy(data, &finf, sizeof(finf)); - } else if (memcmp(data, "CMAP", 4) == 0) { + } else if (memcmp(section_header.magic, "CMAP", 4) == 0) { BCFNT::CMAP cmap; memcpy(&cmap, data, sizeof(cmap)); - section_size = cmap.section_size; // Relocate the offsets in the CMAP section - cmap.next_cmap_offset += new_address - previous_address; + cmap.next_cmap_offset += offset; memcpy(data, &cmap, sizeof(cmap)); - } else if (memcmp(data, "CWDH", 4) == 0) { + } else if (memcmp(section_header.magic, "CWDH", 4) == 0) { BCFNT::CWDH cwdh; memcpy(&cwdh, data, sizeof(cwdh)); - section_size = cwdh.section_size; // Relocate the offsets in the CWDH section - cwdh.next_cwdh_offset += new_address - previous_address; + cwdh.next_cwdh_offset += offset; memcpy(data, &cwdh, sizeof(cwdh)); - } else if (memcmp(data, "TGLP", 4) == 0) { + } else if (memcmp(section_header.magic, "TGLP", 4) == 0) { BCFNT::TGLP tglp; memcpy(&tglp, data, sizeof(tglp)); - section_size = tglp.section_size; // Relocate the offsets in the TGLP section - tglp.sheet_data_offset += new_address - previous_address; + tglp.sheet_data_offset += offset; memcpy(data, &tglp, sizeof(tglp)); } - data += section_size; + current_offset += section_header.section_size; } } diff --git a/src/core/hle/service/apt/bcfnt/bcfnt.h b/src/core/hle/service/apt/bcfnt/bcfnt.h index 388c6bea0..8936dcf63 100644 --- a/src/core/hle/service/apt/bcfnt/bcfnt.h +++ b/src/core/hle/service/apt/bcfnt/bcfnt.h @@ -22,6 +22,11 @@ struct CFNT { u32_le num_blocks; }; +struct SectionHeader { + u8 magic[4]; + u32_le section_size; +}; + struct FINF { u8 magic[4]; u32_le section_size; @@ -75,12 +80,13 @@ struct CWDH { }; /** - * Relocates the internal addresses of the BCFNT Shared Font to the new base. + * Relocates the internal addresses of the BCFNT Shared Font to the new base. The current base will + * be auto-detected based on the file headers. + * * @param shared_font SharedMemory object that contains the Shared Font - * @param previous_address Previous address at which the offsets in the structure were based. * @param new_address New base for the offsets in the structure. */ -void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address); +void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr new_address); } // namespace BCFNT } // namespace APT diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 77261eafe..1b6733a79 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -24,6 +24,7 @@ void Apply() { VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution; AudioCore::SelectSink(values.sink_id); + AudioCore::EnableStretching(values.enable_audio_stretching); } diff --git a/src/core/settings.h b/src/core/settings.h index 34ff22f3f..fb3fbe391 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -82,6 +82,7 @@ struct Values { // Audio std::string sink_id; + bool enable_audio_stretching; // Debugging bool use_gdbstub; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 36513dedc..3de372f67 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -400,6 +400,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { default: LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); UNIMPLEMENTED(); + index = "0.0"; break; } |