mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-15 07:25:46 -04:00
SPU: Add time stretched audio output
This commit is contained in:
@ -13,6 +13,7 @@
|
||||
|
||||
struct WindowInfo;
|
||||
enum class AudioBackend : u8;
|
||||
enum class AudioStretchMode : u8;
|
||||
class AudioStream;
|
||||
class CDImage;
|
||||
|
||||
@ -77,7 +78,8 @@ std::optional<std::time_t> GetResourceFileTimestamp(const char* filename);
|
||||
TinyString TranslateString(const char* context, const char* str, const char* disambiguation = nullptr, int n = -1);
|
||||
std::string TranslateStdString(const char* context, const char* str, const char* disambiguation = nullptr, int n = -1);
|
||||
|
||||
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend);
|
||||
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend, u32 sample_rate, u32 channels, u32 buffer_ms,
|
||||
u32 latency_ms, AudioStretchMode stretch);
|
||||
|
||||
/// Returns the scale of OSD elements.
|
||||
float GetOSDScale();
|
||||
|
@ -275,10 +275,15 @@ void Settings::Load(SettingsInterface& si)
|
||||
audio_backend =
|
||||
ParseAudioBackend(si.GetStringValue("Audio", "Backend", GetAudioBackendName(DEFAULT_AUDIO_BACKEND)).c_str())
|
||||
.value_or(DEFAULT_AUDIO_BACKEND);
|
||||
audio_output_volume = si.GetIntValue("Audio", "OutputVolume", 100);
|
||||
audio_fast_forward_volume = si.GetIntValue("Audio", "FastForwardVolume", 100);
|
||||
audio_buffer_size = si.GetIntValue("Audio", "BufferSize", DEFAULT_AUDIO_BUFFER_SIZE);
|
||||
audio_resampling = si.GetBoolValue("Audio", "Resampling", true);
|
||||
audio_stretch_mode =
|
||||
AudioStream::ParseStretchMode(
|
||||
si.GetStringValue("Audio", "StretchMode", AudioStream::GetStretchModeName(DEFAULT_AUDIO_STRETCH_MODE)).c_str())
|
||||
.value_or(DEFAULT_AUDIO_STRETCH_MODE);
|
||||
audio_output_latency_ms = si.GetUIntValue("Audio", "OutputLatencyMS", DEFAULT_AUDIO_OUTPUT_LATENCY_MS);
|
||||
audio_buffer_ms = si.GetUIntValue("Audio", "BufferMS", DEFAULT_AUDIO_BUFFER_MS);
|
||||
audio_output_volume = si.GetUIntValue("Audio", "OutputVolume", 100);
|
||||
audio_fast_forward_volume = si.GetUIntValue("Audio", "FastForwardVolume", 100);
|
||||
|
||||
audio_output_muted = si.GetBoolValue("Audio", "OutputMuted", false);
|
||||
audio_sync_enabled = si.GetBoolValue("Audio", "Sync", true);
|
||||
audio_dump_on_boot = si.GetBoolValue("Audio", "DumpOnBoot", false);
|
||||
@ -472,10 +477,11 @@ void Settings::Save(SettingsInterface& si) const
|
||||
si.SetIntValue("CDROM", "SeekSpeedup", cdrom_seek_speedup);
|
||||
|
||||
si.SetStringValue("Audio", "Backend", GetAudioBackendName(audio_backend));
|
||||
si.SetIntValue("Audio", "OutputVolume", audio_output_volume);
|
||||
si.SetIntValue("Audio", "FastForwardVolume", audio_fast_forward_volume);
|
||||
si.SetIntValue("Audio", "BufferSize", audio_buffer_size);
|
||||
si.SetBoolValue("Audio", "Resampling", audio_resampling);
|
||||
si.SetStringValue("Audio", "StretchMode", AudioStream::GetStretchModeName(audio_stretch_mode));
|
||||
si.SetUIntValue("Audio", "BufferMS", audio_buffer_ms);
|
||||
si.SetUIntValue("Audio", "OutputLatencyMS", audio_output_latency_ms);
|
||||
si.SetUIntValue("Audio", "OutputVolume", audio_output_volume);
|
||||
si.SetUIntValue("Audio", "FastForwardVolume", audio_fast_forward_volume);
|
||||
si.SetBoolValue("Audio", "OutputMuted", audio_output_muted);
|
||||
si.SetBoolValue("Audio", "Sync", audio_sync_enabled);
|
||||
si.SetBoolValue("Audio", "DumpOnBoot", audio_dump_on_boot);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "common/settings_interface.h"
|
||||
#include "common/string.h"
|
||||
#include "types.h"
|
||||
#include "util/audio_stream.h"
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@ -142,10 +143,11 @@ struct Settings
|
||||
u32 cdrom_seek_speedup = 1;
|
||||
|
||||
AudioBackend audio_backend = DEFAULT_AUDIO_BACKEND;
|
||||
s32 audio_output_volume = 100;
|
||||
s32 audio_fast_forward_volume = 100;
|
||||
u32 audio_buffer_size = DEFAULT_AUDIO_BUFFER_SIZE;
|
||||
bool audio_resampling = true;
|
||||
AudioStretchMode audio_stretch_mode = DEFAULT_AUDIO_STRETCH_MODE;
|
||||
u32 audio_output_latency_ms = DEFAULT_AUDIO_OUTPUT_LATENCY_MS;
|
||||
u32 audio_buffer_ms = DEFAULT_AUDIO_BUFFER_MS;
|
||||
u32 audio_output_volume = 100;
|
||||
u32 audio_fast_forward_volume = 100;
|
||||
bool audio_output_muted = false;
|
||||
bool audio_sync_enabled = true;
|
||||
bool audio_dump_on_boot = false;
|
||||
@ -400,7 +402,9 @@ struct Settings
|
||||
|
||||
static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO;
|
||||
|
||||
static constexpr u32 DEFAULT_AUDIO_BUFFER_SIZE = 2048;
|
||||
static constexpr u32 DEFAULT_AUDIO_BUFFER_MS = 50;
|
||||
static constexpr u32 DEFAULT_AUDIO_OUTPUT_LATENCY_MS = 20;
|
||||
static constexpr AudioStretchMode DEFAULT_AUDIO_STRETCH_MODE = AudioStretchMode::TimeStretch;
|
||||
|
||||
// Enable console logging by default on Linux platforms.
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
|
@ -30,8 +30,7 @@ void SPU::Initialize()
|
||||
"SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
|
||||
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<SPU*>(param)->ExecuteTransfer(ticks); }, this,
|
||||
false);
|
||||
m_null_audio_stream = AudioStream::CreateNullAudioStream();
|
||||
m_null_audio_stream->Reconfigure(SAMPLE_RATE, SAMPLE_RATE, NUM_CHANNELS, Settings::DEFAULT_AUDIO_BUFFER_SIZE);
|
||||
m_null_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms);
|
||||
|
||||
CreateOutputStream();
|
||||
Reset();
|
||||
@ -39,22 +38,23 @@ void SPU::Initialize()
|
||||
|
||||
void SPU::CreateOutputStream()
|
||||
{
|
||||
Log_InfoPrintf("Creating '%s' audio stream, sample rate = %u, channels = %u, buffer size = %u",
|
||||
Settings::GetAudioBackendName(g_settings.audio_backend), SAMPLE_RATE, NUM_CHANNELS,
|
||||
g_settings.audio_buffer_size);
|
||||
Log_InfoPrintf(
|
||||
"Creating '%s' audio stream, sample rate = %u, channels = %u, buffer = %u, latency = %u, stretching = %s",
|
||||
Settings::GetAudioBackendName(g_settings.audio_backend), SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms,
|
||||
g_settings.audio_output_latency_ms, AudioStream::GetStretchModeName(g_settings.audio_stretch_mode));
|
||||
|
||||
m_audio_stream = Host::CreateAudioStream(g_settings.audio_backend);
|
||||
|
||||
if (!m_audio_stream ||
|
||||
!m_audio_stream->Reconfigure(SAMPLE_RATE, SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_size))
|
||||
m_audio_stream =
|
||||
Host::CreateAudioStream(g_settings.audio_backend, SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms,
|
||||
g_settings.audio_output_latency_ms, g_settings.audio_stretch_mode);
|
||||
if (!m_audio_stream)
|
||||
{
|
||||
Host::ReportErrorAsync("Error", "Failed to create or configure audio stream, falling back to null output.");
|
||||
m_audio_stream.reset();
|
||||
m_audio_stream = AudioStream::CreateNullAudioStream();
|
||||
m_audio_stream->Reconfigure(SAMPLE_RATE, SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_size);
|
||||
m_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms);
|
||||
}
|
||||
|
||||
m_audio_stream->SetOutputVolume(System::GetAudioOutputVolume());
|
||||
m_audio_stream->SetPaused(System::IsPaused());
|
||||
}
|
||||
|
||||
void SPU::RecreateOutputStream()
|
||||
@ -77,7 +77,7 @@ void SPU::Shutdown()
|
||||
m_tick_event.reset();
|
||||
m_transfer_event.reset();
|
||||
m_dump_writer.reset();
|
||||
m_audio_stream = nullptr;
|
||||
m_audio_stream.reset();
|
||||
}
|
||||
|
||||
void SPU::Reset()
|
||||
|
@ -923,9 +923,7 @@ void System::PauseSystem(bool paused)
|
||||
return;
|
||||
|
||||
SetState(paused ? State::Paused : State::Running);
|
||||
if (!paused)
|
||||
g_spu.GetOutputStream()->EmptyBuffers();
|
||||
g_spu.GetOutputStream()->PauseOutput(paused);
|
||||
g_spu.GetOutputStream()->SetPaused(paused);
|
||||
|
||||
if (paused)
|
||||
{
|
||||
@ -1179,7 +1177,7 @@ bool System::BootSystem(SystemBootParameters parameters)
|
||||
// Good to go.
|
||||
Host::OnSystemStarted();
|
||||
UpdateSoftwareCursor();
|
||||
g_spu.GetOutputStream()->PauseOutput(false);
|
||||
g_spu.GetOutputStream()->SetPaused(false);
|
||||
|
||||
// Initial state must be set before loading state.
|
||||
s_state =
|
||||
@ -1813,7 +1811,7 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u
|
||||
if (s_state == State::Starting)
|
||||
s_state = State::Running;
|
||||
|
||||
g_spu.GetOutputStream()->EmptyBuffers();
|
||||
g_spu.GetOutputStream()->EmptyBuffer();
|
||||
ResetPerformanceCounters();
|
||||
ResetThrottler();
|
||||
return true;
|
||||
@ -2035,14 +2033,6 @@ void System::ResetThrottler()
|
||||
|
||||
void System::Throttle()
|
||||
{
|
||||
// Reset the throttler on audio buffer overflow, so we don't end up out of phase.
|
||||
if (g_spu.GetOutputStream()->DidUnderflow() && s_target_speed >= 1.0f)
|
||||
{
|
||||
Log_VerbosePrintf("Audio buffer underflowed, resetting throttler");
|
||||
ResetThrottler();
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow variance of up to 40ms either way.
|
||||
#ifndef __ANDROID__
|
||||
static constexpr double MAX_VARIANCE_TIME_NS = 40 * 1000000;
|
||||
@ -2181,7 +2171,8 @@ void System::UpdateSpeedLimiterState()
|
||||
m_display_all_frames = !m_throttler_enabled || g_settings.display_all_frames;
|
||||
|
||||
bool syncing_to_host = false;
|
||||
if (g_settings.sync_to_host_refresh_rate && g_settings.audio_resampling && target_speed == 1.0f && IsRunning())
|
||||
if (g_settings.sync_to_host_refresh_rate && (g_settings.audio_stretch_mode != AudioStretchMode::Off) &&
|
||||
target_speed == 1.0f && IsValid())
|
||||
{
|
||||
float host_refresh_rate;
|
||||
if (g_host_display->GetHostRefreshRate(&host_refresh_rate))
|
||||
@ -2212,21 +2203,18 @@ void System::UpdateSpeedLimiterState()
|
||||
UpdateThrottlePeriod();
|
||||
ResetThrottler();
|
||||
|
||||
const u32 input_sample_rate = (target_speed == 0.0f || !g_settings.audio_resampling) ?
|
||||
SPU::SAMPLE_RATE :
|
||||
static_cast<u32>(static_cast<float>(SPU::SAMPLE_RATE) * target_speed);
|
||||
Log_InfoPrintf("Audio input sample rate: %u hz", input_sample_rate);
|
||||
|
||||
AudioStream* stream = g_spu.GetOutputStream();
|
||||
stream->SetInputSampleRate(input_sample_rate);
|
||||
stream->SetWaitForBufferFill(true);
|
||||
|
||||
if (g_settings.audio_fast_forward_volume != g_settings.audio_output_volume)
|
||||
stream->SetOutputVolume(GetAudioOutputVolume());
|
||||
|
||||
stream->SetSync(audio_sync_enabled);
|
||||
if (audio_sync_enabled)
|
||||
stream->EmptyBuffers();
|
||||
// Adjust nominal rate when resampling, or syncing to host.
|
||||
const bool rate_adjust =
|
||||
(syncing_to_host || g_settings.audio_stretch_mode == AudioStretchMode::Resample) && target_speed > 0.0f;
|
||||
stream->SetNominalRate(rate_adjust ? target_speed : 1.0f);
|
||||
|
||||
// stream->SetSync(audio_sync_enabled);
|
||||
// if (audio_sync_enabled)
|
||||
// stream->EmptyBuffer();
|
||||
}
|
||||
|
||||
g_host_display->SetDisplayMaxFPS(max_display_fps);
|
||||
@ -3034,8 +3022,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||
UpdateOverclock();
|
||||
}
|
||||
|
||||
if (g_settings.audio_backend != old_settings.audio_backend ||
|
||||
g_settings.audio_buffer_size != old_settings.audio_buffer_size)
|
||||
if (g_settings.audio_backend != old_settings.audio_backend)
|
||||
{
|
||||
if (g_settings.audio_backend != old_settings.audio_backend)
|
||||
{
|
||||
@ -3044,7 +3031,15 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||
}
|
||||
|
||||
g_spu.RecreateOutputStream();
|
||||
g_spu.GetOutputStream()->PauseOutput(IsPaused());
|
||||
}
|
||||
if (g_settings.audio_stretch_mode != old_settings.audio_stretch_mode)
|
||||
g_spu.GetOutputStream()->SetStretchMode(g_settings.audio_stretch_mode);
|
||||
if (g_settings.audio_buffer_ms != old_settings.audio_buffer_ms ||
|
||||
g_settings.audio_output_latency_ms != old_settings.audio_output_latency_ms ||
|
||||
g_settings.audio_stretch_mode != old_settings.audio_stretch_mode)
|
||||
{
|
||||
g_spu.RecreateOutputStream();
|
||||
UpdateSpeedLimiterState();
|
||||
}
|
||||
|
||||
if (g_settings.emulation_speed != old_settings.emulation_speed)
|
||||
@ -3169,7 +3164,6 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||
g_dma.SetHaltTicks(g_settings.dma_halt_ticks);
|
||||
|
||||
if (g_settings.audio_backend != old_settings.audio_backend ||
|
||||
g_settings.audio_buffer_size != old_settings.audio_buffer_size ||
|
||||
g_settings.video_sync_enabled != old_settings.video_sync_enabled ||
|
||||
g_settings.audio_sync_enabled != old_settings.audio_sync_enabled ||
|
||||
g_settings.increase_timer_resolution != old_settings.increase_timer_resolution ||
|
||||
@ -3177,7 +3171,6 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||
g_settings.fast_forward_speed != old_settings.fast_forward_speed ||
|
||||
g_settings.display_max_fps != old_settings.display_max_fps ||
|
||||
g_settings.display_all_frames != old_settings.display_all_frames ||
|
||||
g_settings.audio_resampling != old_settings.audio_resampling ||
|
||||
g_settings.sync_to_host_refresh_rate != old_settings.sync_to_host_refresh_rate)
|
||||
{
|
||||
UpdateSpeedLimiterState();
|
||||
|
Reference in New Issue
Block a user