Skip to content

Commit

Permalink
rpcsx-os: aout playback support
Browse files Browse the repository at this point in the history
ga2mer committed Oct 3, 2024
1 parent 1a3d783 commit 3c1620c
Showing 8 changed files with 547 additions and 23 deletions.
6 changes: 3 additions & 3 deletions .github/BUILDING.md
Original file line number Diff line number Diff line change
@@ -4,20 +4,20 @@
### The dependencies for Debian-like distributions.

```
sudo apt install build-essential cmake libunwind-dev libglfw3-dev libvulkan-dev vulkan-validationlayers-dev spirv-tools glslang-tools libspirv-cross-c-shared-dev libsox-dev git
sudo apt install build-essential cmake libunwind-dev libglfw3-dev libvulkan-dev vulkan-validationlayers-dev spirv-tools glslang-tools libspirv-cross-c-shared-dev libsox-dev git libasound2-dev
```
# git is only needed for ubuntu 22.04

### The dependencies for Fedora distributions:

```
sudo dnf install cmake libunwind-devel glfw-devel vulkan-devel vulkan-validation-layers-devel spirv-tools glslang-devel gcc-c++ gcc spirv-tools-devel xbyak-devel sox-devel
sudo dnf install cmake libunwind-devel glfw-devel vulkan-devel vulkan-validation-layers-devel spirv-tools glslang-devel gcc-c++ gcc spirv-tools-devel xbyak-devel sox-devel alsa-lib-devel
```

### The dependencies for Arch distributions:

