Skip to content

Commit 39c2cb8

Browse files
committed
ajm fixes
1 parent 138425f commit 39c2cb8

File tree

12 files changed

+163
-99
lines changed

12 files changed

+163
-99
lines changed

src/core/libraries/ajm/ajm.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ struct AjmSidebandGaplessDecode {
133133

134134
struct AjmSidebandResampleParameters {
135135
float ratio;
136-
uint32_t flags;
136+
u32 flags;
137137
};
138138

139139
struct AjmDecAt9InitializeParameters {

src/core/libraries/ajm/ajm_at9.cpp

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
22
// SPDX-License-Identifier: GPL-2.0-or-later
33

4+
#include "ajm_result.h"
45
#include "common/assert.h"
56
#include "core/libraries/ajm/ajm_at9.h"
67
#include "error_codes.h"
@@ -85,8 +86,8 @@ void AjmAt9Decoder::GetInfo(void* out_info) const {
8586
auto* info = reinterpret_cast<AjmSidebandDecAt9CodecInfo*>(out_info);
8687
info->super_frame_size = m_codec_info.superframeSize;
8788
info->frames_in_super_frame = m_codec_info.framesInSuperframe;
89+
info->next_frame_size = m_superframe_bytes_remain;
8890
info->frame_samples = m_codec_info.frameSamples;
89-
info->next_frame_size = static_cast<Atrac9Handle*>(m_handle)->Config.FrameBytes;
9091
}
9192

9293
u8 g_at9_guid[] = {0xD2, 0x42, 0xE1, 0x47, 0xBA, 0x36, 0x8D, 0x4D,
@@ -133,18 +134,22 @@ void AjmAt9Decoder::ParseRIFFHeader(std::span<u8>& in_buf, AjmInstanceGapless& g
133134
}
134135
}
135136

136-
std::tuple<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf,
137-
SparseOutputBuffer& output,
138-
AjmInstanceGapless& gapless) {
139-
bool is_reset = false;
137+
u32 AjmAt9Decoder::GetMinimumInputSize() const {
138+
return m_superframe_bytes_remain;
139+
}
140+
141+
DecoderResult AjmAt9Decoder::ProcessData(std::span<u8>& in_buf, SparseOutputBuffer& output,
142+
AjmInstanceGapless& gapless) {
143+
DecoderResult result{};
140144
if (True(m_flags & AjmAt9CodecFlags::ParseRiffHeader) &&
141145
*reinterpret_cast<u32*>(in_buf.data()) == 'FFIR') {
142146
ParseRIFFHeader(in_buf, gapless);
143-
is_reset = true;
147+
result.is_reset = true;
144148
}
145149

146150
if (!m_is_initialized) {
147-
return {0, 0, is_reset};
151+
result.result = ORBIS_AJM_RESULT_NOT_INITIALIZED;
152+
return result;
148153
}
149154

150155
int ret = 0;
@@ -166,7 +171,14 @@ std::tuple<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf,
166171
default:
167172
UNREACHABLE();
168173
}
169-
ASSERT_MSG(ret == At9Status::ERR_SUCCESS, "Atrac9Decode failed ret = {:#x}", ret);
174+
if (ret != At9Status::ERR_SUCCESS) {
175+
LOG_ERROR(Lib_Ajm, "Atrac9Decode failed ret = {:#x}", ret);
176+
result.result = ORBIS_AJM_RESULT_CODEC_ERROR | ORBIS_AJM_RESULT_FATAL;
177+
result.internal_result = ret;
178+
return result;
179+
}
180+
181+
result.frames_decoded += 1;
170182
in_buf = in_buf.subspan(bytes_used);
171183

172184
m_superframe_bytes_remain -= bytes_used;
@@ -196,10 +208,10 @@ std::tuple<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf,
196208
UNREACHABLE();
197209
}
198210

199-
const auto samples_written = pcm_written / m_codec_info.channels;
200-
gapless.current.skipped_samples += m_codec_info.frameSamples - samples_written;
211+
result.samples_written = pcm_written / m_codec_info.channels;
212+
gapless.current.skipped_samples += m_codec_info.frameSamples - result.samples_written;
201213
if (gapless.init.total_samples != 0) {
202-
gapless.current.total_samples -= samples_written;
214+
gapless.current.total_samples -= result.samples_written;
203215
}
204216

205217
m_num_frames += 1;
@@ -209,9 +221,23 @@ std::tuple<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf,
209221
}
210222
m_superframe_bytes_remain = m_codec_info.superframeSize;
211223
m_num_frames = 0;
224+
} else if (gapless.IsEnd()) {
225+
// Drain the remaining superframe
226+
std::vector<s16> buf(m_codec_info.frameSamples * m_codec_info.channels, 0);
227+
while ((m_num_frames % m_codec_info.framesInSuperframe) != 0) {
228+
ret = Atrac9Decode(m_handle, in_buf.data(), buf.data(), &bytes_used,
229+
True(m_flags & AjmAt9CodecFlags::NonInterleavedOutput));
230+
in_buf = in_buf.subspan(bytes_used);
231+
m_superframe_bytes_remain -= bytes_used;
232+
result.frames_decoded += 1;
233+
m_num_frames += 1;
234+
}
235+
in_buf = in_buf.subspan(m_superframe_bytes_remain);
236+
m_superframe_bytes_remain = m_codec_info.superframeSize;
237+
m_num_frames = 0;
212238
}
213239

