This commit is contained in:
auser1337 2024-10-18 20:40:17 -07:00
parent 8d6afc98a8
commit d9d53c7a38
11 changed files with 214 additions and 164 deletions

View File

@ -54,7 +54,7 @@ file(GLOB LIBATRAC9_SOURCES
) )
add_library(LibAtrac9 STATIC ${LIBATRAC9_SOURCES}) add_library(LibAtrac9 STATIC ${LIBATRAC9_SOURCES})
target_include_directories(LibAtrac9 PUBLIC LibAtrac9/C/src) target_include_directories(LibAtrac9 INTERFACE LibAtrac9/C/src)
# Zlib-Ng # Zlib-Ng
if (NOT TARGET zlib-ng::zlib) if (NOT TARGET zlib-ng::zlib)

View File

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off #pragma clang optimize off
#include <future>
#include <numeric> #include <numeric>
#include <magic_enum.hpp> #include <magic_enum.hpp>
#include "ajm_at9.h" #include "ajm_at9.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/debug.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/ajm/ajm.h" #include "core/libraries/ajm/ajm.h"
#include "core/libraries/ajm/ajm_error.h" #include "core/libraries/ajm/ajm_error.h"
@ -16,7 +16,6 @@
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
extern "C" { extern "C" {
#include <error_codes.h>
#include <libatrac9.h> #include <libatrac9.h>
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavutil/opt.h> #include <libavutil/opt.h>
@ -28,18 +27,34 @@ namespace Libraries::Ajm {
static constexpr u32 AJM_INSTANCE_STATISTICS = 0x80000; static constexpr u32 AJM_INSTANCE_STATISTICS = 0x80000;
static constexpr u32 SCE_AJM_WAIT_INFINITE = -1;
static constexpr u32 MaxInstances = 0x2fff; static constexpr u32 MaxInstances = 0x2fff;
static constexpr u32 MaxBatches = 1000;
struct BatchInfo {
u16 instance{};
u16 offset_in_qwords{}; // Needed for AjmBatchError?
bool waiting{};
bool finished{};
std::mutex mtx;
std::condition_variable cv;
int result{};
};
struct AjmDevice { struct AjmDevice {
u32 max_prio; u32 max_prio{};
u32 min_prio; u32 min_prio{};
u32 curr_cursor{}; u32 curr_cursor{};
u32 release_cursor{MaxInstances - 1}; u32 release_cursor{MaxInstances - 1};
std::array<bool, NumAjmCodecs> is_registered{}; std::array<bool, NumAjmCodecs> is_registered{};
std::array<u32, MaxInstances> free_instances{}; std::array<u32, MaxInstances> free_instances{};
std::array<std::unique_ptr<AjmAt9Decoder>, MaxInstances> instances; std::array<std::unique_ptr<AjmInstance>, MaxInstances> instances;
std::vector<std::shared_ptr<BatchInfo>> batches{};
std::mutex batches_mutex;
bool IsRegistered(AjmCodecType type) const { [[nodiscard]] bool IsRegistered(AjmCodecType type) const {
return is_registered[static_cast<u32>(type)]; return is_registered[static_cast<u32>(type)];
} }
@ -86,11 +101,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobControlBufferRa(AjmSingleJob* batch_pos, u32 in
batch_pos->opcode.is_statistic = instance == AJM_INSTANCE_STATISTICS; batch_pos->opcode.is_statistic = instance == AJM_INSTANCE_STATISTICS;
batch_pos->opcode.is_control = true; batch_pos->opcode.is_control = true;
if (instance == AJM_INSTANCE_STATISTICS) { AjmInOutJob* job;
BREAKPOINT();
}
AjmInOutJob* job = nullptr;
if (ret_addr == nullptr) { if (ret_addr == nullptr) {
batch_pos->job_size = sizeof(AjmInOutJob); batch_pos->job_size = sizeof(AjmInOutJob);
job = &batch_pos->job; job = &batch_pos->job;
@ -121,45 +132,10 @@ void* PS4_SYSV_ABI sceAjmBatchJobInlineBuffer(AjmSingleJob* batch_pos, const voi
return nullptr; return nullptr;
} }
auto ParseWavHeader = [](void* buf, WavHeader* header) {
if (!buf) {
// buf is passed as nullptr in some cases (i.e. GetCodecInfo)
return;
}
std::memcpy(header, buf, sizeof(WavHeader));
std::string riff(header->RIFF, 4);
std::string wave(header->WAVE, 4);
std::string fmt(header->fmt, 4);
std::string dataID(header->Subchunk2ID, 4);
if (std::memcmp(header->RIFF, "RIFF", 4) != 0 || std::memcmp(header->WAVE, "WAVE", 4) != 0 ||
std::memcmp(header->fmt, "fmt ", 4) != 0 ||
std::memcmp(header->Subchunk2ID, "data", 4) != 0) {
LOG_ERROR(Lib_Ajm, "Invalid WAV file.");
return;
}
LOG_INFO(Lib_Ajm, "RIFF header: {}", riff);
LOG_INFO(Lib_Ajm, "WAVE header: {}", wave);
LOG_INFO(Lib_Ajm, "FMT: {}", fmt);
LOG_INFO(Lib_Ajm, "Data size: {}", header->ChunkSize);
LOG_INFO(Lib_Ajm, "Sampling Rate: {}", header->SamplesPerSec);
LOG_INFO(Lib_Ajm, "Number of bits used: {}", header->bitsPerSample);
LOG_INFO(Lib_Ajm, "Number of channels: {}", header->NumOfChan);
LOG_INFO(Lib_Ajm, "Number of bytes per second: {}", header->bytesPerSec);
LOG_INFO(Lib_Ajm, "Data length: {}", header->Subchunk2Size);
LOG_INFO(Lib_Ajm, "Audio Format: {}", header->AudioFormat);
LOG_INFO(Lib_Ajm, "Block align: {}", header->blockAlign);
LOG_INFO(Lib_Ajm, "Data string: {}", dataID);
};
void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instance, AjmFlags flags, void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instance, AjmFlags flags,
void* in_buffer, u32 in_size, u8* out_buffer, u8* in_buffer, u32 in_size, u8* out_buffer,
const u32 out_size, u8* sideband_output, const u32 out_size, u8* sideband_output,
const u32 sideband_output_size, const void* ret_addr) { const u32 sideband_output_size, const void* ret_addr) {
WavHeader header{};
ParseWavHeader(in_buffer, &header);
LOG_INFO(Lib_Ajm, LOG_INFO(Lib_Ajm,
"called instance = {:#x}, flags = {:#x}, cmd = {}, in_size = {:#x}, out_size = {:#x}, " "called instance = {:#x}, flags = {:#x}, cmd = {}, in_size = {:#x}, out_size = {:#x}, "
"ret_addr = {}", "ret_addr = {}",
@ -178,7 +154,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instan
batch_pos->opcode.is_statistic = false; batch_pos->opcode.is_statistic = false;
batch_pos->opcode.is_control = false; batch_pos->opcode.is_control = false;
AjmInOutJob* job = nullptr; AjmInOutJob* job;
if (ret_addr == nullptr) { if (ret_addr == nullptr) {
batch_pos->job_size = sizeof(AjmInOutJob) + 16; batch_pos->job_size = sizeof(AjmInOutJob) + 16;
job = &batch_pos->job; job = &batch_pos->job;
@ -188,16 +164,17 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instan
job = &batch_pos->ret.job; job = &batch_pos->ret.job;
} }
// todo: add some missing stuff // TODO: Check if all the fields are being set, might be missing some
job->input.buf_size = in_size; job->input.buf_size = in_size;
job->input.buffer = static_cast<u8*>(in_buffer); job->input.buffer = in_buffer;
job->flags = u32(flags.raw); job->flags = u32(flags.raw);
job->unk1 = (job->unk1 & 0xfc000030) + (flags.raw >> 0x1a) + 4; job->unk1 = (job->unk1 & 0xfc000030) + (flags.raw >> 0x1a) + 4;
job->output.buf_size = out_size; job->output.buf_size = out_size;
job->output.buffer = out_buffer; job->output.buffer = out_buffer;
job->output.props &= 0xffffffe0; job->output.props &= 0xffffffe0;
job->output.props |= 0x12; job->output.props |= 0x12;
*reinterpret_cast<u8**>(reinterpret_cast<u8*>(job) + 32) = sideband_output; // *reinterpret_cast<u8**>(reinterpret_cast<u8*>(job) + 32) = sideband_output;
job->sideband_output = sideband_output;
return job; return job;
} }
@ -213,6 +190,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in
instance, flags.raw, magic_enum::enum_name(AjmJobRunFlags(flags.command)), instance, flags.raw, magic_enum::enum_name(AjmJobRunFlags(flags.command)),
magic_enum::enum_name(AjmJobSidebandFlags(flags.sideband)), num_in_buffers, magic_enum::enum_name(AjmJobSidebandFlags(flags.sideband)), num_in_buffers,
num_out_buffers, fmt::ptr(ret_addr)); num_out_buffers, fmt::ptr(ret_addr));
const u32 job_size = (num_in_buffers * 2 + 1 + num_out_buffers * 2) * 8; const u32 job_size = (num_in_buffers * 2 + 1 + num_out_buffers * 2) * 8;
const bool is_debug = ret_addr != nullptr; const bool is_debug = ret_addr != nullptr;
batch_pos->opcode.instance = instance; batch_pos->opcode.instance = instance;
@ -223,7 +201,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in
batch_pos->opcode.is_statistic = false; batch_pos->opcode.is_statistic = false;
batch_pos->opcode.is_control = false; batch_pos->opcode.is_control = false;
u32* job = nullptr; u32* job;
if (!is_debug) { if (!is_debug) {
batch_pos->job_size = job_size + 16; batch_pos->job_size = job_size + 16;
job = batch_pos->job; job = batch_pos->job;
@ -237,7 +215,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in
} }
for (s32 i = 0; i < num_in_buffers; i++) { for (s32 i = 0; i < num_in_buffers; i++) {
AjmJobBuffer* in_buf = reinterpret_cast<AjmJobBuffer*>(job); auto* in_buf = reinterpret_cast<AjmJobBuffer*>(job);
in_buf->props &= 0xffffffe0; in_buf->props &= 0xffffffe0;
in_buf->props |= 1; in_buf->props |= 1;
in_buf->buf_size = in_buffers[i].size; in_buf->buf_size = in_buffers[i].size;
@ -250,7 +228,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in
job += 2; job += 2;
for (s32 i = 0; i < num_out_buffers; i++) { for (s32 i = 0; i < num_out_buffers; i++) {
AjmJobBuffer* out_buf = reinterpret_cast<AjmJobBuffer*>(job); auto* out_buf = reinterpret_cast<AjmJobBuffer*>(job);
out_buf->props &= 0xffffffe0; out_buf->props &= 0xffffffe0;
out_buf->props |= 0x11; out_buf->props |= 0x11;
out_buf->buf_size = out_buffers[i].size; out_buf->buf_size = out_buffers[i].size;
@ -273,23 +251,23 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_
return ORBIS_AJM_ERROR_MALFORMED_BATCH; return ORBIS_AJM_ERROR_MALFORMED_BATCH;
} }
static constexpr u32 MaxBatches = 1000; const auto batch_info = std::make_shared<BatchInfo>();
struct BatchInfo { {
u16 instance; if (dev->batches.size() >= MaxBatches) {
u16 offset_in_qwords;
};
std::array<BatchInfo, MaxBatches> batches{};
u32 num_batches = 0;
const u8* batch_ptr = batch;
const u8* batch_end = batch + batch_size;
while (batch_ptr < batch_end) {
if (num_batches >= MaxBatches) {
LOG_ERROR(Lib_Ajm, "Too many batches in job!"); LOG_ERROR(Lib_Ajm, "Too many batches in job!");
return ORBIS_AJM_ERROR_OUT_OF_MEMORY; return ORBIS_AJM_ERROR_OUT_OF_MEMORY;
} }
AjmJobHeader header;
*out_batch_id = static_cast<u32>(dev->batches.size());
dev->batches.push_back(batch_info);
}
const u8* batch_ptr = batch;
const u8* batch_end = batch + batch_size;
AjmJobHeader header{};
while (batch_ptr < batch_end) {
std::memcpy(&header, batch_ptr, sizeof(u64)); std::memcpy(&header, batch_ptr, sizeof(u64));
const auto& opcode = header.opcode; const auto& opcode = header.opcode;
@ -297,34 +275,57 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_
const u8* job_ptr = batch_ptr + sizeof(AjmJobHeader) + opcode.is_debug * 16; const u8* job_ptr = batch_ptr + sizeof(AjmJobHeader) + opcode.is_debug * 16;
if (opcode.is_control) { if (opcode.is_control) {
// ASSERT_MSG(!opcode.is_statistic, "Statistic instance is not handled");
const auto command = AjmJobControlFlags(opcode.command_flags); const auto command = AjmJobControlFlags(opcode.command_flags);
switch (command) { switch (command) {
case AjmJobControlFlags::Reset: { case AjmJobControlFlags::Reset: {
LOG_INFO(Lib_Ajm, "Resetting instance {}", opcode.instance); LOG_INFO(Lib_Ajm, "Resetting instance {}", instance);
dev->instances[opcode.instance]->Reset(); dev->instances[instance]->Reset();
break; break;
} }
case (AjmJobControlFlags::Initialize | AjmJobControlFlags::Reset): case AjmJobControlFlags::Initialize:
LOG_INFO(Lib_Ajm, "Initializing instance {}", opcode.instance); LOG_INFO(Lib_Ajm, "Initializing instance {}", instance);
if (dev->instances[instance]->codec_type == AjmCodecType::At9Dec) {
const auto at9_instance =
dynamic_cast<AjmAt9Decoder*>(dev->instances[instance].get());
const auto in_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr);
std::memcpy(
at9_instance->config_data,
reinterpret_cast<const SceAjmDecAt9InitializeParameters*>(in_buffer->buffer)
->config_data,
SCE_AT9_CONFIG_DATA_SIZE);
LOG_INFO(
Lib_Ajm, "Initialize params: {}, config_data: {}, reserved: {}",
fmt::ptr(reinterpret_cast<const SceAjmDecAt9InitializeParameters*>(
in_buffer->buffer)),
fmt::ptr(reinterpret_cast<const SceAjmDecAt9InitializeParameters*>(
in_buffer->buffer)
->config_data),
reinterpret_cast<const SceAjmDecAt9InitializeParameters*>(in_buffer->buffer)
->reserved);
}
break; break;
case AjmJobControlFlags::Resample: case AjmJobControlFlags::Resample:
LOG_INFO(Lib_Ajm, "Set resample params of instance {}", opcode.instance); LOG_INFO(Lib_Ajm, "Set resample params of instance {}", instance);
break;
case AjmJobControlFlags::StatisticsEngine:
ASSERT_MSG(instance == AJM_INSTANCE_STATISTICS,
"Expected AJM_INSTANCE_STATISTICS for StatisticsEngine command");
LOG_TRACE(Lib_Ajm, "StatisticsEngine flag not implemented");
break; break;
default: default:
break; break;
} }
// Write sideband structures. // Write sideband structures.
const AjmJobBuffer* out_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr + 24); const auto* out_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr + 24);
auto* result = reinterpret_cast<AjmSidebandResult*>(out_buffer->buffer); auto* result = reinterpret_cast<AjmSidebandResult*>(out_buffer->buffer);
result->result = 0; result->result = 0;
result->internal_result = 0; result->internal_result = 0;
} else { } else {
const auto command = AjmJobRunFlags(opcode.command_flags); const auto command = AjmJobRunFlags(opcode.command_flags);
const auto sideband = AjmJobSidebandFlags(opcode.sideband_flags); const auto sideband = AjmJobSidebandFlags(opcode.sideband_flags);
const AjmJobBuffer* in_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr); const auto* in_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr);
const AjmJobBuffer* out_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr + 24); const auto* out_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr + 24);
job_ptr += 24; job_ptr += 24;
LOG_INFO(Lib_Ajm, "Decode job cmd = {}, sideband = {}, in_addr = {}, in_size = {}", LOG_INFO(Lib_Ajm, "Decode job cmd = {}, sideband = {}, in_addr = {}, in_size = {}",
@ -332,7 +333,7 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_
fmt::ptr(in_buffer->buffer), in_buffer->buf_size); fmt::ptr(in_buffer->buffer), in_buffer->buf_size);
// Decode as much of the input bitstream as possible. // Decode as much of the input bitstream as possible.
AjmAt9Decoder* decoder_instance = dev->instances[opcode.instance].get(); AjmInstance* decoder_instance = dev->instances[instance].get();
const auto [in_remain, out_remain, num_frames] = decoder_instance->Decode( const auto [in_remain, out_remain, num_frames] = decoder_instance->Decode(
in_buffer->buffer, in_buffer->buf_size, out_buffer->buffer, out_buffer->buf_size); in_buffer->buffer, in_buffer->buf_size, out_buffer->buffer, out_buffer->buf_size);
@ -357,27 +358,81 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_
sideband_ptr += sizeof(AjmSidebandMFrame); sideband_ptr += sizeof(AjmSidebandMFrame);
} }
if (True(command & AjmJobRunFlags::GetCodecInfo)) { if (True(command & AjmJobRunFlags::GetCodecInfo)) {
LOG_TRACE(Lib_Ajm, "GetCodecInfo called, supplying dummy info for now"); // TODO: Implement this for MP3
auto* codec_info = reinterpret_cast<SceAjmSidebandDecAt9CodecInfo*>(sideband_ptr);
codec_info->uiFrameSamples = 0; if (decoder_instance->codec_type == AjmCodecType::At9Dec) {
codec_info->uiFramesInSuperFrame = 0; const auto at9_decoder_instance =
codec_info->uiNextFrameSize = 0; dynamic_cast<AjmAt9Decoder*>(decoder_instance);
codec_info->uiSuperFrameSize = 0;
Atrac9CodecInfo decoder_codec_info;
Atrac9GetCodecInfo(at9_decoder_instance->handle, &decoder_codec_info);
auto* codec_info =
reinterpret_cast<SceAjmSidebandDecAt9CodecInfo*>(sideband_ptr);
codec_info->uiFrameSamples = decoder_codec_info.frameSamples;
codec_info->uiFramesInSuperFrame = decoder_codec_info.framesInSuperframe;
codec_info->uiNextFrameSize =
static_cast<Atrac9Handle*>(at9_decoder_instance->handle)->Config.FrameBytes;
codec_info->uiSuperFrameSize = decoder_codec_info.superframeSize;
sideband_ptr += sizeof(SceAjmSidebandDecAt9CodecInfo); sideband_ptr += sizeof(SceAjmSidebandDecAt9CodecInfo);
} }
} }
}
batch_ptr += sizeof(AjmJobHeader) + header.job_size; batch_ptr += sizeof(AjmJobHeader) + header.job_size;
} }
static int batch_id = 0;
*out_batch_id = ++batch_id; batch_info->finished = true;
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceAjmBatchWait() { // useless for now because batch processing isn't asynchronous
LOG_ERROR(Lib_Ajm, "(STUBBED) called"); int PS4_SYSV_ABI sceAjmBatchWait(const u32 context, const u32 batch_id, const u32 timeout,
return ORBIS_OK; AjmBatchError* const batch_error) {
LOG_INFO(Lib_Ajm, "called context = {}, batch_id = {}, timeout = {}", context, batch_id,
timeout);
if (batch_id > 0xFF) {
return ORBIS_AJM_ERROR_INVALID_BATCH;
}
if (batch_id >= dev->batches.size()) {
return ORBIS_AJM_ERROR_INVALID_BATCH;
}
const std::shared_ptr<BatchInfo> batch = dev->batches[batch_id];
if (batch->waiting) {
return ORBIS_AJM_ERROR_BUSY;
}
batch->waiting = true;
if (timeout > 0 && timeout != SCE_AJM_WAIT_INFINITE) {
std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
if (!batch->finished) {
batch->waiting = false;
return ORBIS_AJM_ERROR_IN_PROGRESS;
}
}
if (timeout == SCE_AJM_WAIT_INFINITE) {
while (!batch->finished) {
std::this_thread::yield();
}
}
// timeout == 0 = non-blocking poll
if (!batch->finished) {
batch->waiting = false;
return ORBIS_AJM_ERROR_IN_PROGRESS;
}
dev->batches.erase(dev->batches.begin() + batch_id);
return 0;
} }
int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData() { int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData() {
@ -433,16 +488,16 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context, AjmCodecType codec_type, AjmI
} }
const u32 index = dev->free_instances[dev->curr_cursor++]; const u32 index = dev->free_instances[dev->curr_cursor++];
dev->curr_cursor %= MaxInstances; dev->curr_cursor %= MaxInstances;
std::unique_ptr<AjmAt9Decoder> instance; std::unique_ptr<AjmInstance> instance;
switch (codec_type) { switch (codec_type) {
case AjmCodecType::Mp3Dec: case AjmCodecType::Mp3Dec:
instance = std::make_unique<AjmAt9Decoder>(); instance = std::make_unique<AjmMp3Decoder>();
break; break;
case AjmCodecType::At9Dec: case AjmCodecType::At9Dec:
instance = std::make_unique<AjmAt9Decoder>(); instance = std::make_unique<AjmAt9Decoder>();
break; break;
default: default:
break; UNREACHABLE_MSG("Codec #{} not implemented", u32(codec_type));
} }
instance->index = index; instance->index = index;
instance->codec_type = codec_type; instance->codec_type = codec_type;

