2022-12-04 11:03:45 +00:00
|
|
|
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
|
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
|
|
|
2019-10-10 16:20:10 +00:00
|
|
|
#pragma once
|
2022-07-08 12:43:38 +00:00
|
|
|
#include "common/types.h"
|
2022-07-27 14:42:41 +00:00
|
|
|
#include <array>
|
2020-12-06 16:43:46 +00:00
|
|
|
#include <atomic>
|
2019-10-10 16:20:10 +00:00
|
|
|
#include <memory>
|
2022-07-27 14:42:41 +00:00
|
|
|
#include <optional>
|
2023-08-13 06:28:28 +00:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
2019-10-10 16:20:10 +00:00
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(push)
|
|
|
|
#pragma warning(disable : 4324) // warning C4324: structure was padded due to alignment specifier
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace soundtouch {
|
|
|
|
class SoundTouch;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum class AudioStretchMode : u8
|
|
|
|
{
|
|
|
|
Off,
|
|
|
|
Resample,
|
|
|
|
TimeStretch,
|
|
|
|
Count
|
|
|
|
};
|
2019-10-10 16:20:10 +00:00
|
|
|
|
|
|
|
class AudioStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using SampleType = s16;
|
|
|
|
|
2020-06-06 04:40:20 +00:00
|
|
|
enum : u32
|
2019-10-10 16:20:10 +00:00
|
|
|
{
|
2022-07-27 14:42:41 +00:00
|
|
|
CHUNK_SIZE = 64,
|
|
|
|
MAX_CHANNELS = 2
|
2019-10-10 16:20:10 +00:00
|
|
|
};
|
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
public:
|
2019-10-10 16:20:10 +00:00
|
|
|
virtual ~AudioStream();
|
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
static u32 GetAlignedBufferSize(u32 size);
|
|
|
|
static u32 GetBufferSizeForMS(u32 sample_rate, u32 ms);
|
|
|
|
static u32 GetMSForBufferSize(u32 sample_rate, u32 buffer_size);
|
2019-10-10 16:20:10 +00:00
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
static const char* GetStretchModeName(AudioStretchMode mode);
|
2022-08-25 15:26:55 +00:00
|
|
|
static const char* GetStretchModeDisplayName(AudioStretchMode mode);
|
2022-07-27 14:42:41 +00:00
|
|
|
static std::optional<AudioStretchMode> ParseStretchMode(const char* name);
|
2020-06-06 04:40:20 +00:00
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
ALWAYS_INLINE u32 GetSampleRate() const { return m_sample_rate; }
|
|
|
|
ALWAYS_INLINE u32 GetChannels() const { return m_channels; }
|
|
|
|
ALWAYS_INLINE u32 GetBufferSize() const { return m_buffer_size; }
|
|
|
|
ALWAYS_INLINE u32 GetTargetBufferSize() const { return m_target_buffer_size; }
|
|
|
|
ALWAYS_INLINE u32 GetOutputVolume() const { return m_volume; }
|
|
|
|
ALWAYS_INLINE float GetNominalTempo() const { return m_nominal_rate; }
|
|
|
|
ALWAYS_INLINE bool IsPaused() const { return m_paused; }
|
2021-01-10 07:01:02 +00:00
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
u32 GetBufferedFramesRelaxed() const;
|
2019-10-10 16:20:10 +00:00
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
/// Temporarily pauses the stream, preventing it from requesting data.
|
|
|
|
virtual void SetPaused(bool paused);
|
2019-10-10 16:20:10 +00:00
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
virtual void SetOutputVolume(u32 volume);
|
2019-10-10 16:20:10 +00:00
|
|
|
|
2020-01-24 04:53:40 +00:00
|
|
|
void BeginWrite(SampleType** buffer_ptr, u32* num_frames);
|
|
|
|
void WriteFrames(const SampleType* frames, u32 num_frames);
|
|
|
|
void EndWrite(u32 num_frames);
|
2019-10-10 16:20:10 +00:00
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
void EmptyBuffer();
|
2020-12-06 16:43:46 +00:00
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
/// Nominal rate is used for both resampling and timestretching, input samples are assumed to be this amount faster
|
|
|
|
/// than the sample rate.
|
|
|
|
void SetNominalRate(float tempo);
|
2022-07-29 11:16:02 +00:00
|
|
|
void UpdateTargetTempo(float tempo);
|
2020-01-11 03:28:40 +00:00
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
void SetStretchMode(AudioStretchMode mode);
|
|
|
|
|
|
|
|
static std::unique_ptr<AudioStream> CreateNullStream(u32 sample_rate, u32 channels, u32 buffer_ms);
|
2020-05-09 14:44:23 +00:00
|
|
|
|
2023-09-17 02:28:11 +00:00
|
|
|
#ifdef ENABLE_CUBEB
|
2023-08-13 06:28:28 +00:00
|
|
|
static std::unique_ptr<AudioStream> CreateCubebAudioStream(u32 sample_rate, u32 channels, u32 buffer_ms,
|
|
|
|
u32 latency_ms, AudioStretchMode stretch);
|
|
|
|
static std::vector<std::string> GetCubebDriverNames();
|
|
|
|
static std::vector<std::pair<std::string, std::string>> GetCubebOutputDevices(const char* driver);
|
|
|
|
#endif
|
2024-03-20 12:46:20 +00:00
|
|
|
#ifdef ENABLE_SDL2
|
|
|
|
static std::unique_ptr<AudioStream> CreateSDLAudioStream(u32 sample_rate, u32 channels, u32 buffer_ms, u32 latency_ms,
|
|
|
|
AudioStretchMode stretch);
|
|
|
|
#endif
|
2023-08-13 06:28:28 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
static std::unique_ptr<AudioStream> CreateXAudio2Stream(u32 sample_rate, u32 channels, u32 buffer_ms, u32 latency_ms,
|
|
|
|
AudioStretchMode stretch);
|
|
|
|
#endif
|
|
|
|
|
2019-10-10 16:20:10 +00:00
|
|
|
protected:
|
2022-07-27 14:42:41 +00:00
|
|
|
AudioStream(u32 sample_rate, u32 channels, u32 buffer_ms, AudioStretchMode stretch);
|
|
|
|
void BaseInitialize();
|
2019-10-10 16:20:10 +00:00
|
|
|
|
2024-03-20 12:46:20 +00:00
|
|
|
void ReadFrames(s16* samples, u32 num_frames);
|
|
|
|
void ApplyVolume(s16* samples, u32 num_frames);
|
2020-05-09 14:44:23 +00:00
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
u32 m_sample_rate = 0;
|
2019-10-10 16:20:10 +00:00
|
|
|
u32 m_channels = 0;
|
2022-07-27 14:42:41 +00:00
|
|
|
u32 m_buffer_ms = 0;
|
|
|
|
u32 m_volume = 0;
|
|
|
|
|
|
|
|
AudioStretchMode m_stretch_mode = AudioStretchMode::Off;
|
|
|
|
bool m_stretch_inactive = false;
|
|
|
|
bool m_filling = false;
|
|
|
|
bool m_paused = false;
|
|
|
|
|
|
|
|
private:
|
|
|
|
enum : u32
|
|
|
|
{
|
|
|
|
AVERAGING_BUFFER_SIZE = 256,
|
|
|
|
AVERAGING_WINDOW = 50,
|
|
|
|
STRETCH_RESET_THRESHOLD = 5,
|
|
|
|
TARGET_IPS = 691,
|
|
|
|
};
|
|
|
|
|
|
|
|
void AllocateBuffer();
|
|
|
|
void DestroyBuffer();
|
|
|
|
|
|
|
|
void InternalWriteFrames(s32* bData, u32 nFrames);
|
|
|
|
|
|
|
|
void StretchAllocate();
|
|
|
|
void StretchDestroy();
|
|
|
|
void StretchWrite();
|
|
|
|
void StretchUnderrun();
|
|
|
|
void StretchOverrun();
|
|
|
|
|
|
|
|
float AddAndGetAverageTempo(float val);
|
|
|
|
void UpdateStretchTempo();
|
|
|
|
|
2019-10-10 16:20:10 +00:00
|
|
|
u32 m_buffer_size = 0;
|
2022-07-27 14:42:41 +00:00
|
|
|
std::unique_ptr<s32[]> m_buffer;
|
|
|
|
|
|
|
|
std::atomic<u32> m_rpos{0};
|
|
|
|
std::atomic<u32> m_wpos{0};
|
|
|
|
|
|
|
|
std::unique_ptr<soundtouch::SoundTouch> m_soundtouch;
|
|
|
|
|
|
|
|
u32 m_target_buffer_size = 0;
|
|
|
|
u32 m_stretch_reset = STRETCH_RESET_THRESHOLD;
|
|
|
|
|
|
|
|
u32 m_stretch_ok_count = 0;
|
|
|
|
float m_nominal_rate = 1.0f;
|
|
|
|
float m_dynamic_target_usage = 0.0f;
|
|
|
|
|
|
|
|
u32 m_average_position = 0;
|
|
|
|
u32 m_average_available = 0;
|
|
|
|
u32 m_staging_buffer_pos = 0;
|
|
|
|
|
|
|
|
std::array<float, AVERAGING_BUFFER_SIZE> m_average_fullness = {};
|
|
|
|
|
|
|
|
// temporary staging buffer, used for timestretching
|
|
|
|
alignas(16) std::array<s32, CHUNK_SIZE> m_staging_buffer;
|
|
|
|
|
|
|
|
// float buffer, soundtouch only accepts float samples as input
|
|
|
|
alignas(16) std::array<float, CHUNK_SIZE * MAX_CHANNELS> m_float_buffer;
|
|
|
|
};
|
2019-10-10 16:20:10 +00:00
|
|
|
|
2022-07-27 14:42:41 +00:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|