Files
KytyPS5/src/libs/ajm.cpp
T
2026-09-25 00:01:51 +04:00

1192 lines
44 KiB
C++

#include "common/assert.h"
#include "common/common.h"
#include "common/logging/log.h"
#include "libs/ajm/aac_decoder.h"
#include "libs/ajm/atrac9_decoder.h"
#include "libs/ajm/decoder.h"
#include "libs/ajm/mp3_decoder.h"
#include "libs/audio.h"
#include "libs/errno.h"
#include "libs/libs.h"
#include <algorithm>
#include <atomic>
#include <cerrno>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <limits>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
namespace Libs::Audio::Ajm {
LIB_NAME("Ajm", "Ajm");
struct AjmBatchInfo {
void* buffer;
size_t offset;
size_t size;
void* last_good_job;
const void* last_good_job_ra;
};
struct AjmBatchError {
int error_code;
const void* job_addr;
uint32_t cmd_offset;
const void* job_ra;
};
struct AjmBuffer {
void* address;
size_t size;
};
struct AjmSidebandResult {
int32_t result;
int32_t internal_result;
};
struct AjmSidebandStream {
int32_t size_consumed;
int32_t size_produced;
uint64_t total_decoded_samples;
};
struct AjmSidebandMFrame {
uint32_t num_frames;
uint32_t reserved;
};
struct AjmSidebandResampleInfo {
float ratio;
int32_t num_samples;
uint32_t reserved[8];
};
struct AjmGetStatisticsResult {
AjmSidebandResult result;
float engine_usage_batch;
float engine_usage_interval[3];
uint32_t memory[6];
};
struct AjmDecOpusInitializeParameters {
uint32_t channel_num;
uint32_t sample_rate;
uint32_t mapping_family;
};
struct AjmSidebandDecOpusCodecInfo {
uint32_t frames_per_packet;
};
enum class AjmCodec : uint32_t {
DecMp3 = 0,
DecAt9 = 1,
DecM4aac = 2,
DecUnknownNewABI = 14,
DecOpus = 24,
};
constexpr int32_t AJM_ERROR_INVALID_CONTEXT = static_cast<int32_t>(0x80930002u);
constexpr int32_t AJM_ERROR_INVALID_PARAMETER = static_cast<int32_t>(0x80930005u);
constexpr int32_t AJM_ERROR_CODEC_NOT_SUPPORTED = static_cast<int32_t>(0x80930008u);
constexpr int32_t AJM_ERROR_JOB_CREATION = static_cast<int32_t>(0x80930012u);
constexpr size_t AJM_JOB_CONTROL_SIZE = 48;
constexpr size_t AJM_JOB_RUN_SIZE = 64;
constexpr size_t AJM_JOB_GET_STATISTICS_SIZE =
sizeof(uint32_t) + sizeof(float) * 3 + 8 + AJM_JOB_RUN_SIZE;
constexpr size_t AJM_JOB_SET_RESAMPLE_PARAMETERS_SIZE =
sizeof(float) * 2 + sizeof(uint32_t) + 12 + AJM_JOB_CONTROL_SIZE;
constexpr size_t AJM_SIDEBAND_RESULT_SIZE = sizeof(AjmSidebandResult);
constexpr size_t AJM_SIDEBAND_STREAM_SIZE = sizeof(AjmSidebandStream);
constexpr size_t AJM_SIDEBAND_MFRAME_SIZE = sizeof(AjmSidebandMFrame);
constexpr size_t AJM_SIDEBAND_FORMAT_SIZE = sizeof(AjmSidebandFormat);
constexpr size_t AJM_SIDEBAND_GAPLESS_SIZE = sizeof(AjmSidebandGaplessDecode);
constexpr size_t AJM_SIDEBAND_RESAMPLE_INFO_SIZE = sizeof(AjmSidebandResampleInfo);
constexpr uint64_t AJM_FLAG_RUN_GET_CODEC_INFO = 1ull << 11u;
constexpr uint64_t AJM_FLAG_RUN_MULTIPLE_FRAMES = 1ull << 12u;
constexpr uint64_t AJM_FLAG_CONTROL_RESET = 1ull << 13u;
constexpr uint64_t AJM_FLAG_CONTROL_INITIALIZE = 1ull << 14u;
constexpr uint64_t AJM_FLAG_SIDEBAND_RESAMPLE_INFO = 1ull << 44u;
constexpr uint64_t AJM_FLAG_SIDEBAND_GAPLESS = 1ull << 45u;
constexpr uint64_t AJM_FLAG_SIDEBAND_FORMAT = 1ull << 46u;
constexpr uint64_t AJM_FLAG_SIDEBAND_STREAM = 1ull << 47u;
constexpr uint64_t AJM_INSTANCE_FLAG_FORMAT_OFFSET = 7u;
constexpr uint64_t AJM_INSTANCE_FLAG_FORMAT_MASK = 0x7u;
constexpr uint64_t AJM_INSTANCE_FLAG_MAX_CHANNEL_MASK = 0x7fu;
constexpr uint32_t AJM_DEC_OPUS_MAX_CHANNELS_FOR_10CH = 10;
constexpr uint32_t AJM_DEC_OPUS_FRAME_SAMPLES = 960;
static std::atomic_uint32_t g_ajm_next_instance {1};
static std::atomic_uint32_t g_ajm_next_batch {1};
static uint32_t AjmGetFlagChannelCount(uint64_t flags) {
return static_cast<uint32_t>(flags & AJM_INSTANCE_FLAG_MAX_CHANNEL_MASK);
}
static AjmSampleEncoding AjmGetFlagSampleEncoding(uint64_t flags) {
const auto encoding = static_cast<uint32_t>((flags >> AJM_INSTANCE_FLAG_FORMAT_OFFSET) &
AJM_INSTANCE_FLAG_FORMAT_MASK);
switch (encoding) {
case static_cast<uint32_t>(AjmSampleEncoding::S16): return AjmSampleEncoding::S16;
case static_cast<uint32_t>(AjmSampleEncoding::S32): return AjmSampleEncoding::S32;
case static_cast<uint32_t>(AjmSampleEncoding::Float): return AjmSampleEncoding::Float;
default: EXIT("unsupported AJM PCM sample encoding %u\n", encoding);
}
return AjmSampleEncoding::S16;
}
static const char* AjmCodecName(uint32_t codec) {
switch (codec) {
case static_cast<uint32_t>(AjmCodec::DecMp3): return "MP3 decoder";
case static_cast<uint32_t>(AjmCodec::DecAt9): return "ATRAC9 decoder";
case static_cast<uint32_t>(AjmCodec::DecM4aac): return "MPEG4-AAC decoder";
case 3: return "CELP8 decoder";
case 4: return "CELP8 encoder";
case 12: return "CELP16 decoder";
case 13: return "CELP16 encoder";
case 22: return "HE-VAG decoder";
case 23: return "LPCM decoder";
case static_cast<uint32_t>(AjmCodec::DecOpus): return "Opus decoder";
default: return "unknown";
}
}
static_assert(sizeof(AjmDecAt9ConfigDataInfo) == 20);
int KYTY_SYSV_ABI AjmDecAt9ParseConfigData(const void* config_data,
AjmDecAt9ConfigDataInfo* config_info) {
PRINT_NAME();
if (config_data == nullptr || config_info == nullptr) {
return AJM_ERROR_INVALID_PARAMETER;
}
auto* handle = Atrac9GetHandle();
if (handle == nullptr) {
return AJM_ERROR_INVALID_PARAMETER;
}
uint8_t config[ATRAC9_CONFIG_DATA_SIZE] {};
std::memcpy(config, config_data, sizeof(config));
Atrac9CodecInfo codec_info {};
const int init_result = Atrac9InitDecoder(handle, config);
const int info_result =
init_result == 0 ? Atrac9GetCodecInfo(handle, &codec_info) : init_result;
Atrac9ReleaseHandle(handle);
if (info_result != 0 || codec_info.channels <= 0 || codec_info.samplingRate <= 0 ||
codec_info.frameSamples <= 0 || codec_info.framesInSuperframe <= 0 ||
codec_info.superframeSize <= 0) {
return AJM_ERROR_INVALID_PARAMETER;
}
config_info->channels = static_cast<uint32_t>(codec_info.channels);
config_info->sample_rate = static_cast<uint32_t>(codec_info.samplingRate);
config_info->frame_samples_per_channel = static_cast<uint32_t>(codec_info.frameSamples);
config_info->superframe_samples_per_channel =
static_cast<uint32_t>(codec_info.frameSamples) *
static_cast<uint32_t>(codec_info.framesInSuperframe);
config_info->superframe_size = static_cast<uint32_t>(codec_info.superframeSize);
return OK;
}
class AjmOpusDecoder final: public AjmDecoder {
public:
AjmOpusDecoder(uint32_t channels, uint32_t sample_rate, AjmSampleEncoding encoding,
uint64_t flags)
: AjmDecoder(channels, sample_rate, encoding) {
(void)flags;
}
AjmDecodeResult Initialize(const void* codec_parameters,
size_t codec_parameters_size) override {
auto result = MakeResult();
if (codec_parameters == nullptr ||
codec_parameters_size < sizeof(AjmDecOpusInitializeParameters)) {
result.result = AJM_RESULT_INVALID_PARAMETER;
return result;
}
const auto* params = static_cast<const AjmDecOpusInitializeParameters*>(codec_parameters);
if (params->channel_num == 0 || params->channel_num > AJM_DEC_OPUS_MAX_CHANNELS_FOR_10CH ||
params->sample_rate == 0) {
result.result = AJM_RESULT_INVALID_PARAMETER;
return result;
}
SetFormat(params->channel_num, params->sample_rate, m_sample_encoding);
m_mapping_family = params->mapping_family;
m_total_decoded_samples = 0;
m_is_initialized = true;
m_frames_per_packet = 1;
LOGF("AJM Opus initialized: %" PRIu32 " Hz, %" PRIu32 " ch, mapping=%" PRIu32 "\n",
params->sample_rate, params->channel_num, params->mapping_family);
return MakeResult();
}
void Reset() override { m_total_decoded_samples = 0; }
AjmDecodeResult Decode(const void* input, size_t input_size, void* output, size_t output_size,
bool multiple_frames, AjmGaplessState* gapless) override {
(void)multiple_frames;
auto result = MakeResult();
if (input == nullptr || input_size == 0) {
result.result = AJM_RESULT_PARTIAL_INPUT;
return result;
}
if (!m_is_initialized) {
result.result = AJM_RESULT_NOT_INITIALIZED;
return result;
}
auto samples_to_write = AJM_DEC_OPUS_FRAME_SAMPLES;
auto skip_samples = 0u;
if (gapless != nullptr) {
skip_samples = std::min<uint32_t>(samples_to_write, gapless->current.skip_samples);
samples_to_write -= skip_samples;
if (gapless->HasSampleLimit()) {
samples_to_write =
std::min<uint32_t>(samples_to_write, gapless->current.total_samples);
}
}
const auto output_bytes = static_cast<size_t>(samples_to_write) *
static_cast<size_t>(m_channels) *
AjmBytesPerSample(m_sample_encoding);
if (output_bytes != 0 && output == nullptr) {
result.result = AJM_RESULT_NOT_ENOUGH_ROOM;
return result;
}
if (output_bytes > output_size) {
result.result = AJM_RESULT_NOT_ENOUGH_ROOM;
return result;
}
if (output_bytes != 0) {
std::memset(output, 0, output_bytes);
}
if (gapless != nullptr) {
gapless->current.skip_samples =
static_cast<uint16_t>(gapless->current.skip_samples - skip_samples);
if (gapless->HasSampleLimit()) {
gapless->current.total_samples =
(gapless->current.total_samples > samples_to_write
? gapless->current.total_samples - samples_to_write
: 0);
}
const auto skipped_samples = AJM_DEC_OPUS_FRAME_SAMPLES - samples_to_write;
const auto skipped_total = std::min<uint32_t>(
std::numeric_limits<uint16_t>::max(),
static_cast<uint32_t>(gapless->current.skipped_samples) + skipped_samples);
gapless->current.skipped_samples = static_cast<uint16_t>(skipped_total);
}
result.input_consumed = input_size;
result.output_written = output_bytes;
result.frames = 1;
result.frames_per_packet = m_frames_per_packet;
m_total_decoded_samples += samples_to_write;
result.total_decoded_samples = m_total_decoded_samples;
result.format = GetFormat();
return result;
}
void WriteCodecInfo(void* output, size_t output_size,
const AjmDecodeResult& result) const override {
(void)result;
if (output == nullptr || output_size < sizeof(AjmSidebandDecOpusCodecInfo)) {
return;
}
auto* info = static_cast<AjmSidebandDecOpusCodecInfo*>(output);
info->frames_per_packet = m_frames_per_packet;
}
[[nodiscard]] size_t CodecInfoSize() const override {
return sizeof(AjmSidebandDecOpusCodecInfo);
}
private:
bool m_is_initialized = false;
uint32_t m_mapping_family = 0;
uint32_t m_frames_per_packet = 1;
};
struct AjmInstanceState {
uint32_t context = 0;
uint32_t codec = 0;
uint64_t flags = 0;
AjmGaplessState gapless;
std::unique_ptr<AjmDecoder> decoder;
};
static std::mutex g_ajm_instances_mutex;
static std::unordered_map<uint32_t, AjmInstanceState> g_ajm_instances;
static bool AjmCodecIsSupported(uint32_t codec) {
return codec == static_cast<uint32_t>(AjmCodec::DecMp3) ||
codec == static_cast<uint32_t>(AjmCodec::DecAt9) ||
codec == static_cast<uint32_t>(AjmCodec::DecM4aac) ||
codec == static_cast<uint32_t>(AjmCodec::DecOpus) ||
codec == static_cast<uint32_t>(AjmCodec::DecUnknownNewABI);
}
static bool AjmLogCodecSupport(uint32_t codec) {
const auto supported = AjmCodecIsSupported(codec);
if (supported) {
static std::atomic_bool logged {false};
if (!logged.exchange(true, std::memory_order_relaxed)) {
std::printf("AJM codec=%u, name=%s\n", codec, AjmCodecName(codec));
}
} else {
std::printf("AJM codec=%u, name=%s\n", codec, AjmCodecName(codec));
std::printf("AJM unsupported codec=%u (%s)\n", codec, AjmCodecName(codec));
}
return supported;
}
static std::unique_ptr<AjmDecoder> AjmCreateDecoder(uint32_t codec, uint64_t flags) {
if (!AjmCodecIsSupported(codec)) {
return {};
}
auto channels = AjmGetFlagChannelCount(flags);
if (channels == 0) {
channels = 2;
}
const auto encoding = AjmGetFlagSampleEncoding(flags);
switch (codec) {
case static_cast<uint32_t>(AjmCodec::DecMp3):
return std::make_unique<AjmMp3Decoder>(channels, 48000, encoding, flags);
case static_cast<uint32_t>(AjmCodec::DecAt9):
return std::make_unique<AjmAt9Decoder>(channels, 48000, encoding, flags);
case static_cast<uint32_t>(AjmCodec::DecM4aac):
return std::make_unique<AjmAacDecoder>(channels, 48000, encoding, flags);
case static_cast<uint32_t>(AjmCodec::DecOpus):
return std::make_unique<AjmOpusDecoder>(channels, 48000, encoding, flags);
default: break;
}
return {};
}
static AjmInstanceState* AjmFindInstanceLocked(uint32_t instance) {
const auto it = g_ajm_instances.find(instance);
if (it == g_ajm_instances.end()) {
return nullptr;
}
return &it->second;
}
static uint8_t* AjmReserveJob(AjmBatchInfo* info, size_t job_size) {
EXIT_NOT_IMPLEMENTED(info == nullptr);
if (info->offset + job_size > info->size) {
return nullptr;
}
auto* job = static_cast<uint8_t*>(info->buffer) + info->offset;
info->last_good_job = job;
info->last_good_job_ra = nullptr;
info->offset += job_size;
std::memset(job, 0, job_size);
return job;
}
static int AjmAppendJob(AjmBatchInfo* info, size_t job_size, const char* name) {
auto* job = AjmReserveJob(info, job_size);
if (job == nullptr) {
LOGF("\t %s: not enough batch space, offset=0x%016" PRIx64 ", size=0x%016" PRIx64
", job=0x%016" PRIx64 "\n",
name, static_cast<uint64_t>(info->offset), static_cast<uint64_t>(info->size),
static_cast<uint64_t>(job_size));
return AJM_ERROR_JOB_CREATION;
}
return OK;
}
static int32_t AjmClampInt32(size_t value) {
return static_cast<int32_t>(
std::min<size_t>(value, static_cast<size_t>(std::numeric_limits<int32_t>::max())));
}
static void AjmWriteResult(void* result, size_t size, const AjmDecodeResult& decode_result = {}) {
if (result == nullptr || size == 0) {
return;
}
std::memset(result, 0, size);
if (size >= AJM_SIDEBAND_RESULT_SIZE) {
auto* sideband = static_cast<AjmSidebandResult*>(result);
sideband->result = decode_result.result;
sideband->internal_result = decode_result.internal_result;
}
}
static void AjmWriteStreamResult(void* result, size_t result_size,
const AjmDecodeResult& decode_result, bool multiple_frames) {
if (result == nullptr) {
return;
}
AjmWriteResult(result, result_size, decode_result);
if (result_size >= AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE) {
auto* stream = reinterpret_cast<AjmSidebandStream*>(static_cast<uint8_t*>(result) +
AJM_SIDEBAND_RESULT_SIZE);
stream->size_consumed = AjmClampInt32(decode_result.input_consumed);
stream->size_produced = AjmClampInt32(decode_result.output_written);
stream->total_decoded_samples = decode_result.total_decoded_samples;
}
if (multiple_frames && result_size >= AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE +
AJM_SIDEBAND_MFRAME_SIZE) {
auto* mframe = reinterpret_cast<AjmSidebandMFrame*>(
static_cast<uint8_t*>(result) + AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE);
mframe->num_frames = decode_result.frames;
}
}
static void AjmWriteFormat(void* result, size_t result_size, const AjmSidebandFormat& format,
const AjmDecodeResult& decode_result = {}) {
if (result == nullptr || result_size < AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_FORMAT_SIZE) {
AjmWriteResult(result, result_size, decode_result);
return;
}
AjmWriteResult(result, result_size, decode_result);
auto* format_output = reinterpret_cast<AjmSidebandFormat*>(static_cast<uint8_t*>(result) +
AJM_SIDEBAND_RESULT_SIZE);
*format_output = format;
}
static void AjmWriteCodecInfo(AjmDecoder* decoder, void* result, size_t result_size,
const AjmDecodeResult& decode_result) {
if (decoder == nullptr || result == nullptr || result_size <= AJM_SIDEBAND_RESULT_SIZE) {
return;
}
decoder->WriteCodecInfo(static_cast<uint8_t*>(result) + AJM_SIDEBAND_RESULT_SIZE,
result_size - AJM_SIDEBAND_RESULT_SIZE, decode_result);
}
static void AjmWriteSideband(uint64_t flags, void* sideband_output, size_t sideband_output_size,
AjmDecoder* decoder, const AjmGaplessState* gapless,
const AjmDecodeResult& decode_result) {
if (sideband_output == nullptr || sideband_output_size == 0) {
return;
}
AjmWriteResult(sideband_output, sideband_output_size, decode_result);
size_t offset = AJM_SIDEBAND_RESULT_SIZE;
if ((flags & AJM_FLAG_RUN_GET_CODEC_INFO) != 0 && decoder != nullptr) {
const auto codec_info_size = decoder->CodecInfoSize();
if (offset + codec_info_size <= sideband_output_size) {
decoder->WriteCodecInfo(static_cast<uint8_t*>(sideband_output) + offset,
sideband_output_size - offset, decode_result);
offset += codec_info_size;
}
}
if ((flags & AJM_FLAG_SIDEBAND_RESAMPLE_INFO) != 0 &&
offset + AJM_SIDEBAND_RESAMPLE_INFO_SIZE <= sideband_output_size) {
auto* info = reinterpret_cast<AjmSidebandResampleInfo*>(
static_cast<uint8_t*>(sideband_output) + offset);
info->ratio = 1.0f;
offset += AJM_SIDEBAND_RESAMPLE_INFO_SIZE;
}
if ((flags & AJM_FLAG_SIDEBAND_GAPLESS) != 0 &&
offset + AJM_SIDEBAND_GAPLESS_SIZE <= sideband_output_size) {
auto* gapless_output = reinterpret_cast<AjmSidebandGaplessDecode*>(
static_cast<uint8_t*>(sideband_output) + offset);
if (gapless != nullptr) {
*gapless_output = gapless->current;
}
offset += AJM_SIDEBAND_GAPLESS_SIZE;
}
if ((flags & AJM_FLAG_SIDEBAND_FORMAT) != 0 &&
offset + AJM_SIDEBAND_FORMAT_SIZE <= sideband_output_size) {
auto* format =
reinterpret_cast<AjmSidebandFormat*>(static_cast<uint8_t*>(sideband_output) + offset);
*format = decode_result.format;
offset += AJM_SIDEBAND_FORMAT_SIZE;
}
if ((flags & AJM_FLAG_SIDEBAND_STREAM) != 0 &&
offset + AJM_SIDEBAND_STREAM_SIZE <= sideband_output_size) {
auto* stream =
reinterpret_cast<AjmSidebandStream*>(static_cast<uint8_t*>(sideband_output) + offset);
stream->size_consumed = AjmClampInt32(decode_result.input_consumed);
stream->size_produced = AjmClampInt32(decode_result.output_written);
stream->total_decoded_samples = decode_result.total_decoded_samples;
offset += AJM_SIDEBAND_STREAM_SIZE;
}
if ((flags & AJM_FLAG_RUN_MULTIPLE_FRAMES) != 0 &&
offset + AJM_SIDEBAND_MFRAME_SIZE <= sideband_output_size) {
auto* mframe =
reinterpret_cast<AjmSidebandMFrame*>(static_cast<uint8_t*>(sideband_output) + offset);
mframe->num_frames = decode_result.frames;
}
}
static bool AjmCodecIsValid(uint32_t codec) {
switch (codec) {
case static_cast<uint32_t>(AjmCodec::DecMp3):
case static_cast<uint32_t>(AjmCodec::DecAt9):
case static_cast<uint32_t>(AjmCodec::DecM4aac):
case 3:
case 4:
case 12:
case 13:
case static_cast<uint32_t>(AjmCodec::DecUnknownNewABI):
case 22:
case 23:
case static_cast<uint32_t>(AjmCodec::DecOpus): return true;
default: return false;
}
}
static AjmDecodeResult AjmDecodeInstance(uint32_t instance, const void* input, size_t input_size,
void* output, size_t output_size, bool multiple_frames) {
std::scoped_lock lock(g_ajm_instances_mutex);
auto* state = AjmFindInstanceLocked(instance);
if (state == nullptr || state->decoder == nullptr) {
AjmDecodeResult result {};
result.result = AJM_RESULT_INVALID_PARAMETER;
return result;
}
return state->decoder->Decode(input, input_size, output, output_size, multiple_frames,
&state->gapless);
}
static AjmDecodeResult AjmDecodeSplitInstance(uint32_t instance, const AjmBuffer* input_buffers,
size_t input_buffers_num,
const AjmBuffer* output_buffers,
size_t output_buffers_num, bool multiple_frames) {
size_t input_size = 0;
size_t output_size = 0;
for (size_t i = 0; i < input_buffers_num && input_buffers != nullptr; i++) {
input_size += input_buffers[i].size;
}
for (size_t i = 0; i < output_buffers_num && output_buffers != nullptr; i++) {
output_size += output_buffers[i].size;
}
std::vector<uint8_t> input(input_size);
size_t input_offset = 0;
for (size_t i = 0; i < input_buffers_num && input_buffers != nullptr; i++) {
if (input_buffers[i].address != nullptr && input_buffers[i].size != 0) {
std::memcpy(input.data() + input_offset, input_buffers[i].address,
input_buffers[i].size);
}
input_offset += input_buffers[i].size;
}
std::vector<uint8_t> output(output_size);
auto result = AjmDecodeInstance(instance, input.data(), input.size(), output.data(),
output.size(), multiple_frames);
size_t output_offset = 0;
for (size_t i = 0; i < output_buffers_num && output_buffers != nullptr; i++) {
const auto bytes = std::min(
output_buffers[i].size,
(result.output_written > output_offset ? result.output_written - output_offset : 0));
if (output_buffers[i].address != nullptr && bytes != 0) {
std::memcpy(output_buffers[i].address, output.data() + output_offset, bytes);
}
output_offset += output_buffers[i].size;
}
return result;
}
static AjmDecodeResult AjmInitializeInstance(uint32_t instance, const void* codec_parameters,
size_t codec_parameters_size) {
std::scoped_lock lock(g_ajm_instances_mutex);
auto* state = AjmFindInstanceLocked(instance);
if (state == nullptr || state->decoder == nullptr) {
AjmDecodeResult result {};
result.result = AJM_RESULT_INVALID_PARAMETER;
return result;
}
state->gapless.Reset();
return state->decoder->Initialize(codec_parameters, codec_parameters_size);
}
static AjmDecodeResult AjmControlInstance(uint32_t instance, uint64_t flags,
const void* sideband_input, size_t sideband_input_size,
AjmDecoder** out_decoder, AjmGaplessState** out_gapless) {
std::scoped_lock lock(g_ajm_instances_mutex);
auto* state = AjmFindInstanceLocked(instance);
if (state == nullptr || state->decoder == nullptr) {
AjmDecodeResult result {};
result.result = AJM_RESULT_INVALID_PARAMETER;
return result;
}
auto* decoder = state->decoder.get();
if (out_decoder != nullptr) {
*out_decoder = decoder;
}
if (out_gapless != nullptr) {
*out_gapless = &state->gapless;
}
AjmDecodeResult result = decoder->MakeResult();
if ((flags & AJM_FLAG_SIDEBAND_GAPLESS) != 0) {
if (sideband_input == nullptr || sideband_input_size < AJM_SIDEBAND_GAPLESS_SIZE) {
result.result = AJM_RESULT_INVALID_PARAMETER;
return result;
}
state->gapless.Set(*static_cast<const AjmSidebandGaplessDecode*>(sideband_input),
(flags & AJM_FLAG_CONTROL_RESET) != 0);
sideband_input = static_cast<const uint8_t*>(sideband_input) + AJM_SIDEBAND_GAPLESS_SIZE;
sideband_input_size -= AJM_SIDEBAND_GAPLESS_SIZE;
}
if ((flags & AJM_FLAG_CONTROL_RESET) != 0) {
state->gapless.Reset();
decoder->Reset();
result = decoder->MakeResult();
}
if ((flags & AJM_FLAG_CONTROL_INITIALIZE) != 0) {
state->gapless.Reset();
result = decoder->Initialize(sideband_input, sideband_input_size);
}
return result;
}
static AjmDecodeResult AjmSetGaplessDecode(uint32_t instance, const void* gapless_decode,
int reset) {
const auto flags = AJM_FLAG_SIDEBAND_GAPLESS | (reset != 0 ? AJM_FLAG_CONTROL_RESET : 0);
return AjmControlInstance(instance, flags, gapless_decode, sizeof(AjmSidebandGaplessDecode),
nullptr, nullptr);
}
static AjmDecodeResult AjmGetGaplessDecode(uint32_t instance,
AjmSidebandGaplessDecode* gapless_decode) {
std::scoped_lock lock(g_ajm_instances_mutex);
auto* state = AjmFindInstanceLocked(instance);
if (state == nullptr || state->decoder == nullptr) {
AjmDecodeResult result {};
result.result = AJM_RESULT_INVALID_PARAMETER;
return result;
}
if (gapless_decode != nullptr) {
*gapless_decode = state->gapless.current;
}
return state->decoder->MakeResult();
}
static AjmSidebandFormat AjmGetInstanceFormat(uint32_t instance,
AjmDecodeResult* out_result = nullptr,
AjmDecoder** out_decoder = nullptr,
AjmGaplessState** out_gapless = nullptr) {
std::scoped_lock lock(g_ajm_instances_mutex);
auto* state = AjmFindInstanceLocked(instance);
if (state == nullptr || state->decoder == nullptr) {
if (out_result != nullptr) {
out_result->result = AJM_RESULT_INVALID_PARAMETER;
}
return AjmMakeFormat(2, 48000, AjmSampleEncoding::S16);
}
if (out_decoder != nullptr) {
*out_decoder = state->decoder.get();
}
if (out_gapless != nullptr) {
*out_gapless = &state->gapless;
}
if (out_result != nullptr) {
*out_result = state->decoder->MakeResult();
out_result->format = state->decoder->GetFormat();
}
return state->decoder->GetFormat();
}
int KYTY_SYSV_ABI AjmInitialize(int64_t reserved, uint32_t* context) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(context == nullptr);
if (reserved != 0) {
LOGF("\t ignoring reserved = 0x%016" PRIx64 "\n", static_cast<uint64_t>(reserved));
}
*context = 1;
return OK;
}
int KYTY_SYSV_ABI AjmFinalize(uint32_t context) {
PRINT_NAME();
LOGF("\t context = %" PRIu32 "\n", context);
return OK;
}
int KYTY_SYSV_ABI AjmModuleRegister(uint32_t context, uint32_t codec, int64_t reserved) {
PRINT_NAME();
if (reserved != 0) {
LOGF("\t ignoring reserved = 0x%016" PRIx64 "\n", static_cast<uint64_t>(reserved));
}
LOGF("\t codec = %u\n", codec);
LOGF("\t %s\n", AjmCodecName(codec));
const auto valid = AjmCodecIsValid(codec);
const auto supported = valid && AjmLogCodecSupport(codec);
int result = OK;
if (context != 1) {
result = AJM_ERROR_INVALID_CONTEXT;
} else if (!valid) {
result = AJM_ERROR_INVALID_PARAMETER;
} else if (!supported) {
result = AJM_ERROR_CODEC_NOT_SUPPORTED;
}
if (!supported) {
LOGF("\t codec is not implemented\n");
}
return result;
}
int KYTY_SYSV_ABI AjmModuleUnregister(uint32_t context, uint32_t codec) {
PRINT_NAME();
LOGF("\t context = %" PRIu32 ", codec = %" PRIu32 "\n", context, codec);
return OK;
}
int KYTY_SYSV_ABI AjmInstanceCreate(uint32_t context, uint32_t codec, uint64_t flags,
uint32_t* instance) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(instance == nullptr);
const auto supported = AjmLogCodecSupport(codec);
const auto slot = (g_ajm_next_instance.fetch_add(1, std::memory_order_relaxed) & 0x3fffu);
*instance = (codec << 14u) | slot;
auto state = AjmInstanceState {};
state.context = context;
state.codec = codec;
state.flags = flags;
state.decoder = AjmCreateDecoder(codec, flags);
{
std::scoped_lock lock(g_ajm_instances_mutex);
g_ajm_instances[*instance] = std::move(state);
}
LOGF("\t context = %" PRIu32 "\n"
"\t codec = %" PRIu32 "\n"
"\t flags = 0x%016" PRIx64 "\n"
"\t instance = 0x%08" PRIx32 "\n",
context, codec, flags, *instance);
if (!supported) {
LOGF("\t unsupported codec instance created without decoder\n");
}
return OK;
}
int KYTY_SYSV_ABI AjmInstanceDestroy(uint32_t context, uint32_t instance) {
PRINT_NAME();
LOGF("\t context = %" PRIu32 ", instance = 0x%08" PRIx32 "\n", context, instance);
std::scoped_lock lock(g_ajm_instances_mutex);
g_ajm_instances.erase(instance);
return OK;
}
int KYTY_SYSV_ABI AjmMemoryRegister(uint32_t context, void* ptr, size_t pages) {
PRINT_NAME();
LOGF("\t context = %" PRIu32 ", ptr = 0x%016" PRIx64 ", pages = 0x%016" PRIx64 "\n", context,
reinterpret_cast<uint64_t>(ptr), static_cast<uint64_t>(pages));
return OK;
}
int KYTY_SYSV_ABI AjmMemoryUnregister(uint32_t context, void* ptr) {
PRINT_NAME();
LOGF("\t context = %" PRIu32 ", ptr = 0x%016" PRIx64 "\n", context,
reinterpret_cast<uint64_t>(ptr));
return OK;
}
int KYTY_SYSV_ABI AjmBatchInitialize(void* buffer, size_t size, AjmBatchInfo* info) {
PRINT_NAME();
if (buffer == nullptr || info == nullptr) {
return AJM_ERROR_INVALID_PARAMETER;
}
info->buffer = buffer;
info->offset = 0;
info->size = size;
info->last_good_job = nullptr;
info->last_good_job_ra = nullptr;
return OK;
}
int KYTY_SYSV_ABI AjmBatchStart(uint32_t context, const AjmBatchInfo* info, int priority,
AjmBatchError* error, uint32_t* batch) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
EXIT_NOT_IMPLEMENTED(batch == nullptr);
(void)context;
(void)priority;
if (error != nullptr) {
std::memset(error, 0, sizeof(AjmBatchError));
}
*batch = g_ajm_next_batch.fetch_add(1, std::memory_order_relaxed);
return OK;
}
int KYTY_SYSV_ABI AjmBatchWait(uint32_t context, uint32_t batch, uint32_t timeout,
AjmBatchError* error) {
(void)context;
(void)batch;
(void)timeout;
if (error != nullptr) {
std::memset(error, 0, sizeof(AjmBatchError));
}
return OK;
}
int KYTY_SYSV_ABI AjmBatchCancel(uint32_t context, uint32_t batch) {
PRINT_NAME();
LOGF("\t context = %" PRIu32 ", batch = %" PRIu32 "\n", context, batch);
return OK;
}
int KYTY_SYSV_ABI AjmBatchErrorDump(const AjmBatchInfo* info, AjmBatchError* error) {
PRINT_NAME();
if (error != nullptr) {
std::memset(error, 0, sizeof(AjmBatchError));
}
LOGF("\t info = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(info));
return OK;
}
int KYTY_SYSV_ABI AjmBatchJobInitialize(AjmBatchInfo* info, uint32_t instance,
const void* codec_parameters, size_t codec_parameters_size,
void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
const auto decode_result =
AjmInitializeInstance(instance, codec_parameters, codec_parameters_size);
AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE, decode_result);
LOGF("\t instance = 0x%08" PRIx32 ", codec_parameters = 0x%016" PRIx64 ", size = 0x%016" PRIx64
"\n",
instance, reinterpret_cast<uint64_t>(codec_parameters),
static_cast<uint64_t>(codec_parameters_size));
return AjmAppendJob(info, AJM_JOB_CONTROL_SIZE, "initialize");
}
int KYTY_SYSV_ABI AjmBatchJobClearContext(AjmBatchInfo* info, uint32_t instance, void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
const auto decode_result =
AjmControlInstance(instance, AJM_FLAG_CONTROL_RESET, nullptr, 0, nullptr, nullptr);
AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE, decode_result);
LOGF("\t instance = 0x%08" PRIx32 "\n", instance);
return AjmAppendJob(info, AJM_JOB_CONTROL_SIZE, "clear-context");
}
int KYTY_SYSV_ABI AjmBatchJobDecode(AjmBatchInfo* info, uint32_t instance,
const void* bitstream_input, size_t bitstream_input_size,
void* pcm_output, size_t pcm_output_size, void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
const auto decode_result = AjmDecodeInstance(instance, bitstream_input, bitstream_input_size,
pcm_output, pcm_output_size, true);
AjmWriteStreamResult(
result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE + AJM_SIDEBAND_MFRAME_SIZE,
decode_result, true);
LOGF("\t instance = 0x%08" PRIx32 ", input = 0x%016" PRIx64 ", input_size = 0x%016" PRIx64
", output = 0x%016" PRIx64 ", output_size = 0x%016" PRIx64 "\n",
instance, reinterpret_cast<uint64_t>(bitstream_input),
static_cast<uint64_t>(bitstream_input_size), reinterpret_cast<uint64_t>(pcm_output),
static_cast<uint64_t>(pcm_output_size));
return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "decode");
}
int KYTY_SYSV_ABI AjmBatchJobDecodeSingle(AjmBatchInfo* info, uint32_t instance,
const void* bitstream_input, size_t bitstream_input_size,
void* pcm_output, size_t pcm_output_size, void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
const auto decode_result = AjmDecodeInstance(instance, bitstream_input, bitstream_input_size,
pcm_output, pcm_output_size, false);
AjmWriteStreamResult(result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE, decode_result,
false);
LOGF("\t instance = 0x%08" PRIx32 "\n", instance);
return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "decode-single");
}
int KYTY_SYSV_ABI AjmBatchJobDecodeSplit(AjmBatchInfo* info, uint32_t instance,
const AjmBuffer* input_buffers, size_t input_buffers_num,
const AjmBuffer* output_buffers, size_t output_buffers_num,
void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
const auto decode_result = AjmDecodeSplitInstance(instance, input_buffers, input_buffers_num,
output_buffers, output_buffers_num, true);
AjmWriteStreamResult(
result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE + AJM_SIDEBAND_MFRAME_SIZE,
decode_result, true);
LOGF("\t instance = 0x%08" PRIx32 ", input_buffers = 0x%016" PRIx64
", input_num = 0x%016" PRIx64 ", output_buffers = 0x%016" PRIx64
", output_num = 0x%016" PRIx64 "\n",
instance, reinterpret_cast<uint64_t>(input_buffers),
static_cast<uint64_t>(input_buffers_num), reinterpret_cast<uint64_t>(output_buffers),
static_cast<uint64_t>(output_buffers_num));
return AjmAppendJob(info, 16 * (input_buffers_num + output_buffers_num) + 32, "decode-split");
}
int KYTY_SYSV_ABI AjmBatchJobEncode(AjmBatchInfo* info, uint32_t instance, const void* pcm_input,
size_t pcm_input_size, void* bitstream_output,
size_t bitstream_output_size, void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
AjmDecodeResult encode_result {};
encode_result.input_consumed = pcm_input_size;
encode_result.output_written = bitstream_output_size;
encode_result.frames = (pcm_input_size != 0 || bitstream_output_size != 0 ? 1u : 0u);
encode_result.total_decoded_samples = bitstream_output_size;
if (bitstream_output != nullptr && bitstream_output_size != 0) {
std::memset(bitstream_output, 0, bitstream_output_size);
}
AjmWriteStreamResult(
result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE + AJM_SIDEBAND_MFRAME_SIZE,
encode_result, true);
LOGF("\t instance = 0x%08" PRIx32 ", input = 0x%016" PRIx64 ", output = 0x%016" PRIx64 "\n",
instance, reinterpret_cast<uint64_t>(pcm_input),
reinterpret_cast<uint64_t>(bitstream_output));
return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "encode");
}
int KYTY_SYSV_ABI AjmBatchJobGetInfo(AjmBatchInfo* info, uint32_t instance, void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
AjmDecodeResult decode_result {};
const auto format = AjmGetInstanceFormat(instance, &decode_result);
AjmWriteFormat(result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_FORMAT_SIZE, format,
decode_result);
LOGF("\t instance = 0x%08" PRIx32 "\n", instance);
return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "get-info");
}
int KYTY_SYSV_ABI AjmBatchJobGetCodecInfo(AjmBatchInfo* info, uint32_t instance, void* result,
size_t result_size) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
AjmDecodeResult decode_result {};
AjmDecoder* decoder = nullptr;
(void)AjmGetInstanceFormat(instance, &decode_result, &decoder);
AjmWriteResult(result, result_size, decode_result);
AjmWriteCodecInfo(decoder, result, result_size, decode_result);
LOGF("\t instance = 0x%08" PRIx32 ", result_size = 0x%016" PRIx64 "\n", instance,
static_cast<uint64_t>(result_size));
return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "get-codec-info");
}
int KYTY_SYSV_ABI AjmBatchJobSetGaplessDecode(AjmBatchInfo* info, uint32_t instance,
const void* gapless_decode, int reset, void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
const auto decode_result = AjmSetGaplessDecode(instance, gapless_decode, reset);
AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE, decode_result);
LOGF("\t instance = 0x%08" PRIx32 ", gapless_decode = 0x%016" PRIx64 ", reset = %d\n", instance,
reinterpret_cast<uint64_t>(gapless_decode), reset);
return AjmAppendJob(info, AJM_JOB_CONTROL_SIZE, "set-gapless");
}
int KYTY_SYSV_ABI AjmBatchJobGetGaplessDecode(AjmBatchInfo* info, uint32_t instance, void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
AjmDecodeResult decode_result {};
AjmSidebandGaplessDecode gapless_decode {};
decode_result = AjmGetGaplessDecode(instance, &gapless_decode);
AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_GAPLESS_SIZE, decode_result);
if (result != nullptr) {
auto* gapless = reinterpret_cast<AjmSidebandGaplessDecode*>(static_cast<uint8_t*>(result) +
AJM_SIDEBAND_RESULT_SIZE);
*gapless = gapless_decode;
}
LOGF("\t instance = 0x%08" PRIx32 "\n", instance);
return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "get-gapless");
}
int KYTY_SYSV_ABI AjmBatchJobSetResampleParameters(AjmBatchInfo* info, uint32_t instance,
float ratio, uint32_t flags, void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE);
LOGF("\t instance = 0x%08" PRIx32 ", ratio = %f, flags = 0x%08" PRIx32 "\n", instance,
static_cast<double>(ratio), flags);
return AjmAppendJob(info, AJM_JOB_SET_RESAMPLE_PARAMETERS_SIZE, "set-resample");
}
int KYTY_SYSV_ABI AjmBatchJobSetResampleParametersEx(AjmBatchInfo* info, uint32_t instance,
float ratio_start,
float ratio_change_per_sample, uint32_t flags,
void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE);
LOGF("\t instance = 0x%08" PRIx32 ", ratio_start = %f, ratio_change = %f, flags = 0x%08" PRIx32
"\n",
instance, static_cast<double>(ratio_start), static_cast<double>(ratio_change_per_sample),
flags);
return AjmAppendJob(info, AJM_JOB_SET_RESAMPLE_PARAMETERS_SIZE, "set-resample-ex");
}
int KYTY_SYSV_ABI AjmBatchJobGetResampleInfo(AjmBatchInfo* info, uint32_t instance, void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
AjmDecodeResult decode_result {};
(void)AjmGetInstanceFormat(instance, &decode_result);
AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_RESAMPLE_INFO_SIZE,
decode_result);
if (result != nullptr) {
auto* resample = reinterpret_cast<AjmSidebandResampleInfo*>(static_cast<uint8_t*>(result) +
AJM_SIDEBAND_RESULT_SIZE);
resample->ratio = 1.0f;
}
LOGF("\t instance = 0x%08" PRIx32 "\n", instance);
return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "get-resample-info");
}
int KYTY_SYSV_ABI AjmBatchJobGetStatistics(AjmBatchInfo* info, float interval, void* result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
AjmWriteResult(result, sizeof(AjmGetStatisticsResult));
(void)interval;
return AjmAppendJob(info, AJM_JOB_GET_STATISTICS_SIZE, "get-statistics");
}
int KYTY_SYSV_ABI AjmBatchJobControl(AjmBatchInfo* info, uint32_t instance, uint64_t flags,
const void* sideband_input, size_t sideband_input_size,
void* sideband_output, size_t sideband_output_size) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
AjmDecoder* decoder = nullptr;
AjmGaplessState* gapless = nullptr;
const auto decode_result = AjmControlInstance(instance, flags, sideband_input,
sideband_input_size, &decoder, &gapless);
AjmWriteSideband(flags, sideband_output, sideband_output_size, decoder, gapless, decode_result);
LOGF("\t instance = 0x%08" PRIx32 ", flags = 0x%016" PRIx64 ", input = 0x%016" PRIx64
", input_size = 0x%016" PRIx64 ", output = 0x%016" PRIx64 ", output_size = 0x%016" PRIx64
"\n",
instance, flags, reinterpret_cast<uint64_t>(sideband_input),
static_cast<uint64_t>(sideband_input_size), reinterpret_cast<uint64_t>(sideband_output),
static_cast<uint64_t>(sideband_output_size));
return AjmAppendJob(info, AJM_JOB_CONTROL_SIZE, "control");
}
int KYTY_SYSV_ABI AjmBatchJobRun(AjmBatchInfo* info, uint32_t instance, uint64_t flags,
const void* data_input, size_t data_input_size, void* data_output,
size_t data_output_size, void* sideband_output,
size_t sideband_output_size) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
auto decode_result =
AjmDecodeInstance(instance, data_input, data_input_size, data_output, data_output_size,
(flags & AJM_FLAG_RUN_MULTIPLE_FRAMES) != 0);
AjmDecoder* decoder = nullptr;
AjmGaplessState* gapless = nullptr;
(void)AjmGetInstanceFormat(instance, nullptr, &decoder, &gapless);
AjmWriteSideband(flags, sideband_output, sideband_output_size, decoder, gapless, decode_result);
LOGF("\t instance = 0x%08" PRIx32 ", flags = 0x%016" PRIx64 ", input = 0x%016" PRIx64
", input_size = 0x%016" PRIx64 ", output = 0x%016" PRIx64 ", output_size = 0x%016" PRIx64
"\n",
instance, flags, reinterpret_cast<uint64_t>(data_input),
static_cast<uint64_t>(data_input_size), reinterpret_cast<uint64_t>(data_output),
static_cast<uint64_t>(data_output_size));
return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "run");
}
int KYTY_SYSV_ABI AjmBatchJobRunSplit(AjmBatchInfo* info, uint32_t instance, uint64_t flags,
const AjmBuffer* input_buffers, size_t input_buffers_num,
const AjmBuffer* output_buffers, size_t output_buffers_num,
void* sideband_output, size_t sideband_output_size) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info == nullptr);
auto decode_result =
AjmDecodeSplitInstance(instance, input_buffers, input_buffers_num, output_buffers,
output_buffers_num, (flags & AJM_FLAG_RUN_MULTIPLE_FRAMES) != 0);
AjmDecoder* decoder = nullptr;
AjmGaplessState* gapless = nullptr;
(void)AjmGetInstanceFormat(instance, nullptr, &decoder, &gapless);
AjmWriteSideband(flags, sideband_output, sideband_output_size, decoder, gapless, decode_result);
return AjmAppendJob(info, 16 * (input_buffers_num + output_buffers_num) + 32, "run-split");
}
const char* KYTY_SYSV_ABI AjmStrError(int error) {
PRINT_NAME();
LOGF("\t error = %d\n", error);
return "AJM";
}
} // namespace Libs::Audio::Ajm