Duckstation/src/common/audio_stream.cpp

220 lines
5.3 KiB
C++
Raw Normal View History

2019-10-10 16:20:10 +00:00
#include "audio_stream.h"
2020-01-10 03:31:12 +00:00
#include "assert.h"
#include "log.h"
2020-01-10 03:31:12 +00:00
#include <algorithm>
#include <cstring>
Log_SetChannel(AudioStream);
2019-10-10 16:20:10 +00:00
AudioStream::AudioStream() = default;
AudioStream::~AudioStream() = default;
bool AudioStream::Reconfigure(u32 output_sample_rate /*= DefaultOutputSampleRate*/, u32 channels /*= 1*/,
u32 buffer_size /*= DefaultBufferSize*/)
2019-10-10 16:20:10 +00:00
{
if (IsDeviceOpen())
CloseDevice();
m_output_sample_rate = output_sample_rate;
m_channels = channels;
m_buffer_size = buffer_size;
m_output_paused = true;
if (!SetBufferSize(buffer_size))
return false;
2019-10-10 16:20:10 +00:00
if (!OpenDevice())
{
EmptyBuffers();
m_buffer_size = 0;
m_output_sample_rate = 0;
m_channels = 0;
return false;
}
return true;
}
void AudioStream::SetOutputVolume(u32 volume)
{
std::unique_lock<std::mutex> lock(m_buffer_mutex);
m_output_volume = volume;
}
2019-10-10 16:20:10 +00:00
void AudioStream::PauseOutput(bool paused)
{
if (m_output_paused == paused)
return;
PauseDevice(paused);
m_output_paused = paused;
// Empty buffers on pause.
if (paused)
EmptyBuffers();
}
void AudioStream::Shutdown()
{
if (!IsDeviceOpen())
return;
CloseDevice();
EmptyBuffers();
m_buffer_size = 0;
m_output_sample_rate = 0;
m_channels = 0;
m_output_paused = true;
}
void AudioStream::BeginWrite(SampleType** buffer_ptr, u32* num_frames)
2019-10-10 16:20:10 +00:00
{
2019-10-11 03:24:41 +00:00
m_buffer_mutex.lock();
2019-10-10 16:20:10 +00:00
EnsureBuffer(*num_frames * m_channels);
2019-10-10 16:20:10 +00:00
*buffer_ptr = m_buffer.GetWritePointer();
*num_frames = m_buffer.GetContiguousSpace() / m_channels;
2019-10-10 16:20:10 +00:00
}
void AudioStream::WriteFrames(const SampleType* frames, u32 num_frames)
2019-10-10 16:20:10 +00:00
{
const u32 num_samples = num_frames * m_channels;
2019-10-11 03:24:41 +00:00
std::unique_lock<std::mutex> lock(m_buffer_mutex);
2019-10-10 16:20:10 +00:00
EnsureBuffer(num_samples);
m_buffer.PushRange(frames, num_samples);
FramesAvailable();
2019-10-10 16:20:10 +00:00
}
void AudioStream::EndWrite(u32 num_frames)
2019-10-10 16:20:10 +00:00
{
m_buffer.AdvanceTail(num_frames * m_channels);
FramesAvailable();
2019-10-10 16:20:10 +00:00
2019-10-11 03:24:41 +00:00
m_buffer_mutex.unlock();
2019-10-10 16:20:10 +00:00
}
float AudioStream::GetMaxLatency(u32 sample_rate, u32 buffer_size)
{
return (static_cast<float>(buffer_size) / static_cast<float>(sample_rate));
}
bool AudioStream::SetBufferSize(u32 buffer_size)
{
const u32 buffer_size_in_samples = buffer_size * m_channels;
const u32 max_samples = buffer_size_in_samples * 2u;
if (max_samples > m_buffer.GetCapacity())
return false;
m_buffer_size = buffer_size;
m_max_samples = max_samples;
return true;
}
2020-01-07 04:17:41 +00:00
u32 AudioStream::GetSamplesAvailable() const
{
// TODO: Use atomic loads
u32 available_samples;
2020-01-07 04:17:41 +00:00
{
std::unique_lock<std::mutex> lock(m_buffer_mutex);
available_samples = m_buffer.GetSize();
2020-01-07 04:17:41 +00:00
}
return available_samples / m_channels;
2020-01-07 04:17:41 +00:00
}
u32 AudioStream::GetSamplesAvailableLocked() const
2019-10-10 16:20:10 +00:00
{
return m_buffer.GetSize() / m_channels;
}
2019-10-10 16:20:10 +00:00
void AudioStream::ReadFrames(SampleType* samples, u32 num_frames, bool apply_volume)
{
const u32 total_samples = num_frames * m_channels;
u32 samples_copied = 0;
2019-10-10 16:20:10 +00:00
{
std::unique_lock<std::mutex> lock(m_buffer_mutex);
samples_copied = std::min(m_buffer.GetSize(), total_samples);
if (samples_copied > 0)
m_buffer.PopRange(samples, samples_copied);
2019-10-10 16:20:10 +00:00
m_buffer_draining_cv.notify_one();
}
2019-10-10 16:20:10 +00:00
if (samples_copied < total_samples)
{
if (samples_copied > 0)
2019-10-10 16:20:10 +00:00
{
m_resample_buffer.resize(samples_copied);
std::memcpy(m_resample_buffer.data(), samples, sizeof(SampleType) * samples_copied);
// super basic resampler - spread the input samples evenly across the output samples. will sound like ass and have
// aliasing, but better than popping by inserting silence.
const u32 increment =
static_cast<u32>(65536.0f * (static_cast<float>(samples_copied / m_channels) / static_cast<float>(num_frames)));
SampleType* out_ptr = samples;
const SampleType* resample_ptr = m_resample_buffer.data();
const u32 copy_stride = sizeof(SampleType) * m_channels;
u32 resample_subpos = 0;
for (u32 i = 0; i < num_frames; i++)
{
std::memcpy(out_ptr, resample_ptr, copy_stride);
out_ptr += m_channels;
resample_subpos += increment;
resample_ptr += (resample_subpos >> 16) * m_channels;
resample_subpos %= 65536u;
}
Log_DevPrintf("Audio buffer underflow, resampled %u frames to %u", samples_copied / m_channels, num_frames);
}
else
{
// read nothing, so zero-fill
std::memset(samples, 0, sizeof(SampleType) * total_samples);
Log_DevPrintf("Audio buffer underflow with no samples, added %u frames silence", num_frames);
2019-10-10 16:20:10 +00:00
}
}
if (apply_volume && m_output_volume != FullVolume)
2019-10-10 16:20:10 +00:00
{
SampleType* current_ptr = samples;
const SampleType* end_ptr = samples + (num_frames * m_channels);
while (current_ptr != end_ptr)
{
*current_ptr = ApplyVolume(*current_ptr, m_output_volume);
current_ptr++;
}
2019-10-10 16:20:10 +00:00
}
}
void AudioStream::EnsureBuffer(u32 size)
2019-10-11 03:24:41 +00:00
{
if (GetBufferSpace() >= size)
2019-10-11 03:24:41 +00:00
return;
if (m_sync)
{
std::unique_lock<std::mutex> lock(m_buffer_mutex, std::adopt_lock);
m_buffer_draining_cv.wait(lock, [this, size]() { return GetBufferSpace() >= size; });
2019-10-11 03:24:41 +00:00
lock.release();
}
else
{
m_buffer.Remove(size);
2019-10-11 03:24:41 +00:00
}
}
void AudioStream::DropFrames(u32 count)
2019-10-10 16:20:10 +00:00
{
m_buffer.Remove(count);
2019-10-10 16:20:10 +00:00
}
void AudioStream::EmptyBuffers()
{
2019-10-11 06:54:21 +00:00
std::unique_lock<std::mutex> lock(m_buffer_mutex);
m_buffer.Clear();
2019-10-10 16:20:10 +00:00
}