8 changed files with 336 additions and 1 deletions
-
1src/CMakeLists.txt
-
11src/audio_core/CMakeLists.txt
-
50src/audio_core/audio_out.cpp
-
44src/audio_core/audio_out.h
-
37src/audio_core/buffer.h
-
103src/audio_core/stream.cpp
-
89src/audio_core/stream.h
-
2src/core/CMakeLists.txt
@ -0,0 +1,11 @@ |
|||
add_library(audio_core STATIC |
|||
audio_out.cpp |
|||
audio_out.h |
|||
buffer.h |
|||
stream.cpp |
|||
stream.h |
|||
) |
|||
|
|||
create_target_directory_groups(audio_core) |
|||
|
|||
target_link_libraries(audio_core PUBLIC common core) |
|||
@ -0,0 +1,50 @@ |
|||
// Copyright 2018 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "audio_core/audio_out.h"
|
|||
#include "common/assert.h"
|
|||
#include "common/logging/log.h"
|
|||
|
|||
namespace AudioCore { |
|||
|
|||
/// Returns the stream format from the specified number of channels
|
|||
static Stream::Format ChannelsToStreamFormat(int num_channels) { |
|||
switch (num_channels) { |
|||
case 1: |
|||
return Stream::Format::Mono16; |
|||
case 2: |
|||
return Stream::Format::Stereo16; |
|||
case 6: |
|||
return Stream::Format::Multi51Channel16; |
|||
} |
|||
|
|||
LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels); |
|||
UNREACHABLE(); |
|||
return {}; |
|||
} |
|||
|
|||
StreamPtr AudioOut::OpenStream(int sample_rate, int num_channels, |
|||
Stream::ReleaseCallback&& release_callback) { |
|||
streams.push_back(std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels), |
|||
std::move(release_callback))); |
|||
return streams.back(); |
|||
} |
|||
|
|||
std::vector<u64> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) { |
|||
return stream->GetTagsAndReleaseBuffers(max_count); |
|||
} |
|||
|
|||
void AudioOut::StartStream(StreamPtr stream) { |
|||
stream->Play(); |
|||
} |
|||
|
|||
void AudioOut::StopStream(StreamPtr stream) { |
|||
stream->Stop(); |
|||
} |
|||
|
|||
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) { |
|||
return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data))); |
|||
} |
|||
|
|||
} // namespace AudioCore
|
|||
@ -0,0 +1,44 @@ |
|||
// Copyright 2018 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <vector> |
|||
|
|||
#include "audio_core/buffer.h" |
|||
#include "audio_core/stream.h" |
|||
#include "common/common_types.h" |
|||
|
|||
namespace AudioCore { |
|||
|
|||
using StreamPtr = std::shared_ptr<Stream>; |
|||
|
|||
/** |
|||
* Represents an audio playback interface, used to open and play audio streams |
|||
*/ |
|||
class AudioOut { |
|||
public: |
|||
/// Opens a new audio stream |
|||
StreamPtr OpenStream(int sample_rate, int num_channels, |
|||
Stream::ReleaseCallback&& release_callback); |
|||
|
|||
/// Returns a vector of recently released buffers specified by tag for the specified stream |
|||
std::vector<u64> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count); |
|||
|
|||
/// Starts an audio stream for playback |
|||
void StartStream(StreamPtr stream); |
|||
|
|||
/// Stops an audio stream that is currently playing |
|||
void StopStream(StreamPtr stream); |
|||
|
|||
/// Queues a buffer into the specified audio stream, returns true on success |
|||
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data); |
|||
|
|||
private: |
|||
/// Active audio streams on the interface |
|||
std::vector<StreamPtr> streams; |
|||
}; |
|||
|
|||
} // namespace AudioCore |
|||
@ -0,0 +1,37 @@ |
|||
// Copyright 2018 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
|
|||
#include "common/common_types.h" |
|||
|
|||
namespace AudioCore { |
|||
|
|||
/** |
|||
* Represents a buffer of audio samples to be played in an audio stream |
|||
*/ |
|||
class Buffer { |
|||
public: |
|||
using Tag = u64; |
|||
|
|||
Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {} |
|||
|
|||
/// Returns the raw audio data for the buffer |
|||
const std::vector<u8>& GetData() const { |
|||
return data; |
|||
} |
|||
|
|||
/// Returns the buffer tag, this is provided by the game to the audout service |
|||
Tag GetTag() const { |
|||
return tag; |
|||
} |
|||
|
|||
private: |
|||
Tag tag; |
|||
std::vector<u8> data; |
|||
}; |
|||
|
|||
} // namespace AudioCore |
|||
@ -0,0 +1,103 @@ |
|||
// Copyright 2018 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "common/logging/log.h"
|
|||
#include "core/core_timing.h"
|
|||
#include "core/core_timing_util.h"
|
|||
|
|||
#include "audio_core/stream.h"
|
|||
|
|||
namespace AudioCore { |
|||
|
|||
constexpr size_t MaxAudioBufferCount{32}; |
|||
|
|||
/// Returns the sample size for the specified audio stream format
|
|||
static size_t SampleSizeFromFormat(Stream::Format format) { |
|||
switch (format) { |
|||
case Stream::Format::Mono16: |
|||
return 2; |
|||
case Stream::Format::Stereo16: |
|||
return 4; |
|||
case Stream::Format::Multi51Channel16: |
|||
return 12; |
|||
}; |
|||
|
|||
LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format)); |
|||
UNREACHABLE(); |
|||
return {}; |
|||
} |
|||
|
|||
Stream::Stream(int sample_rate, Format format, ReleaseCallback&& release_callback) |
|||
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)} { |
|||
release_event = CoreTiming::RegisterEvent( |
|||
"Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); }); |
|||
} |
|||
|
|||
void Stream::Play() { |
|||
state = State::Playing; |
|||
PlayNextBuffer(); |
|||
} |
|||
|
|||
void Stream::Stop() { |
|||
ASSERT_MSG(false, "Unimplemented"); |
|||
} |
|||
|
|||
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { |
|||
const size_t num_samples{buffer.GetData().size() / SampleSizeFromFormat(format)}; |
|||
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); |
|||
} |
|||
|
|||
void Stream::PlayNextBuffer() { |
|||
if (!IsPlaying()) { |
|||
// Ensure we are in playing state before playing the next buffer
|
|||
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
|
|||
return; |
|||
} |
|||
|
|||
active_buffer = queued_buffers.front(); |
|||
queued_buffers.pop(); |
|||
|
|||
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); |
|||
} |
|||
|
|||
void Stream::ReleaseActiveBuffer() { |
|||
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 { |
|||
ASSERT_MSG(false, "Unimplemented"); |
|||
return {}; |
|||
} |
|||
|
|||
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) { |
|||
std::vector<Buffer::Tag> tags; |
|||
for (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
|
|||
@ -0,0 +1,89 @@ |
|||
// Copyright 2018 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <functional> |
|||
#include <memory> |
|||
#include <vector> |
|||
#include <queue> |
|||
|
|||
#include "audio_core/buffer.h" |
|||
#include "common/assert.h" |
|||
#include "common/common_types.h" |
|||
#include "core/core_timing.h" |
|||
|
|||
namespace AudioCore { |
|||
|
|||
using BufferPtr = std::shared_ptr<Buffer>; |
|||
|
|||
/** |
|||
* Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut |
|||
*/ |
|||
class Stream { |
|||
public: |
|||
/// Audio format of the stream |
|||
enum class Format { |
|||
Mono16, |
|||
Stereo16, |
|||
Multi51Channel16, |
|||
}; |
|||
|
|||
/// Callback function type, used to change guest state on a buffer being released |
|||
using ReleaseCallback = std::function<void()>; |
|||
|
|||
Stream(int sample_rate, Format format, ReleaseCallback&& release_callback); |
|||
|
|||
/// Plays the audio stream |
|||
void Play(); |
|||
|
|||
/// Stops the audio stream |
|||
void Stop(); |
|||
|
|||
/// Queues a buffer into the audio stream, returns true on success |
|||
bool QueueBuffer(BufferPtr&& buffer); |
|||
|
|||
/// Returns true if the audio stream contains a buffer with the specified tag |
|||
bool ContainsBuffer(Buffer::Tag tag) const; |
|||
|
|||
/// Returns a vector of recently released buffers specified by tag |
|||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count); |
|||
|
|||
/// Returns true if the stream is currently playing |
|||
bool IsPlaying() const { |
|||
return state == State::Playing; |
|||
} |
|||
|
|||
/// Returns the number of queued buffers |
|||
size_t GetQueueSize() const { |
|||
return queued_buffers.size(); |
|||
} |
|||
|
|||
private: |
|||
/// Current state of the stream |
|||
enum class State { |
|||
Stopped, |
|||
Playing, |
|||
}; |
|||
|
|||
/// Plays the next queued buffer in the audio stream, starting playback if necessary |
|||
void PlayNextBuffer(); |
|||
|
|||
/// Releases the actively playing buffer, signalling that it has been completed |
|||
void ReleaseActiveBuffer(); |
|||
|
|||
/// Gets the number of core cycles when the specified buffer will be released |
|||
s64 GetBufferReleaseCycles(const Buffer& buffer) const; |
|||
|
|||
int sample_rate; ///< Sample rate of the stream |
|||
Format format; ///< Format of the stream |
|||
ReleaseCallback release_callback; ///< Buffer release callback for the stream |
|||
State state{State::Stopped}; ///< Playback state of the stream |
|||
CoreTiming::EventType* release_event{}; ///< Core timing release event for the stream |
|||
BufferPtr active_buffer; ///< Actively playing buffer in the stream |
|||
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream |
|||
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream |
|||
}; |
|||
|
|||
} // namespace AudioCore |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue