c5d41fd812
In some cases, our callbacks were using s64 as a parameter, and in other cases, they were using an int, which is inconsistent. To make all callbacks consistent, we can just use an s64 as the type for late cycles, given it gets rid of the need to cast internally. While we're at it, also resolve some signed/unsigned conversions that were occurring related to the callback registration.
136 lines
3.7 KiB
C++
136 lines
3.7 KiB
C++
// Copyright 2018 yuzu Emulator Project
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
#include "audio_core/sink.h"
|
|
#include "audio_core/sink_details.h"
|
|
#include "audio_core/sink_stream.h"
|
|
#include "audio_core/stream.h"
|
|
#include "common/assert.h"
|
|
#include "common/logging/log.h"
|
|
#include "core/core_timing.h"
|
|
#include "core/core_timing_util.h"
|
|
#include "core/settings.h"
|
|
|
|
namespace AudioCore {
|
|
|
|
constexpr std::size_t MaxAudioBufferCount{32};
|
|
|
|
u32 Stream::GetNumChannels() const {
|
|
switch (format) {
|
|
case Format::Mono16:
|
|
return 1;
|
|
case Format::Stereo16:
|
|
return 2;
|
|
case Format::Multi51Channel16:
|
|
return 6;
|
|
}
|
|
UNIMPLEMENTED_MSG("Unimplemented format={}", static_cast<u32>(format));
|
|
return {};
|
|
}
|
|
|
|
Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format,
|
|
ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_)
|
|
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
|
|
sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
|
|
|
|
release_event = core_timing.RegisterEvent(
|
|
name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); });
|
|
}
|
|
|
|
void Stream::Play() {
|
|
state = State::Playing;
|
|
PlayNextBuffer();
|
|
}
|
|
|
|
void Stream::Stop() {
|
|
state = State::Stopped;
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
Stream::State Stream::GetState() const {
|
|
return state;
|
|
}
|
|
|
|
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
|
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
|
return Core::Timing::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
|
}
|
|
|
|
static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
|
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
|
|
|
|
if (volume == 1.0f) {
|
|
return;
|
|
}
|
|
|
|
// Implementation of a volume slider with a dynamic range of 60 dB
|
|
const float volume_scale_factor = volume == 0 ? 0 : std::exp(6.90775f * volume) * 0.001f;
|
|
for (auto& sample : samples) {
|
|
sample = static_cast<s16>(sample * volume_scale_factor);
|
|
}
|
|
}
|
|
|
|
void Stream::PlayNextBuffer() {
|
|
if (!IsPlaying()) {
|
|
// Ensure we are in playing state before playing the next buffer
|
|
sink_stream.Flush();
|
|
return;
|
|
}
|
|
|
|
if (active_buffer) {
|
|
// Do not queue a new buffer if we are already playing a buffer
|
|
return;
|
|
}
|
|
|
|
if (queued_buffers.empty()) {
|
|
// No queued buffers - we are effectively paused
|
|
sink_stream.Flush();
|
|
return;
|
|
}
|
|
|
|
active_buffer = queued_buffers.front();
|
|
queued_buffers.pop();
|
|
|
|
VolumeAdjustSamples(active_buffer->GetSamples());
|
|
|
|
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
|
|
|
core_timing.ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
|
}
|
|
|
|
void Stream::ReleaseActiveBuffer() {
|
|
ASSERT(active_buffer);
|
|
released_buffers.push(std::move(active_buffer));
|
|
release_callback();
|
|
PlayNextBuffer();
|
|
}
|
|
|
|
bool Stream::QueueBuffer(BufferPtr&& buffer) {
|
|
if (queued_buffers.size() < MaxAudioBufferCount) {
|
|
queued_buffers.push(std::move(buffer));
|
|
PlayNextBuffer();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
|
UNIMPLEMENTED();
|
|
return {};
|
|
}
|
|
|
|
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
|
|
std::vector<Buffer::Tag> tags;
|
|
for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
|
tags.push_back(released_buffers.front()->GetTag());
|
|
released_buffers.pop();
|
|
}
|
|
return tags;
|
|
}
|
|
|
|
} // namespace AudioCore
|