Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HW/Atrac3Standalone.cpp
3186 views
1
#include "SimpleAudioDec.h"
2
#include "Common/LogReporting.h"
3
#include "ext/at3_standalone/at3_decoders.h"
4
5
inline int16_t clamp16(float f) {
6
if (f >= 1.0f)
7
return 32767;
8
else if (f <= -1.0f)
9
return -32767;
10
else
11
return (int)(f * 32767);
12
}
13
14
// Uses our standalone AT3/AT3+ decoder derived from FFMPEG
15
// Test case for ATRAC3: Mega Man Maverick Hunter X, PSP menu sound
16
class Atrac3Audio : public AudioDecoder {
17
public:
18
Atrac3Audio(PSPAudioType audioType, int channels, size_t blockAlign, const uint8_t *extraData, size_t extraDataSize)
19
: audioType_(audioType), channels_(channels) {
20
blockAlign_ = (int)blockAlign;
21
if (audioType_ == PSP_CODEC_AT3) {
22
at3Ctx_ = atrac3_alloc(channels, &blockAlign_, extraData, (int)extraDataSize);
23
if (at3Ctx_) {
24
codecOpen_ = true;
25
} else {
26
ERROR_LOG(Log::ME, "Failed to open atrac3 context! !channels=%d blockAlign=%d ed=%d)", channels, (int)blockAlign, (int)extraDataSize);
27
codecFailed_ = true;
28
}
29
}
30
for (int i = 0; i < 2; i++) {
31
buffers_[i] = new float[4096];
32
}
33
}
34
~Atrac3Audio() {
35
if (at3Ctx_) {
36
atrac3_free(at3Ctx_);
37
}
38
if (at3pCtx_) {
39
atrac3p_free(at3pCtx_);
40
}
41
for (int i = 0; i < 2; i++) {
42
delete[] buffers_[i];
43
}
44
}
45
46
bool IsOK() const override {
47
return codecOpen_;
48
}
49
50
void FlushBuffers() override {
51
if (at3Ctx_) {
52
atrac3_flush_buffers(at3Ctx_);
53
}
54
if (at3pCtx_) {
55
atrac3p_flush_buffers(at3pCtx_);
56
}
57
}
58
59
bool Decode(const uint8_t *inbuf, int inbytes, int *inbytesConsumed, int outputChannels, int16_t *outbuf, int *outSamples) override {
60
if (outSamples)
61
*outSamples = 0;
62
if (inbytesConsumed)
63
*inbytesConsumed = 0;
64
if (!codecOpen_) {
65
// We delay the codecOpen until the first decode, so the setChannels call from MediaEngine::getAudioSamples
66
// can take effect. Note, we don't do this with Atrac3, just Atrac3+.
67
if (codecFailed_) {
68
return false;
69
}
70
if (audioType_ == PSP_CODEC_AT3PLUS) {
71
at3pCtx_ = atrac3p_alloc(channels_, &blockAlign_);
72
if (at3pCtx_) {
73
codecOpen_ = true;
74
} else {
75
ERROR_LOG(Log::ME, "Failed to open atrac3+ context! (channels=%d blockAlign=%d)", channels_, (int)blockAlign_);
76
codecFailed_ = true;
77
}
78
}
79
if (!codecOpen_) {
80
WARN_LOG_N_TIMES(codecNotOpen, 5, Log::ME, "Atrac3Audio:Decode: Codec not open, not decoding");
81
return false;
82
}
83
}
84
if (inbytes != blockAlign_ && blockAlign_ != 0) {
85
WARN_LOG(Log::ME, "Atrac3Audio::Decode: inbytes not matching expected blockalign. Updating blockAlign_. Got %d bytes, expected %d. (%s)", inbytes, blockAlign_, at3pCtx_ ? "Atrac3+" : "Atrac3");
86
}
87
88
blockAlign_ = inbytes;
89
// We just call the decode function directly without going through the whole packet machinery.
90
int result;
91
int nb_samples = 0;
92
if (audioType_ == PSP_CODEC_AT3PLUS) {
93
result = atrac3p_decode_frame(at3pCtx_, buffers_, &nb_samples, inbuf, inbytes);
94
} else {
95
result = atrac3_decode_frame(at3Ctx_, buffers_, &nb_samples, inbuf, inbytes);
96
}
97
if (result < 0) {
98
// NOTE: Here, to recover from single bad packets, we update inBytesConsumed/outSamples with the regular packet size.
99
// Otherwise we might try to decode the same packet over and over.
100
// This is seen in some unofficial game mods, mainly.
101
if (inbytesConsumed) {
102
*inbytesConsumed = inbytes;
103
}
104
if (outSamples) {
105
if (*outSamples != 0) {
106
nb_samples = std::min(*outSamples, nb_samples);
107
}
108
*outSamples = nb_samples;
109
}
110
return false;
111
}
112
if (inbytesConsumed) {
113
*inbytesConsumed = result;
114
}
115
if (outSamples) {
116
// Allow capping the output samples by setting *outSamples to non-zero.
117
if (*outSamples != 0) {
118
nb_samples = std::min(*outSamples, nb_samples);
119
}
120
*outSamples = nb_samples;
121
}
122
if (nb_samples > 0) {
123
if (outSamples) {
124
*outSamples = nb_samples;
125
}
126
if (outbuf) {
127
_dbg_assert_(outputChannels == 1 || outputChannels == 2);
128
const float *left = buffers_[0];
129
if (outputChannels == 2) {
130
// Stereo output, standard.
131
const float *right = channels_ == 2 ? buffers_[1] : buffers_[0];
132
for (int i = 0; i < nb_samples; i++) {
133
outbuf[i * 2] = clamp16(left[i]);
134
outbuf[i * 2 + 1] = clamp16(right[i]);
135
}
136
} else {
137
// Mono output, just take the left channel.
138
for (int i = 0; i < nb_samples; i++) {
139
outbuf[i] = clamp16(left[i]);
140
}
141
}
142
}
143
}
144
return true;
145
}
146
147
void SetChannels(int channels) override {
148
_dbg_assert_(audioType_ == PSPAudioType::PSP_CODEC_AT3PLUS);
149
if (audioType_ == PSPAudioType::PSP_CODEC_AT3PLUS) {
150
if (!at3pCtx_) {
151
// Codec shouldn't be open yet.
152
_dbg_assert_(!codecOpen_);
153
channels_ = 1;
154
} else {
155
// This will come every packet, just ignore it.
156
}
157
}
158
}
159
160
PSPAudioType GetAudioType() const override { return audioType_; }
161
162
private:
163
ATRAC3PContext *at3pCtx_ = nullptr;
164
ATRAC3Context *at3Ctx_ = nullptr;
165
166
int channels_ = 0;
167
int blockAlign_ = 0;
168
169
float *buffers_[2]{};
170
171
bool codecOpen_ = false;
172
bool codecFailed_ = false;
173
174
PSPAudioType audioType_;
175
};
176
177
AudioDecoder *CreateAtrac3Audio(int channels, size_t blockAlign, const uint8_t *extraData, size_t extraDataSize) {
178
return new Atrac3Audio(PSP_CODEC_AT3, channels, blockAlign, extraData, extraDataSize);
179
}
180
AudioDecoder *CreateAtrac3PlusAudio(int channels, size_t blockAlign) {
181
return new Atrac3Audio(PSP_CODEC_AT3PLUS, channels, blockAlign, nullptr, 0);
182
}
183
184