Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic audio output implementation #47

Merged
merged 1 commit into from
Aug 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
280 changes: 280 additions & 0 deletions orbis-kernel/include/orbis/AudioOut.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
#pragma once

#include <condition_variable>
#include <forward_list>
#include <functional>
#include <map>
#include <mutex>
#include <sys/ucontext.h>
#include <thread>
#include <ucontext.h>
#include <utility>
#include <cstdio>
#include "sys/sysproto.hpp"
#include <pthread.h>
#include <sox.h>

struct args {
int32_t audioPort;
int32_t idControl;
int32_t idAudio;
orbis::Thread *thread;
int32_t evfId;
};

int msleep(long msec)
{
struct timespec ts;
int res;

if (msec < 0)
{
errno = EINVAL;
return -1;
}

ts.tv_sec = msec / 1000;
ts.tv_nsec = (msec % 1000) * 1000000;

do {
res = nanosleep(&ts, &ts);
} while (res && errno == EINTR);

return res;
}

void DumpHex(const void* data, size_t size) {
char ascii[17];
size_t i, j;
ascii[16] = '\0';
for (i = 0; i < size; ++i) {
printf("%02X ", ((unsigned char*)data)[i]);
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
ascii[i % 16] = ((unsigned char*)data)[i];
} else {
ascii[i % 16] = '.';
}
if ((i+1) % 8 == 0 || i+1 == size) {
printf(" ");
if ((i+1) % 16 == 0) {
printf("| %s \n", ascii);
} else if (i+1 == size) {
ascii[(i+1) % 16] = '\0';
if ((i+1) % 16 <= 8) {
printf(" ");
}
for (j = (i+1) % 16; j < 16; ++j) {
printf(" ");
}
printf("| %s \n", ascii);
}
}
}
}