214-
return {1, m_codec_info.frameSamples, is_reset};
240+
return result;
215241
}
216242

217243
AjmSidebandFormat AjmAt9Decoder::GetFormat() const {
@@ -232,7 +258,7 @@ u32 AjmAt9Decoder::GetNextFrameSize(const AjmInstanceGapless& gapless) const {
232258
const auto samples =
233259
gapless.init.total_samples != 0
234260
? std::min<u32>(gapless.current.total_samples, m_codec_info.frameSamples - skip_samples)
235-
: m_codec_info.frameSamples;
261+
: m_codec_info.frameSamples - skip_samples;
236262
return samples * m_codec_info.channels * GetPCMSize(m_format);
237263
}
238264

src/core/libraries/ajm/ajm_at9.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ struct AjmAt9Decoder final : AjmCodec {
3636
void Initialize(const void* buffer, u32 buffer_size) override;
3737
void GetInfo(void* out_info) const override;
3838
AjmSidebandFormat GetFormat() const override;
39+
u32 GetMinimumInputSize() const override;
3940
u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const override;
40-
std::tuple<u32, u32, bool> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
41-
AjmInstanceGapless& gapless) override;
41+
DecoderResult ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
42+
AjmInstanceGapless& gapless) override;
4243

4344
private:
4445
template <class T>

src/core/libraries/ajm/ajm_batch.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ struct AjmBatch {
5252
u32 id{};
5353
std::atomic_bool waiting{};
5454
std::atomic_bool canceled{};
55+
std::atomic_bool processed{};
5556
std::binary_semaphore finished{0};
5657
boost::container::small_vector<AjmJob, 16> jobs;
5758

src/core/libraries/ajm/ajm_context.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ s32 AjmContext::BatchCancel(const u32 batch_id) {
3939
batch = *p_batch;
4040
}
4141

42-
batch->canceled = true;
42+
if (batch->processed) {
43+
return ORBIS_OK;
44+
}
45+
46+
bool expected = false;
47+
batch->canceled.compare_exchange_strong(expected, true);
4348
return ORBIS_OK;
4449
}
4550