View File

@ -41,12 +41,14 @@ struct AjmInOutJob {
u32 unk1; u32 unk1;
u32 flags; u32 flags;
AjmJobBuffer output; AjmJobBuffer output;
void* sideband_output;
}; };
enum class AjmJobControlFlags : u32 { enum class AjmJobControlFlags : u32 {
Reset = 1 << 2, Reset = 1 << 2,
Initialize = 1 << 3, Initialize = 1 << 3,
Resample = 1 << 4, Resample = 1 << 4,
StatisticsEngine = 1U << 31,
}; };
DECLARE_ENUM_FLAG_OPERATORS(AjmJobControlFlags) DECLARE_ENUM_FLAG_OPERATORS(AjmJobControlFlags)
@ -92,7 +94,7 @@ struct AjmSingleJob {
}; };
}; };
static_assert(sizeof(AjmSingleJob) == 0x40); static_assert(sizeof(AjmSingleJob) == 0x48);
struct AjmMultiJob { struct AjmMultiJob {
AjmHLEOpcode opcode; AjmHLEOpcode opcode;
@ -149,7 +151,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobControlBufferRa(AjmSingleJob* batch_pos, u32 in
void* PS4_SYSV_ABI sceAjmBatchJobInlineBuffer(AjmSingleJob* batch_pos, const void* in_buffer, void* PS4_SYSV_ABI sceAjmBatchJobInlineBuffer(AjmSingleJob* batch_pos, const void* in_buffer,
size_t in_size, const void** batch_address); size_t in_size, const void** batch_address);
void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instance, AjmFlags flags, void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instance, AjmFlags flags,
void * in_buffer, u32 in_size, u8* out_buffer, u8* in_buffer, u32 in_size, u8* out_buffer,
const u32 out_size, u8* sideband_output, const u32 out_size, u8* sideband_output,
const u32 sideband_output_size, const void* ret_addr); const u32 sideband_output_size, const void* ret_addr);
void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 instance, void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 instance,
@ -159,9 +161,10 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in
u64 num_output_buffers, void* sideband_output, u64 num_output_buffers, void* sideband_output,
u64 sideband_output_size, const void* ret_addr); u64 sideband_output_size, const void* ret_addr);
int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_size, int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_size,
const int priority, AjmBatchError* patch_error, const int priority, AjmBatchError* batch_error,
u32* out_batch_id); u32* out_batch_id);
int PS4_SYSV_ABI sceAjmBatchWait(); int PS4_SYSV_ABI sceAjmBatchWait(const u32 context, const u32 batch_id, const u32 timeout,
AjmBatchError* const batch_error);
int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData(); int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData();
int PS4_SYSV_ABI sceAjmDecMp3ParseFrame(const u8* stream, u32 stream_size, int parse_ofl, int PS4_SYSV_ABI sceAjmDecMp3ParseFrame(const u8* stream, u32 stream_size, int parse_ofl,
AjmDecMp3ParseFrame* frame); AjmDecMp3ParseFrame* frame);