```
sudo pacman -S libunwind glfw-x11 vulkan-devel sox glslang git cmake
sudo pacman -S libunwind glfw-x11 vulkan-devel sox glslang git cmake alsa-lib
```
> Side note you will need to pull ``spirv-cross`` from the AUR for now so do the following
```
2 changes: 1 addition & 1 deletion .github/workflows/rpcsx.yml
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ jobs:
sudo apt update
sudo apt install -y cmake build-essential libunwind-dev \
libglfw3-dev libvulkan-dev vulkan-validationlayers \
libsox-dev
libsox-dev libasound2-dev
echo "deb http://azure.archive.ubuntu.com/ubuntu noble main universe" | sudo tee /etc/apt/sources.list
sudo apt update
sudo apt install g++-14 ninja-build
5 changes: 4 additions & 1 deletion rpcsx-os/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -6,6 +6,9 @@ target_include_directories(standalone-config INTERFACE orbis-kernel-config)
add_library(orbis::kernel::config ALIAS standalone-config)

add_executable(rpcsx-os
audio/AudioDevice.cpp
audio/AlsaDevice.cpp

iodev/ajm.cpp
iodev/blockpool.cpp
iodev/bt.cpp
@@ -66,7 +69,7 @@ add_executable(rpcsx-os
)

target_include_directories(rpcsx-os PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(rpcsx-os PUBLIC orbis::kernel amdgpu::bridge rx libcrypto libunwind::unwind-x86_64 xbyak::xbyak sox::sox)
target_link_libraries(rpcsx-os PUBLIC orbis::kernel amdgpu::bridge rx libcrypto libunwind::unwind-x86_64 xbyak::xbyak sox::sox asound)
target_base_address(rpcsx-os 0x0000010000000000)
target_compile_options(rpcsx-os PRIVATE "-mfsgsbase")

242 changes: 242 additions & 0 deletions rpcsx-os/audio/AlsaDevice.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
#include "AlsaDevice.hpp"
#include "orbis/utils/Logs.hpp"
#include "rx/hexdump.hpp"

AlsaDevice::AlsaDevice() {}

void AlsaDevice::start() {
setAlsaFormat();
int err;
if ((err = snd_pcm_open(&mPCMHandle, "default", SND_PCM_STREAM_PLAYBACK,
0)) < 0) {
ORBIS_LOG_FATAL("Cannot open audio device", snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_hw_params_malloc(&mHWParams)) < 0) {
ORBIS_LOG_FATAL("Cannot allocate hardware parameter structure",
snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_hw_params_any(mPCMHandle, mHWParams)) < 0) {
ORBIS_LOG_FATAL("Cannot initialize hardware parameter structure",
snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_hw_params_set_rate_resample(mPCMHandle, mHWParams,
0)) < 0) {
ORBIS_LOG_FATAL("Cannot disable rate resampling", snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_hw_params_set_access(mPCMHandle, mHWParams,
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
ORBIS_LOG_FATAL("Cannot set access type", snd_strerror(err));
std::abort();
}
if ((err = snd_pcm_hw_params_set_format(mPCMHandle, mHWParams,
mAlsaFormat)) < 0) {
ORBIS_LOG_FATAL("Cannot set sample format", snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_hw_params_set_rate(mPCMHandle, mHWParams, mFrequency,
0)) < 0) {
ORBIS_LOG_FATAL("Cannot set sample rate", snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_hw_params_set_channels(mPCMHandle, mHWParams, mChannels)) <
0) {
ORBIS_LOG_FATAL("cannot set channel count", snd_strerror(err), mChannels);
std::abort();
}

uint periods = mSampleCount;
if ((err = snd_pcm_hw_params_set_periods_max(mPCMHandle, mHWParams, &periods, NULL)) < 0) {
ORBIS_LOG_FATAL("Cannot set periods count", snd_strerror(err));
std::abort();
}

int frameBytes = snd_pcm_format_physical_width(mAlsaFormat) * mChannels / 8;

snd_pcm_uframes_t size = mSampleSize / frameBytes;

// TODO: it shouldn't work like this

if ((err = snd_pcm_hw_params_set_buffer_size(mPCMHandle, mHWParams, size)) < 0) {
ORBIS_LOG_FATAL("Cannot set buffer size", snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_hw_params_set_period_size(mPCMHandle, mHWParams, size / 2, 0)) < 0) {
ORBIS_LOG_FATAL("Cannot set period size", snd_strerror(err));
std::abort();
}

snd_pcm_uframes_t periodSize;
if ((err = snd_pcm_hw_params_get_period_size(mHWParams, &periodSize, NULL)) < 0) {
ORBIS_LOG_FATAL("cannot set parameters", snd_strerror(err));
std::abort();
}

snd_pcm_uframes_t bufferSize;
if ((err = snd_pcm_hw_params_get_buffer_size(mHWParams, &bufferSize)) < 0) {
ORBIS_LOG_FATAL("cannot set parameters", snd_strerror(err));
std::abort();
}

ORBIS_LOG_TODO("period and buffer", periodSize, bufferSize);

if ((err = snd_pcm_hw_params(mPCMHandle, mHWParams)) < 0) {
ORBIS_LOG_FATAL("cannot set parameters", snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_sw_params_malloc(&mSWParams)) < 0) {
ORBIS_LOG_FATAL("Cannot allocate software parameter structure",
snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_sw_params_current(mPCMHandle, mSWParams)) < 0) {
ORBIS_LOG_FATAL("cannot sw params current", snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_sw_params_set_start_threshold(mPCMHandle, mSWParams, periodSize)) < 0) {
ORBIS_LOG_FATAL("cannot set start threshold", snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_sw_params_set_stop_threshold(mPCMHandle, mSWParams, bufferSize)) < 0) {
ORBIS_LOG_FATAL("cannot set stop threshold", snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_sw_params(mPCMHandle, mSWParams)) < 0) {
ORBIS_LOG_FATAL("cannot set parameters", snd_strerror(err));
std::abort();
}

if ((err = snd_pcm_prepare(mPCMHandle)) < 0) {
ORBIS_LOG_FATAL("cannot prepare audio interface for use",
snd_strerror(err));
std::abort();
}
mWorking = true;
}

int AlsaDevice::fixXRun()
{
switch (snd_pcm_state(mPCMHandle)) {
case SND_PCM_STATE_XRUN:
return snd_pcm_prepare(mPCMHandle);
case SND_PCM_STATE_DRAINING:
if (snd_pcm_stream(mPCMHandle) == SND_PCM_STREAM_CAPTURE)
return snd_pcm_prepare(mPCMHandle);
break;
default:
break;
}
return -EIO;
}

int AlsaDevice::resumeFromSupsend()
{
int res;
while ((res = snd_pcm_resume(mPCMHandle)) == -EAGAIN)
std::this_thread::sleep_for(std::chrono::seconds(1));
if (!res)
return 0;
return snd_pcm_prepare(mPCMHandle);
}

long AlsaDevice::write(void *buf, long len) {
if (!mWorking) return 0;
ssize_t r;
int frameBytes = snd_pcm_format_physical_width(mAlsaFormat) * mChannels / 8;
snd_pcm_uframes_t frames = len / frameBytes;

r = snd_pcm_writei(mPCMHandle, buf, frames);
if (r == -EPIPE) {
if (!(r = fixXRun()))
return write(buf, len);
} else if (r == -ESTRPIPE) {
if (!(r = resumeFromSupsend()))
return write(buf, len);
}
r *= frameBytes;
return r;
}

void AlsaDevice::stop() {
snd_pcm_hw_params_free(mHWParams);
snd_pcm_sw_params_free(mSWParams);
snd_pcm_drain(mPCMHandle);
snd_pcm_drop(mPCMHandle);
mWorking = false;
}

void AlsaDevice::reset() {
if (!mWorking) return;
int err;
err = snd_pcm_drop(mPCMHandle);
if (err >= 0)
err = snd_pcm_prepare(mPCMHandle);
if (err < 0)
err = err;
}

audio_buf_info AlsaDevice::getOSpace() {
int err;
snd_pcm_uframes_t periodSize;
if ((err = snd_pcm_hw_params_get_period_size(mHWParams, &periodSize, NULL)) < 0) {
ORBIS_LOG_FATAL("cannot get period size", snd_strerror(err));
std::abort();
}

snd_pcm_uframes_t bufferSize;
if ((err = snd_pcm_hw_params_get_buffer_size(mHWParams, &bufferSize)) < 0) {
ORBIS_LOG_FATAL("cannot get buffer size", snd_strerror(err));
std::abort();
}
int frameBytes = snd_pcm_format_physical_width(mAlsaFormat) * mChannels / 8;

snd_pcm_sframes_t avail, delay;
audio_buf_info info;
avail = snd_pcm_avail_update(mPCMHandle);
if (avail < 0 || (snd_pcm_uframes_t)avail > bufferSize)
avail = bufferSize;
info.fragsize = periodSize * frameBytes;
info.fragstotal = mSampleCount;
info.bytes = avail * frameBytes;
info.fragments = avail / periodSize;
return info;
}

void AlsaDevice::setAlsaFormat() {
if (mWorking)
return;
_snd_pcm_format fmt;
switch (mFormat) {
case FMT_S32_LE:
fmt = SND_PCM_FORMAT_S32_LE;
break;
case FMT_S16_LE:
fmt = SND_PCM_FORMAT_S16_LE;
break;
case FMT_AC3:
default:
ORBIS_LOG_FATAL("Format is not supported", mFormat);
std::abort();
break;
}
mAlsaFormat = fmt;
}

AlsaDevice::~AlsaDevice() {
stop();
}
31 changes: 31 additions & 0 deletions rpcsx-os/audio/AlsaDevice.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include "AudioDevice.hpp"
#include <alsa/asoundlib.h>
#include <cstdlib>
#include <thread>

class AlsaDevice : public AudioDevice {
private:
snd_pcm_format_t mAlsaFormat;
snd_pcm_t *mPCMHandle;
snd_pcm_hw_params_t *mHWParams;
snd_pcm_sw_params_t *mSWParams;

public:
AlsaDevice();
~AlsaDevice() override;

void init() override {};
void start() override;
long write(void *, long) override;
void stop() override;
void reset() override;

void setAlsaFormat();

int fixXRun();
int resumeFromSupsend();

audio_buf_info getOSpace() override;
};
55 changes: 55 additions & 0 deletions rpcsx-os/audio/AudioDevice.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "AudioDevice.hpp"
#include "orbis/utils/Logs.hpp"
#include "rx/hexdump.hpp"

AudioDevice::AudioDevice() {}

void AudioDevice::init() {}

void AudioDevice::start() {}

long AudioDevice::write(void *buf, long len) {
return -1;
}

void AudioDevice::stop() {
}

void AudioDevice::reset() {}

void AudioDevice::setFormat(orbis::uint format) {
if (mWorking)
return;
mFormat = format;
}

void AudioDevice::setFrequency(orbis::uint frequency) {
if (mWorking)
return;
mFrequency = frequency;
}

void AudioDevice::setChannels(orbis::ushort channels) {
if (mWorking)
return;
if (channels > 8) {
ORBIS_LOG_FATAL("Channels count is not supported", channels);
std::abort();
}
mChannels = channels;
}

void AudioDevice::setSampleSize(orbis::uint sampleSize, orbis::uint sampleCount) {
if (mWorking)
return;
mSampleSize = sampleSize;
mSampleCount = sampleCount;
}

audio_buf_info AudioDevice::getOSpace() {
audio_buf_info info;
return info;
}


AudioDevice::~AudioDevice() {}
Loading

0 comments on commit 3c1620c

Please sign in to comment.