@@ -58,7 +63,9 @@ void AjmContext::WorkerThread(std::stop_token stop) {
5863
Common::SetCurrentThreadName("shadPS4:AjmWorker");
5964
while (!stop.stop_requested()) {
6065
auto batch = batch_queue.PopWait(stop);
61-
if (batch != nullptr) {
66+
if (batch != nullptr && !batch->canceled) {
67+
bool expected = false;
68+
batch->processed.compare_exchange_strong(expected, true);
6269
ProcessBatch(batch->id, batch->jobs);
6370
batch->finished.release();
6471
}

src/core/libraries/ajm/ajm_instance.cpp

Lines changed: 46 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,15 @@
11
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
22
// SPDX-License-Identifier: GPL-2.0-or-later
33

4-
#include "core/libraries/ajm/ajm_at9.h"
5-
#include "core/libraries/ajm/ajm_instance.h"
6-
#include "core/libraries/ajm/ajm_mp3.h"
4+
#include "ajm_at9.h"
5+
#include "ajm_instance.h"
6+
#include "ajm_mp3.h"
7+
#include "ajm_result.h"
78

89
#include <magic_enum/magic_enum.hpp>
910

1011
namespace Libraries::Ajm {
1112

12-
constexpr int ORBIS_AJM_RESULT_NOT_INITIALIZED = 0x00000001;
13-
constexpr int ORBIS_AJM_RESULT_INVALID_DATA = 0x00000002;
14-
constexpr int ORBIS_AJM_RESULT_INVALID_PARAMETER = 0x00000004;
15-
constexpr int ORBIS_AJM_RESULT_PARTIAL_INPUT = 0x00000008;
16-
constexpr int ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM = 0x00000010;
17-
constexpr int ORBIS_AJM_RESULT_STREAM_CHANGE = 0x00000020;
18-
constexpr int ORBIS_AJM_RESULT_TOO_MANY_CHANNELS = 0x00000040;
19-
constexpr int ORBIS_AJM_RESULT_UNSUPPORTED_FLAG = 0x00000080;
20-
constexpr int ORBIS_AJM_RESULT_SIDEBAND_TRUNCATED = 0x00000100;
21-
constexpr int ORBIS_AJM_RESULT_PRIORITY_PASSED = 0x00000200;
22-
constexpr int ORBIS_AJM_RESULT_CODEC_ERROR = 0x40000000;
23-
constexpr int ORBIS_AJM_RESULT_FATAL = 0x80000000;
24-
2513
u8 GetPCMSize(AjmFormatEncoding format) {
2614
switch (format) {
2715
case AjmFormatEncoding::S16:
@@ -60,6 +48,7 @@ void AjmInstance::Reset() {
6048

6149
void AjmInstance::ExecuteJob(AjmJob& job) {
6250
const auto control_flags = job.flags.control_flags;
51+
job.output.p_result->result = 0;
6352
if (True(control_flags & AjmJobControlFlags::Reset)) {
6453
LOG_TRACE(Lib_Ajm, "Resetting instance {}", job.instance_id);
6554
Reset();
@@ -91,8 +80,7 @@ void AjmInstance::ExecuteJob(AjmJob& job) {
9180
m_gapless.current.total_samples -= sample_difference;
9281
} else {
9382
LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_INVALID_PARAMETER");
94-
job.output.p_result->result = ORBIS_AJM_RESULT_INVALID_PARAMETER;
95-
return;
83+
job.output.p_result->result |= ORBIS_AJM_RESULT_INVALID_PARAMETER;
9684
}
9785
}
9886

@@ -106,61 +94,59 @@ void AjmInstance::ExecuteJob(AjmJob& job) {
10694
m_gapless.current.skip_samples -= sample_difference;
10795
} else {
10896
LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_INVALID_PARAMETER");
109-
job.output.p_result->result = ORBIS_AJM_RESULT_INVALID_PARAMETER;
110-
return;
97+
job.output.p_result->result |= ORBIS_AJM_RESULT_INVALID_PARAMETER;
11198
}
11299
}
113100
}
114101

115-
if (!job.input.buffer.empty() && !job.output.buffers.empty()) {
116-
std::span<u8> in_buf(job.input.buffer);
117-
SparseOutputBuffer out_buf(job.output.buffers);
102+
std::span<u8> in_buf(job.input.buffer);
103+
SparseOutputBuffer out_buf(job.output.buffers);
104+
auto in_size = in_buf.size();
105+
auto out_size = out_buf.Size();
106+
u32 frames_decoded = 0;
118107

119-
u32 frames_decoded = 0;
120-
auto in_size = in_buf.size();
121-
auto out_size = out_buf.Size();
122-
while (!in_buf.empty() && !out_buf.IsEmpty() && !m_gapless.IsEnd()) {
108+
if (!job.input.buffer.empty()) {
109+
for (;;) {
110+
if (m_flags.gapless_loop && m_gapless.IsEnd()) {
111+
m_gapless.Reset();
112+
m_total_samples = 0;
113+
}
123114
if (!HasEnoughSpace(out_buf)) {
124-
if (job.output.p_mframe == nullptr || frames_decoded == 0) {
125-
LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM ({} < {})",
126-
out_buf.Size(), m_codec->GetNextFrameSize(m_gapless));
127-
job.output.p_result->result = ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM;
128-
break;
129-
}
115+
LOG_TRACE(Lib_Ajm, "ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM ({} < {})", out_buf.Size(),
116+
m_codec->GetNextFrameSize(m_gapless));
117+
job.output.p_result->result |= ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM;
130118
}
131-
132-
const auto [nframes, nsamples, reset] =
133-
m_codec->ProcessData(in_buf, out_buf, m_gapless);
134-
if (reset) {
119+
if (in_buf.size() < m_codec->GetMinimumInputSize()) {
120+
job.output.p_result->result |= ORBIS_AJM_RESULT_PARTIAL_INPUT;
121+
}
122+
if (job.output.p_result->result != 0) {
123+
break;
124+
}
125+
const auto result = m_codec->ProcessData(in_buf, out_buf, m_gapless);
126+
if (result.is_reset) {
135127
m_total_samples = 0;
128+
} else {
129+
m_total_samples += result.samples_written;
136130
}
137-
if (!nframes) {
138-
LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_NOT_INITIALIZED");
139-
job.output.p_result->result = ORBIS_AJM_RESULT_NOT_INITIALIZED;
131+
frames_decoded += result.frames_decoded;
132+
if (result.result != 0) {
133+
job.output.p_result->result |= result.result;
134+
job.output.p_result->internal_result = result.internal_result;
140135
break;
141136
}
142-
frames_decoded += nframes;
143-
m_total_samples += nsamples;
144-
145137
if (False(job.flags.run_flags & AjmJobRunFlags::MultipleFrames)) {
146138
break;
147139
}
148140
}
141+
}
149142

150-
const auto total_decoded_samples = m_total_samples;
151-
if (m_flags.gapless_loop && m_gapless.IsEnd()) {
152-
in_buf = in_buf.subspan(in_buf.size());
153-
m_gapless.Reset();
154-
m_codec->Reset();
155-
}
156-
if (job.output.p_mframe) {
157-
job.output.p_mframe->num_frames = frames_decoded;
158-
}
159-
if (job.output.p_stream) {
160-
job.output.p_stream->input_consumed = in_size - in_buf.size();
161-
job.output.p_stream->output_written = out_size - out_buf.Size();
162-
job.output.p_stream->total_decoded_samples = total_decoded_samples;
163-
}
143+
if (job.output.p_mframe) {
144+
job.output.p_mframe->num_frames = frames_decoded;
145+
}
146+
if (job.output.p_stream) {
147+
job.output.p_stream->input_consumed = in_size - in_buf.size();
148+
job.output.p_stream->output_written = out_size - out_buf.Size();
149+
job.output.p_stream->total_decoded_samples = m_total_samples;
164150
}
165151

166152
if (job.output.p_format != nullptr) {
@@ -175,6 +161,9 @@ void AjmInstance::ExecuteJob(AjmJob& job) {
175161
}
176162

177163
bool AjmInstance::HasEnoughSpace(const SparseOutputBuffer& output) const {
164+
if (m_gapless.IsEnd()) {
165+
return true;
166+
}
178167
return output.Size() >= m_codec->GetNextFrameSize(m_gapless);
179168
}
180169

src/core/libraries/ajm/ajm_instance.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ struct AjmInstanceGapless {
7373
}
7474
};
7575

76+
struct DecoderResult {
77+
s32 result = 0;
78+
s32 internal_result = 0;
79+
u32 frames_decoded = 0;
80+
u32 samples_written = 0;
81+
bool is_reset = false;
82+
};
83+
7684
class AjmCodec {
7785
public:
7886
virtual ~AjmCodec() = default;
@@ -81,9 +89,10 @@ class AjmCodec {
8189
virtual void Reset() = 0;
8290
virtual void GetInfo(void* out_info) const = 0;
8391
virtual AjmSidebandFormat GetFormat() const = 0;
92+
virtual u32 GetMinimumInputSize() const = 0;
8493
virtual u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const = 0;
85-
virtual std::tuple<u32, u32, bool> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
86-
AjmInstanceGapless& gapless) = 0;
94+
virtual DecoderResult ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
95+
AjmInstanceGapless& gapless) = 0;
8796
};
8897

8998
class AjmInstance {
@@ -94,7 +103,6 @@ class AjmInstance {
94103

95104
private:
96105
bool HasEnoughSpace(const SparseOutputBuffer& output) const;
97-
std::optional<u32> GetNumRemainingSamples() const;
98106
void Reset();
99107

100108
AjmInstanceFlags m_flags{};

src/core/libraries/ajm/ajm_instance_statistics.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Libraries::Ajm {
88

99
void AjmInstanceStatistics::ExecuteJob(AjmJob& job) {
1010
if (job.output.p_engine) {
11-
job.output.p_engine->usage_batch = 0.01;
11+
job.output.p_engine->usage_batch = 0.05;
1212
const auto ic = job.input.statistics_engine_parameters->interval_count;
1313
for (u32 idx = 0; idx < ic; ++idx) {
1414
job.output.p_engine->usage_interval[idx] = 0.01;
@@ -25,10 +25,12 @@ void AjmInstanceStatistics::ExecuteJob(AjmJob& job) {
2525
job.output.p_memory->batch_size = 0x4200;
2626
job.output.p_memory->input_size = 0x2000;
2727
job.output.p_memory->output_size = 0x2000;
28-
job.output.p_memory->small_size = 0x200;
28+
job.output.p_memory->small_size = 0x400;
2929
}
3030
}
3131

32+
void AjmInstanceStatistics::Reset() {}
33+
3234
AjmInstanceStatistics& AjmInstanceStatistics::Getinstance() {
3335
static AjmInstanceStatistics instance;
3436
return instance;

src/core/libraries/ajm/ajm_instance_statistics.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace Libraries::Ajm {
1010
class AjmInstanceStatistics {
1111
public:
1212
void ExecuteJob(AjmJob& job);
13+
void Reset();
1314

1415
static AjmInstanceStatistics& Getinstance();
1516
};

0 commit comments

Comments
 (0)