View File

@ -4,8 +4,10 @@
#include <fmt/format.h> #include <fmt/format.h>
#include "common/assert.h" #include "common/assert.h"
#include "core/libraries/ajm/ajm_at9.h" #include "core/libraries/ajm/ajm_at9.h"
#include "error_codes.h"
extern "C" { extern "C" {
#include <decoder.h>
#include <libatrac9.h> #include <libatrac9.h>
} }
@ -17,9 +19,14 @@ AjmAt9Decoder::AjmAt9Decoder() {
AjmAt9Decoder::Reset(); AjmAt9Decoder::Reset();
} }
AjmAt9Decoder::~AjmAt9Decoder() {} AjmAt9Decoder::~AjmAt9Decoder() {
Atrac9ReleaseHandle(handle);
}
void AjmAt9Decoder::Reset() { void AjmAt9Decoder::Reset() {
Atrac9ReleaseHandle(handle);
handle = Atrac9GetHandle();
ASSERT_MSG(handle, "Atrac9GetHandle failed");
decoded_samples = 0; decoded_samples = 0;
static int filename = 0; static int filename = 0;
file.close(); file.close();
@ -28,31 +35,40 @@ void AjmAt9Decoder::Reset() {
std::tuple<u32, u32, u32> AjmAt9Decoder::Decode(const u8* in_buf, u32 in_size, u8* out_buf, std::tuple<u32, u32, u32> AjmAt9Decoder::Decode(const u8* in_buf, u32 in_size, u8* out_buf,
u32 out_size) { u32 out_size) {
auto ParseWavHeader = [](const uint8_t* buf, WavHeader* header) {
std::memcpy(header, buf, sizeof(WavHeader));
};
if (in_size <= 0 || out_size <= 0) { if (in_size <= 0 || out_size <= 0) {
return std::tuple(in_size, out_size, 0); return std::tuple(in_size, out_size, 0);
} }
WavHeader header{}; Atrac9InitDecoder(handle, config_data);
ParseWavHeader(in_buf - 0x64, &header);
if (!decoder_initialized) { const auto decoder_handle = static_cast<Atrac9Handle*>(handle);
Atrac9InitDecoder(handle, header.configData);
decoder_initialized = true;
}
// todo: do something with decoded data :p
int frame_count;
int bytes_used; int bytes_used;
Atrac9Decode(handle, in_buf, nullptr, &bytes_used);
Atrac9CodecInfo codec_info; Atrac9CodecInfo codec_info;
Atrac9GetCodecInfo(handle, &codec_info); Atrac9GetCodecInfo(handle, &codec_info);
return std::tuple(in_size, out_size, 0); decoded_samples = 0;
const auto size = codec_info.channels * codec_info.frameSamples * sizeof(u16);
for (frame_count = 0; frame_count < decoder_handle->Config.FramesPerSuperframe; frame_count++) {
short pcm_buffer[size];
int ret = Atrac9Decode(decoder_handle, in_buf, pcm_buffer, &bytes_used);
ASSERT_MSG(ret == At9Status::ERR_SUCCESS, "Atrac9Decode failed");
in_buf += bytes_used;
in_size -= bytes_used;
std::memcpy(out_buf, pcm_buffer, size);
file.write(reinterpret_cast<const char*>(pcm_buffer), size);
out_buf += size;
out_size -= size;
decoded_samples += decoder_handle->Config.FrameSamples;
}
LOG_TRACE(Lib_Ajm, "Decoded {} samples, frame count: {}", decoded_samples, frame_count);
return std::tuple(in_size, out_size, frame_count);
} }
} // namespace Libraries::Ajm } // namespace Libraries::Ajm

View File

@ -4,7 +4,6 @@
#pragma once #pragma once
#include <fstream> #include <fstream>
#include <guiddef.h>
#include "common/types.h" #include "common/types.h"
#include "core/libraries/ajm/ajm_instance.h" #include "core/libraries/ajm/ajm_instance.h"
@ -16,42 +15,17 @@ namespace Libraries::Ajm {
constexpr unsigned int SCE_AT9_CONFIG_DATA_SIZE = 4; constexpr unsigned int SCE_AT9_CONFIG_DATA_SIZE = 4;
#pragma pack(push, 1) struct SceAjmDecAt9InitializeParameters {
struct WavHeader { u8 config_data[SCE_AT9_CONFIG_DATA_SIZE];
/* RIFF Chunk Descriptor */
char RIFF[4]; // RIFF Header Magic header
uint32_t ChunkSize; // RIFF Chunk Size
char WAVE[4]; // WAVE Header
/* "fmt" sub-chunk */
char fmt[4]; // FMT header
uint32_t Subchunk1Size; // Size of the fmt chunk
uint16_t AudioFormat; // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law,
// 259=ADPCM
uint16_t NumOfChan; // Number of channels 1=Mono 2=Sterio
uint32_t SamplesPerSec; // Sampling Frequency in Hz
uint32_t bytesPerSec; // bytes per second
uint16_t blockAlign; // 2=16-bit mono, 4=16-bit stereo
uint16_t bitsPerSample; // Number of bits per sample
u16 cbSize; // Extension size
u16 samplesPerBlock;
u32 channelMask;
GUID subFormat;
u32 versionInfo;
u8 configData[SCE_AT9_CONFIG_DATA_SIZE]; // the juicy part
u32 reserved; u32 reserved;
/* "fact" sub-chunk */
char Subchunk2ID[4]; // "fact" string
uint32_t Subchunk2Size; // Sampled data length
}; };
#pragma pack(pop)
static_assert(sizeof(WavHeader) == 80);
struct AjmAt9Decoder final : AjmInstance { struct AjmAt9Decoder final : AjmInstance {
void* handle; void* handle;
bool decoder_initialized = false; bool decoder_initialized = false;
std::fstream file; std::fstream file;
int length; int length;
u8 config_data[SCE_AT9_CONFIG_DATA_SIZE];
explicit AjmAt9Decoder(); explicit AjmAt9Decoder();
~AjmAt9Decoder() override; ~AjmAt9Decoder() override;

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include "common/enum.h"
#include "common/types.h" #include "common/types.h"
extern "C" { extern "C" {
@ -19,6 +20,7 @@ enum class AjmCodecType : u32 {
M4aacDec = 2, M4aacDec = 2,
Max = 23, Max = 23,
}; };
DECLARE_ENUM_FLAG_OPERATORS(AjmCodecType);
static constexpr u32 NumAjmCodecs = u32(AjmCodecType::Max); static constexpr u32 NumAjmCodecs = u32(AjmCodecType::Max);
enum class AjmFormatEncoding : u32 { enum class AjmFormatEncoding : u32 {
@ -64,8 +66,8 @@ struct AjmInstance {
virtual void Reset() = 0; virtual void Reset() = 0;
virtual std::tuple<u32, u32, u32> Decode(const u8* in_buf, u32 in_size, virtual std::tuple<u32, u32, u32> Decode(const u8* in_buf, u32 in_size, u8* out_buf,
u8* out_buf, u32 out_size) = 0; u32 out_size) = 0;
}; };
} // namespace Libraries::Ajm } // namespace Libraries::Ajm

View File

@ -15,7 +15,9 @@ namespace Libraries::Ajm {
// Following tables have been reversed from AJM library // Following tables have been reversed from AJM library
static constexpr std::array<std::array<s32, 3>, 3> SamplerateTable = {{ static constexpr std::array<std::array<s32, 3>, 3> SamplerateTable = {{
{0x5622, 0x5DC0, 0x3E80}, {0xAC44, 0xBB80, 0x7D00}, {0x2B11, 0x2EE0, 0x1F40}, {0x5622, 0x5DC0, 0x3E80},
{0xAC44, 0xBB80, 0x7D00},
{0x2B11, 0x2EE0, 0x1F40},
}}; }};
static constexpr std::array<std::array<s32, 15>, 2> BitrateTable = {{ static constexpr std::array<std::array<s32, 15>, 2> BitrateTable = {{
@ -78,14 +80,13 @@ void AjmMp3Decoder::Reset() {
file.open(fmt::format("inst{}_{}.raw", index, ++filename), std::ios::out | std::ios::binary); file.open(fmt::format("inst{}_{}.raw", index, ++filename), std::ios::out | std::ios::binary);
} }
std::tuple<u32, u32, u32> AjmMp3Decoder::Decode(const u8* buf, u32 in_size, std::tuple<u32, u32, u32> AjmMp3Decoder::Decode(const u8* buf, u32 in_size, u8* out_buf,
u8* out_buf, u32 out_size) { u32 out_size) {
u32 num_frames = 0; u32 num_frames = 0;
AVPacket* pkt = av_packet_alloc(); AVPacket* pkt = av_packet_alloc();
while (in_size > 0 && out_size > 0) { while (in_size > 0 && out_size > 0) {
int ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, int ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, buf, in_size, AV_NOPTS_VALUE,
buf, in_size, AV_NOPTS_VALUE, 0);
AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
ASSERT_MSG(ret >= 0, "Error while parsing {}", ret); ASSERT_MSG(ret >= 0, "Error while parsing {}", ret);
buf += ret; buf += ret;
in_size -= ret; in_size -= ret;

View File

@ -15,13 +15,7 @@ struct AVCodecParserContext;
namespace Libraries::Ajm { namespace Libraries::Ajm {
enum class AjmDecMp3OflType : u32 { enum class AjmDecMp3OflType : u32 { None = 0, Lame = 1, Vbri = 2, Fgh = 3, VbriAndFgh = 4 };
None = 0,
Lame = 1,
Vbri = 2,
Fgh = 3,
VbriAndFgh = 4
};
// 11-bit syncword if MPEG 2.5 extensions are enabled // 11-bit syncword if MPEG 2.5 extensions are enabled
static constexpr u8 SYNCWORDH = 0xff; static constexpr u8 SYNCWORDH = 0xff;
@ -73,8 +67,8 @@ struct AjmMp3Decoder : public AjmInstance {
void Reset() override; void Reset() override;
std::tuple<u32, u32, u32> Decode(const u8* in_buf, u32 in_size, std::tuple<u32, u32, u32> Decode(const u8* in_buf, u32 in_size, u8* out_buf,
u8* out_buf, u32 out_size) override; u32 out_size) override;
static int ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl, static int ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl,
AjmDecMp3ParseFrame* frame); AjmDecMp3ParseFrame* frame);

View File

@ -1,7 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include <iostream>
#include <memory> #include <memory>
#include <fmt/chrono.h>
#include <magic_enum.hpp> #include <magic_enum.hpp>
#include "audio_core/sdl_audio.h" #include "audio_core/sdl_audio.h"
@ -335,6 +338,10 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) {
int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) { int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) {
for (u32 i = 0; i < num; i++) { for (u32 i = 0; i < num; i++) {
std::fstream file;
file.open(fmt::format("handle{}.raw", param[i].handle), std::ios::out | std::ios::binary);
file.write(static_cast<const char*>(param[i].ptr), 1024);
file.close();
if (const auto err = sceAudioOutOutput(param[i].handle, param[i].ptr); err != 0) if (const auto err = sceAudioOutOutput(param[i].handle, param[i].ptr); err != 0)
return err; return err;
} }

View File

@ -10,7 +10,7 @@
#include "core/signals.h" #include "core/signals.h"
#include "video_core/page_manager.h" #include "video_core/page_manager.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_rasterizer.h"
#define ENABLE_USERFAULTFD 1
#ifndef _WIN64 #ifndef _WIN64
#include <sys/mman.h> #include <sys/mman.h>
#ifdef ENABLE_USERFAULTFD #ifdef ENABLE_USERFAULTFD

View File

@ -212,8 +212,6 @@ vk::SamplerAddressMode ClampMode(AmdGpu::ClampMode mode) {
[[fallthrough]]; [[fallthrough]];
case AmdGpu::ClampMode::ClampBorder: case AmdGpu::ClampMode::ClampBorder:
return vk::SamplerAddressMode::eClampToBorder; return vk::SamplerAddressMode::eClampToBorder;
case AmdGpu::ClampMode::ClampHalfBorder:
return vk::SamplerAddressMode::eClampToBorder;
default: default:
UNREACHABLE(); UNREACHABLE();
} }