void * loop(void *vargp)
{
size_t control_shm_size = 0x10000;
size_t audio_shm_size = 65536;

char control_shm_name[32];
char audio_shm_name[32];

sprintf(control_shm_name, "/rpcsx-shm_%d_C", ((struct args*)vargp)->idControl);
sprintf(audio_shm_name, "/rpcsx-shm_%d_%d_A", ((struct args*)vargp)->idAudio, ((struct args*)vargp)->audioPort);

int controlFd = shm_open(control_shm_name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (controlFd == -1) {
perror("shm_open");
exit(EXIT_FAILURE);
}
void *controlPtr = mmap(NULL, control_shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, controlFd, 0);
if (controlPtr == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}

int audioFd = shm_open(audio_shm_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (audioFd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
void *audioPtr = mmap(NULL, audio_shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, audioFd, 0);

int64_t controlPtrWithOffset = (int64_t)(controlPtr + 8);

int32_t bitPattern = 1 << ((struct args*)vargp)->audioPort;

int firstNonEmptyByteIndex;

for (size_t i = 24; i < control_shm_size; ++i) {
if (*((char *)controlPtr + i) > 0) {
firstNonEmptyByteIndex = i - 8;
break;
}
}

int outParamFirstByte = *((char *)controlPtr + firstNonEmptyByteIndex + 8);
int isFloatByte = *((char *)controlPtr + firstNonEmptyByteIndex + 44);
// int outParamThirdByte = *((char *)controlPtr + firstNonEmptyByteIndex + 44); // need to find the third index
int in_channels = 2, in_samples = 256, sample_rate = 48000; // probably there is no point to parse frequency, because it's always 48000
if (outParamFirstByte == 2 && isFloatByte == 0) {
in_channels = 1;
printf("outputParam is ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO\n");
}
if (outParamFirstByte == 4 && isFloatByte == 0) {
in_channels = 2;
printf("outputParam is ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO\n");
}
if (outParamFirstByte == 16 && isFloatByte == 0) {
in_channels = 8;
printf("outputParam is ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH\n");
}
if (outParamFirstByte == 4 && isFloatByte == 1) {
in_channels = 1;
printf("outputParam is ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_MONO\n");
}
if (outParamFirstByte == 8 && isFloatByte == 1) {
in_channels = 2;
printf("outputParam is ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_STEREO\n");
}
if (outParamFirstByte == 32 && isFloatByte == 1) {
in_channels = 8;
printf("outputParam is ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH\n");
}
// // it's need third byte
// if (outParamFirstByte == 16 && outParamSecondByte == 0 && outParamThirdByte == 1) {
// printf("outputParam is ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD");
// }
// if (outParamFirstByte == 32 && outParamSecondByte == 1 && outParamThirdByte == 1) {
// printf("outputParam is ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD");
// }

// length byte will be inited after some time, so we wait for it
int samplesLengthByte;
while(true) {
samplesLengthByte = *((char *)controlPtr + firstNonEmptyByteIndex + 97);
if (samplesLengthByte > 0) {
break;
}
}

in_samples = samplesLengthByte * 256;

if (sox_init() != SOX_SUCCESS) {
exit(1);
}

sox_signalinfo_t out_si = {};
out_si.rate = sample_rate;
out_si.channels = in_channels;
out_si.precision = SOX_SAMPLE_PRECISION;

sox_format_t* output
= sox_open_write("default", &out_si, NULL, "alsa", NULL, NULL);
if (!output) {
exit(1);
}

sox_sample_t samples[in_samples * in_channels];

size_t clips = 0; SOX_SAMPLE_LOCALS;
size_t n_samples;
int size;
if (isFloatByte == 0) {
size = in_samples * in_channels * sizeof(int16_t);
n_samples = size / sizeof(int16_t);
} else if (isFloatByte == 1) {
size = in_samples * in_channels * sizeof(float);
n_samples = size / sizeof(float);
}
while(true) {
// skip sceAudioOutMix%x event
sys_evf_set(((struct args*)vargp)->thread, ((struct args*)vargp)->evfId, bitPattern);
// set zero to freeing audiooutput
for (size_t i = 0; i < 8; ++i) {
*((char *)controlPtr + firstNonEmptyByteIndex + i) = 0x00;
}

// DumpHex(audioPtr, 1000);
// sleep 1ms
msleep(1);
if (isFloatByte == 0) {
int16_t data[size];
memcpy(data, audioPtr, size);
for (size_t n = 0; n < n_samples; n++) {
samples[n] = SOX_SIGNED_16BIT_TO_SAMPLE(data[n], clips);
}
// free(data);
}
if (isFloatByte == 1) {
float data[size];
memcpy(data, audioPtr, size);
for (size_t n = 0; n < n_samples; n++) {
samples[n] = SOX_FLOAT_32BIT_TO_SAMPLE(data[n], clips);
}
// free(data);
}

if (sox_write(output, samples, n_samples) != n_samples) {
exit(1);
}
}
pthread_exit(NULL);
}

namespace orbis {
class AudioOut {
public:
int32_t audioPort;
int32_t idControl;
int32_t idAudio;
int32_t evfId;
AudioOut() {
}

~AudioOut() {
}

static AudioOut& getInstance() {
static AudioOut instance;
return instance;
}

void setPortId(int32_t port) {
this->audioPort = port;
}

void setControlId(int32_t id) {
this->idControl = id;
}

void setAudioId(int32_t id) {
this->idAudio = id;
}

void setEvfId(int32_t evfId) {
this->evfId = evfId;
}

void start(orbis::Thread *thread) {
Ref<File> file;
// probably need to close
auto result = thread->tproc->ops->open(thread, "/dev/audioHack", 0, 0, &file);
if (result.value() == 0) {
struct args *threadArgs = (struct args *)malloc(sizeof(struct args));
threadArgs->audioPort = this->audioPort;
threadArgs->idControl = this->idControl;
threadArgs->idAudio = this->idAudio;
threadArgs->thread = thread;
threadArgs->evfId = this->evfId;

pthread_t thread_id;
pthread_create(&thread_id, NULL, loop, (void *)threadArgs);
}
}
private:
AudioOut( const AudioOut&);
AudioOut& operator=( AudioOut& );
};
} // namespace orbis
101 changes: 101 additions & 0 deletions orbis-kernel/src/sys/sys_sce.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
#include "osem.hpp"
#include "sys/sysproto.hpp"
#include "utils/Logs.hpp"
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include "orbis/AudioOut.hpp"

orbis::SysResult orbis::sys_netcontrol(Thread *thread, sint fd, uint op,
ptr<void> buf, uint nbuf) {
Expand Down Expand Up @@ -1136,6 +1140,103 @@ orbis::SysResult orbis::sys_ipmimgr_call(Thread *thread, uint op, uint kid,
}
return uwrite<uint>(result, 0);
}
} else if (client->name == "SceSysAudioSystemIpc") {
if (syncCallParams.method == 0x12340000) { // check shared memory control
struct SceSysAudioSystemIpcCheckSharedMemoryControlMethodArgs {
uint32_t channelId;
};

static_assert(sizeof(SceSysAudioSystemIpcCheckSharedMemoryControlMethodArgs) == 0x4);

if (dataInfo.size != sizeof(SceSysAudioSystemIpcCheckSharedMemoryControlMethodArgs)) {
return ErrorCode::INVAL;
}

SceSysAudioSystemIpcCheckSharedMemoryControlMethodArgs args;
uread(args, ptr<SceSysAudioSystemIpcCheckSharedMemoryControlMethodArgs>(dataInfo.data));

ORBIS_LOG_TODO("impi: SceSysAudioSystemIpcCheckSharedMemoryControlMethodArgs", args.channelId);
AudioOut& ao = AudioOut::getInstance();
ao.setControlId(args.channelId);
}
if (syncCallParams.method == 0x1234000f) { // create event flag
struct SceSysAudioSystemIpcCreateEventFlagMethodArgs {
uint32_t channelId;
};

static_assert(sizeof(SceSysAudioSystemIpcCreateEventFlagMethodArgs) == 0x4);

if (dataInfo.size != sizeof(SceSysAudioSystemIpcCreateEventFlagMethodArgs)) {
return ErrorCode::INVAL;
}

SceSysAudioSystemIpcCreateEventFlagMethodArgs args;
uread(args, ptr<SceSysAudioSystemIpcCreateEventFlagMethodArgs>(dataInfo.data));

// very bad
char buffer[32];
sprintf(buffer, "sceAudioOutMix%x", args.channelId);
// const char* eventName = &buffer;
int32_t attrs = 0x100;
EventFlag *eventFlag;
if (attrs & kEvfAttrShared) {
auto [insertedEvf, inserted] =
thread->tproc->context->createEventFlag(buffer, attrs, 0);

if (!inserted) {
return ErrorCode::EXIST; // FIXME: verify
}

eventFlag = insertedEvf;
} else {
eventFlag = knew<EventFlag>(attrs, 0);
std::strncpy(eventFlag->name, buffer, 32);
}

int32_t audioOutMixId = thread->tproc->evfMap.insert(eventFlag);
AudioOut& ao = AudioOut::getInstance();
ao.setEvfId(audioOutMixId);
}
if (syncCallParams.method == 0x12340001) { // check shared memory audio
struct SceSysAudioSystemIpcCheckSharedMemoryAudioMethodArgs {
uint32_t audioPort;
uint32_t channelId;
};

static_assert(sizeof(SceSysAudioSystemIpcCheckSharedMemoryAudioMethodArgs) == 0x8);

if (dataInfo.size != sizeof(SceSysAudioSystemIpcCheckSharedMemoryAudioMethodArgs)) {
return ErrorCode::INVAL;
}

SceSysAudioSystemIpcCheckSharedMemoryAudioMethodArgs args;
uread(args, ptr<SceSysAudioSystemIpcCheckSharedMemoryAudioMethodArgs>(dataInfo.data));

ORBIS_LOG_TODO("impi: SceSysAudioSystemIpcCheckSharedMemoryAudioMethodArgs", args.audioPort, args.channelId);
AudioOut& ao = AudioOut::getInstance();
ao.setPortId(args.audioPort);
ao.setAudioId(args.channelId);
}
if (syncCallParams.method == 0x12340002) { // something something open
struct SceSysAudioSystemIpcSomethingMethodArgs {
uint32_t arg1;
uint32_t arg2;
};

static_assert(sizeof(SceSysAudioSystemIpcSomethingMethodArgs) == 0x8);

if (dataInfo.size != sizeof(SceSysAudioSystemIpcSomethingMethodArgs)) {
return ErrorCode::INVAL;
}

SceSysAudioSystemIpcSomethingMethodArgs args;
uread(args, ptr<SceSysAudioSystemIpcSomethingMethodArgs>(dataInfo.data));

ORBIS_LOG_TODO("impi: SceSysAudioSystemIpcSomethingMethodArgs", args.arg1, args.arg2);
// here startToListen
AudioOut& ao = AudioOut::getInstance();
ao.start(thread);
}
}

if (result != nullptr) {
Expand Down
Loading
Loading