Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HLE/AtracCtx2.cpp
5656 views
1
#include <algorithm>
2
3
#include "Common/Serialize/Serializer.h"
4
#include "Common/Serialize/SerializeFuncs.h"
5
#include "Common/Log.h"
6
#include "Core/MemMapHelpers.h"
7
#include "Core/HLE/HLE.h"
8
#include "Core/HLE/ErrorCodes.h"
9
#include "Core/HLE/FunctionWrappers.h"
10
#include "Core/System.h"
11
#include "Core/HLE/AtracCtx2.h"
12
#include "Core/Util/AtracTrack.h"
13
#include "Core/HW/Atrac3Standalone.h"
14
#include "Core/Config.h"
15
#include "Core/MemMap.h"
16
#include "Common/File/FileUtil.h"
17
18
// Convenient command line:
19
// Windows\x64\debug\PPSSPPHeadless.exe --root pspautotests/tests/../ -o --compare --new-atrac --timeout=30 --graphics=software pspautotests/tests/audio/atrac/stream.prx
20
//
21
// See the big comment in sceAtrac.cpp for an overview of the different modes of operation.
22
23
// To run on the real PSP, without gentest.py:
24
// cmd1> C:\dev\ppsspp\pspautotests\tests\audio\atrac> make
25
// cmd2> C:\dev\ppsspp\pspautotests> usbhostfs_pc -b 4000
26
// cmd3> C:\dev\ppsspp\pspautotests> pspsh -p 4000
27
// cmd3> > cd host0:/test/audio/atrac
28
// cmd3> stream.prx
29
// cmd1> C:\dev\ppsspp\pspautotests\tests\audio\atrac>copy /Y ..\..\..\__testoutput.txt stream.expected
30
// Then run the test, see above.
31
32
struct AT3BitrateMeta {
33
u16 sampleSize;
34
u8 dataByte;
35
u8 jointStereo; // I think?
36
};
37
38
static const AT3BitrateMeta g_at3BitrateMeta[5] = {
39
{0x180, 0x04, 0},
40
{0x130, 0x06, 0},
41
{0x0C0, 0x0B, 1},
42
{0x0C0, 0x0E, 0},
43
{0x098, 0x0F, 0},
44
};
45
46
// Needs to support negative numbers, and to handle non-powers-of-two.
47
static int RoundDownToMultiple(int size, int grain) {
48
return size - (size % grain);
49
}
50
51
static int RoundDownToMultipleWithOffset(int offset, int size, int grain) {
52
if (size > offset) {
53
return ((size - offset) / grain) * grain + offset;
54
} else {
55
return size;
56
}
57
}
58
59
static int ComputeSkipFrames(const SceAtracIdInfo &info, int seekPos) {
60
// No idea why this is the rule, but this is the rule.
61
return (seekPos & info.SamplesFrameMask()) < info.SkipSamples() ? 2 : 1;
62
}
63
64
static int ComputeFileOffset(const SceAtracIdInfo &info, int seekPos) {
65
int frameOffset = ((seekPos / info.SamplesPerFrame()) - 1) * info.sampleSize;
66
if ((seekPos & info.SamplesFrameMask()) < info.SkipSamples() && (frameOffset != 0)) {
67
frameOffset -= info.sampleSize;
68
}
69
return frameOffset + info.dataOff;
70
}
71
72
// Unlike the above, this one need to be inclusive.
73
static int ComputeLoopEndFileOffset(const SceAtracIdInfo &info, int seekPos) {
74
return (seekPos / info.SamplesPerFrame() + 1) * info.sampleSize + info.dataOff;
75
}
76
77
static int ComputeSpaceUsed(const SceAtracIdInfo &info) {
78
// The odd case: If streaming from the second buffer, and we're past the loop end (we're in the tail)...
79
if (info.decodePos > info.loopEnd && info.curBuffer == 1) {
80
int space = info.secondBufferByte;
81
if (info.secondStreamOff < space) {
82
space = RoundDownToMultipleWithOffset(info.secondStreamOff, info.secondBufferByte, info.sampleSize);
83
}
84
if ((info.secondStreamOff <= space) && (space - info.secondStreamOff < info.streamDataByte)) {
85
return info.streamDataByte - (space - info.secondStreamOff);
86
}
87
return 0;
88
}
89
90
// The normal case.
91
return info.streamDataByte;
92
}
93
94
static int ComputeRemainFrameStream(const SceAtracIdInfo &info) {
95
if (info.streamDataByte >= info.fileDataEnd - info.curFileOff) {
96
// Already done.
97
return PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY;
98
}
99
// Since we're streaming, the remaining frames are what's valid in the buffer.
100
return std::max(0, info.streamDataByte / info.sampleSize - (int)info.numSkipFrames);
101
}
102
103
// This got so complicated!
104
static int ComputeRemainFrameLooped(const SceAtracIdInfo &info) {
105
const int loopStartFileOffset = ComputeFileOffset(info, info.loopStart);
106
const int loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd);
107
const int writeFileOff = info.curFileOff + info.streamDataByte;
108
const int leftToRead = writeFileOff - loopEndFileOffset;
109
110
int remainFrames;
111
if (writeFileOff <= loopEndFileOffset) {
112
// Simple case - just divide to find the number of frames remaining in the buffer.
113
remainFrames = info.streamDataByte / info.sampleSize;
114
} else {
115
// Darn, we need to take looping into account...
116
const int skipFramesAtLoopStart = ComputeSkipFrames(info, info.loopStart);
117
const int firstPartLength = loopEndFileOffset - loopStartFileOffset;
118
const int secondPartLength = leftToRead % firstPartLength;
119
// Sum up all the parts (the buffered, the space remaining before the loop point
120
// and the space after the loop point), each divided by sample size, need to take skipped frames into account.
121
remainFrames = (loopEndFileOffset - info.curFileOff) / info.sampleSize +
122
(leftToRead / firstPartLength) * (firstPartLength / info.sampleSize - skipFramesAtLoopStart);
123
if (secondPartLength > skipFramesAtLoopStart * info.sampleSize) {
124
remainFrames += secondPartLength / info.sampleSize - skipFramesAtLoopStart;
125
}
126
}
127
128
// Clamp to zero.
129
remainFrames = std::max(0, remainFrames - (int)info.numSkipFrames);
130
if (info.loopNum < 0) {
131
// Infinite looping while streaming, we never return that we're done reading data.
132
return remainFrames;
133
}
134
135
// Additional check for distance to end of playback if we're looping a finite amount of times.
136
const int streamBufferEndFileOffset = info.curFileOff + info.streamDataByte;
137
if (streamBufferEndFileOffset >= loopEndFileOffset) {
138
const int numBufferedLoops = (streamBufferEndFileOffset - loopEndFileOffset) / (loopEndFileOffset - loopStartFileOffset);
139
if (info.loopNum <= numBufferedLoops) {
140
return PSP_ATRAC_LOOP_STREAM_DATA_IS_ON_MEMORY;
141
}
142
}
143
return remainFrames;
144
}
145
146
static void InitLengthAndLoop(SceAtracIdInfo *ctx, int endSample, int waveDataSize, int firstSampleOffset, int loopBegin, int loopEnd) {
147
const int off = ctx->codec == 0x1001 ? 0x45 : 0x170;
148
const int blockShift = 0x100b - ctx->codec;
149
const int firstValidSample = firstSampleOffset + off;
150
int numSamplesInFile;
151
if (endSample == 0) {
152
numSamplesInFile = waveDataSize / ctx->sampleSize << (blockShift & 0x1f);
153
} else {
154
numSamplesInFile = endSample + firstValidSample;
155
}
156
ctx->decodePos = firstValidSample;
157
ctx->loopNum = 0;
158
ctx->endSample = numSamplesInFile - 1;
159
ctx->numSkipFrames = (char)(firstValidSample >> (blockShift & 0x1f));
160
if (-1 < loopBegin) {
161
ctx->loopEnd = loopEnd + off;
162
ctx->loopStart = loopBegin + off;
163
return;
164
}
165
ctx->loopEnd = 0;
166
ctx->loopStart = 0;
167
return;
168
}
169
170
static int ComputeAtracStateAndInitSecondBuffer(SceAtracIdInfo *info, u32 readSize, u32 bufferSize) {
171
AtracStatus state;
172
int loopEndFileOffset;
173
int loopEnd;
174
175
if (bufferSize < (u32)info->fileDataEnd) {
176
if (info->streamDataByte < (s32)info->sampleSize * 2) {
177
return SCE_ERROR_ATRAC_SIZE_TOO_SMALL;
178
}
179
loopEnd = info->loopEnd;
180
state = ATRAC_STATUS_STREAMED_WITHOUT_LOOP;
181
if ((loopEnd != 0) && (state = ATRAC_STATUS_STREAMED_LOOP_FROM_END, loopEnd != info->endSample)) {
182
loopEndFileOffset = ComputeLoopEndFileOffset(*info, loopEnd);
183
loopEnd = (loopEndFileOffset - info->dataOff) + 1;
184
info->state = ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER;
185
if (loopEnd < info->streamDataByte) {
186
info->streamDataByte = loopEnd;
187
}
188
info->secondStreamOff = 0;
189
info->secondBuffer = 0;
190
info->secondBufferByte = 0;
191
return 0;
192
}
193
} else {
194
state = ATRAC_STATUS_HALFWAY_BUFFER;
195
if (readSize >= (u32)info->fileDataEnd) {
196
state = ATRAC_STATUS_ALL_DATA_LOADED;
197
}
198
}
199
info->state = state;
200
return 0;
201
}
202
203
int InitContextFromTrackInfo(SceAtracContext *ctx, const TrackInfo *wave, u32 bufferAddr, int readSize, int bufferSize) {
204
(ctx->info).numChan = (char)wave->numChans;
205
const int extraSamples = (ctx->info).codec == 0x1001 ? 0x45 : 0x170;
206
(ctx->info).firstValidSample = extraSamples + wave->firstSampleOffset;
207
int endSample3 = wave->endSample;
208
(ctx->info).sampleSize = wave->blockAlign;
209
InitLengthAndLoop(&ctx->info, endSample3, wave->waveDataSize, wave->firstSampleOffset, wave->loopStart, wave->loopEnd);
210
const int dataOff = wave->dataOff;
211
const int endSample = (ctx->info).endSample;
212
(ctx->info).streamDataByte = readSize - dataOff;
213
(ctx->info).buffer = bufferAddr;
214
(ctx->info).curFileOff = dataOff;
215
(ctx->info).dataOff = dataOff;
216
(ctx->info).fileDataEnd = wave->waveDataSize + dataOff;
217
(ctx->info).curBuffer = 0;
218
(ctx->info).bufferByte = bufferSize;
219
(ctx->info).streamOff = dataOff;
220
if ((ctx->info).loopEnd > endSample) {
221
return SCE_ERROR_ATRAC_BAD_CODEC_PARAMS;
222
}
223
224
const int numChunks = (endSample >> ((0x100b - (ctx->info).codec) & 0x1f));
225
226
if ((numChunks * (u32)(ctx->info).sampleSize < (wave->waveDataSize + dataOff) - dataOff)) {
227
int retval = ComputeAtracStateAndInitSecondBuffer(&ctx->info, readSize, bufferSize);
228
if (retval < 0) {
229
return retval;
230
}
231
if ((ctx->info).codec != PSP_CODEC_AT3) {
232
// At3plus
233
// Configure the codec for the sample size, or whatever that data is.
234
(ctx->codec).unk40 = wave->sampleSizeMaybe;
235
(ctx->codec).unk48 = 0;
236
(ctx->codec).unk41 = wave->tailFlag;
237
return 0;
238
}
239
// At3. Set up the hardware codec (hopefully we can correctly support this in sceAudiocodec and thus sceAtrac LLE in the future)
240
// This is not actually necessary since we don't use the actual hardware codec.
241
for (int counter = 4; counter >= 0; counter--) {
242
if ((g_at3BitrateMeta[counter].sampleSize == (ctx->info).sampleSize) &&
243
((int)g_at3BitrateMeta[counter].dataByte == wave->sampleSizeMaybe)) {
244
(ctx->codec).unk40 = (char)g_at3BitrateMeta[counter].jointStereo;
245
(ctx->codec).unk41 = 0;
246
(ctx->codec).unk42 = 0;
247
(ctx->codec).unk43 = 0;
248
return 0;
249
}
250
}
251
return 0;
252
} else {
253
return SCE_ERROR_ATRAC_BAD_CODEC_PARAMS;
254
}
255
}
256
257
int Atrac2::RemainingFrames() const {
258
const SceAtracIdInfo &info = context_->info;
259
260
// Handle the easy cases first.
261
switch (info.state) {
262
case ATRAC_STATUS_UNINITIALIZED:
263
case ATRAC_STATUS_NO_DATA:
264
return 0;
265
case ATRAC_STATUS_ALL_DATA_LOADED:
266
return PSP_ATRAC_ALLDATA_IS_ON_MEMORY; // Not sure about no data.
267
case ATRAC_STATUS_HALFWAY_BUFFER:
268
{
269
// Pretty simple - compute the remaining space, and divide by the sample size, adjusting for frames-to-skip.
270
const int writeFileOff = info.dataOff + info.streamDataByte;
271
if (info.curFileOff < writeFileOff) {
272
return std::max(0, (writeFileOff - info.curFileOff) / info.sampleSize - (int)info.numSkipFrames);
273
}
274
return 0;
275
}
276
case ATRAC_STATUS_STREAMED_WITHOUT_LOOP:
277
return ComputeRemainFrameStream(info);
278
279
case ATRAC_STATUS_STREAMED_LOOP_FROM_END:
280
return ComputeRemainFrameLooped(info);
281
282
case ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER:
283
if (info.decodePos <= info.loopEnd) {
284
// If before the tail, just treat it as looped.
285
return ComputeRemainFrameLooped(info);
286
} else {
287
// If in tail, treat is as unlooped.
288
return ComputeRemainFrameStream(info);
289
}
290
break;
291
default:
292
return SCE_ERROR_ATRAC_BAD_ATRACID;
293
}
294
}
295
296
Atrac2::Atrac2(u32 contextAddr, int codecType) {
297
if (contextAddr) {
298
context_ = PSPPointer<SceAtracContext>::Create(contextAddr);
299
// First-time initialization. The rest is initialized in SetData.
300
SceAtracIdInfo &info = context_->info;
301
info.codec = codecType;
302
info.state = ATRAC_STATUS_NO_DATA;
303
info.curBuffer = 0;
304
305
sas_.streamOffset = 0;
306
sas_.bufPtr[0] = 0;
307
sas_.bufPtr[1] = 0;
308
} else {
309
// We're loading state, we'll restore the context in DoState.
310
}
311
}
312
313
Atrac2::~Atrac2() {
314
DumpBufferToFile();
315
delete[] decodeTemp_;
316
// Nothing else to do here, the context is freed by the HLE.
317
}
318
319
void Atrac2::DumpBufferToFile() {
320
if (dumped_) {
321
// Already dumped, no need to dump again.
322
return;
323
}
324
if (!dumpBuffer_.empty()) {
325
std::string filename = StringFromFormat("atrac3_%08x_incomplete.at3", context_->info.endSample);
326
DumpFileIfEnabled(dumpBuffer_.data(), (u32)dumpBuffer_.size(), filename, DumpFileType::Atrac3);
327
dumpBuffer_.clear();
328
}
329
dumped_ = true;
330
}
331
332
void Atrac2::DoState(PointerWrap &p) {
333
auto s = p.Section("Atrac2", 1, 3);
334
if (!s)
335
return;
336
337
Do(p, outputChannels_);
338
// The only thing we need to save now is the outputChannels_ and the context pointer. And technically, not even that since
339
// it can be computed. Still, for future proofing, let's save it.
340
Do(p, context_);
341
342
// Actually, now we also need to save sas state. I guess this could also be saved on the Sas side, but this is easier.
343
if (s >= 2) {
344
Do(p, sas_.streamOffset);
345
Do(p, sas_.bufPtr[0]);
346
}
347
// Added support for streaming sas audio, need some more context state.
348
if (s >= 3) {
349
Do(p, sas_.bufPtr[1]);
350
Do(p, sas_.bufSize[0]);
351
Do(p, sas_.bufSize[1]);
352
Do(p, sas_.isStreaming);
353
Do(p, sas_.curBuffer);
354
Do(p, sas_.fileOffset);
355
}
356
357
const SceAtracIdInfo &info = context_->info;
358
if (p.mode == p.MODE_READ && info.state != ATRAC_STATUS_NO_DATA) {
359
CreateDecoder(info.codec, info.sampleSize, info.numChan);
360
}
361
}
362
363
bool Atrac2::HasSecondBuffer() const {
364
const SceAtracIdInfo &info = context_->info;
365
return info.secondBufferByte != 0;
366
}
367
368
int Atrac2::GetSoundSample(int *outEndSample, int *outLoopStartSample, int *outLoopEndSample) const {
369
const SceAtracIdInfo &info = context_->info;
370
*outEndSample = info.endSample - info.firstValidSample;
371
int loopEnd = -1;
372
if (info.loopEnd == 0) {
373
*outLoopStartSample = -1;
374
*outLoopEndSample = -1;
375
} else {
376
*outLoopStartSample = info.loopStart - info.firstValidSample;
377
*outLoopEndSample = info.loopEnd - info.firstValidSample;
378
}
379
return 0;
380
}
381
382
int Atrac2::ResetPlayPosition(int seekPos, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf, bool *delay) {
383
*delay = false;
384
385
// This was mostly copied straight from the old impl.
386
SceAtracIdInfo &info = context_->info;
387
if (info.state == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER && info.secondBufferByte == 0) {
388
return SCE_ERROR_ATRAC_SECOND_BUFFER_NEEDED;
389
}
390
391
seekPos += info.firstValidSample;
392
393
if ((u32)seekPos > (u32)info.endSample) {
394
return SCE_ERROR_ATRAC_BAD_SAMPLE;
395
}
396
397
int result = ResetPlayPositionInternal(seekPos, bytesWrittenFirstBuf, bytesWrittenSecondBuf);
398
if (result >= 0) {
399
int skipCount = 0;
400
result = SkipFrames(&skipCount);
401
if (skipCount) {
402
*delay = true;
403
}
404
}
405
return result;
406
}
407
408
u32 Atrac2::ResetPlayPositionInternal(int seekPos, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf) {
409
// Redo the same calculation as before, for input validation.
410
411
AtracResetBufferInfo bufferInfo;
412
GetResetBufferInfoInternal(&bufferInfo, seekPos);
413
414
// Input validation.
415
if ((u32)bytesWrittenFirstBuf < bufferInfo.first.minWriteBytes || (u32)bytesWrittenFirstBuf > bufferInfo.first.writableBytes) {
416
return SCE_ERROR_ATRAC_BAD_FIRST_RESET_SIZE;
417
}
418
if ((u32)bytesWrittenSecondBuf < bufferInfo.second.minWriteBytes || (u32)bytesWrittenSecondBuf > bufferInfo.second.writableBytes) {
419
return SCE_ERROR_ATRAC_BAD_SECOND_RESET_SIZE;
420
}
421
422
SceAtracIdInfo &info = context_->info;
423
info.decodePos = seekPos;
424
info.numSkipFrames = ComputeSkipFrames(info, seekPos);
425
info.loopNum = 0;
426
info.curFileOff = ComputeFileOffset(info, seekPos);
427
428
context_->codec.err = 0x20b; // wtf? testing shows it.
429
430
switch (info.state) {
431
case ATRAC_STATUS_ALL_DATA_LOADED:
432
// We're done.
433
return 0;
434
435
case ATRAC_STATUS_HALFWAY_BUFFER:
436
info.streamDataByte += bytesWrittenFirstBuf;
437
if (info.dataOff + info.streamDataByte >= info.fileDataEnd) {
438
// Buffer full, we can transition to a full buffer here, if all the bytes were written. Let's do it.
439
info.state = ATRAC_STATUS_ALL_DATA_LOADED;
440
}
441
return 0;
442
443
case ATRAC_STATUS_STREAMED_WITHOUT_LOOP:
444
case ATRAC_STATUS_STREAMED_LOOP_FROM_END:
445
// We just adopt the bytes that were written as our stream data, no math needed.
446
info.streamDataByte = bytesWrittenFirstBuf;
447
info.curBuffer = 0;
448
info.streamOff = 0;
449
return 0;
450
451
case ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER:
452
{
453
// As usual with the second buffer and trailer, things get tricky here.
454
const int loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd);
455
if (info.curFileOff >= loopEndFileOffset) {
456
const int secondBufferSizeRounded = RoundDownToMultiple(info.secondBufferByte, info.sampleSize);
457
if (info.curFileOff < loopEndFileOffset + secondBufferSizeRounded) {
458
info.streamDataByte = ((loopEndFileOffset + secondBufferSizeRounded) - info.curFileOff) + bytesWrittenFirstBuf;
459
info.curBuffer = 1;
460
info.secondStreamOff = info.curFileOff - loopEndFileOffset;
461
} else {
462
info.streamDataByte = bytesWrittenFirstBuf;
463
info.curBuffer = 2; // Temporary value! Will immediately switch back to 0.
464
info.streamOff = 0;
465
}
466
} else {
467
info.streamDataByte = bytesWrittenFirstBuf;
468
info.curBuffer = 0;
469
info.streamOff = 0;
470
}
471
return 0;
472
}
473
default:
474
_dbg_assert_(false);
475
return 0;
476
}
477
}
478
479
// This is basically sceAtracGetBufferInfoForResetting.
480
// NOTE: Not const! This can cause SkipFrames!
481
int Atrac2::GetBufferInfoForResetting(AtracResetBufferInfo *bufferInfo, int seekPos, bool *delay) {
482
const SceAtracIdInfo &info = context_->info;
483
484
if (info.state == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER && info.secondBufferByte == 0) {
485
return SCE_ERROR_ATRAC_SECOND_BUFFER_NEEDED;
486
}
487
488
seekPos += info.firstValidSample;
489
490
if ((u32)seekPos > (u32)info.endSample) {
491
return SCE_ERROR_ATRAC_BAD_SAMPLE;
492
}
493
494
GetResetBufferInfoInternal(bufferInfo, seekPos);
495
// Yes, this happens here! If there are any frames to skip, they get skipped!
496
// Even though this looks like a function that shouldn't change the state.
497
int skipCount = 0;
498
int retval = SkipFrames(&skipCount);
499
if (skipCount > 0)
500
*delay = true;
501
return retval;
502
}
503
504
void Atrac2::GetResetBufferInfoInternal(AtracResetBufferInfo *bufferInfo, int seekPos) const {
505
const SceAtracIdInfo &info = context_->info;
506
507
switch (info.state) {
508
case ATRAC_STATUS_NO_DATA:
509
case ATRAC_STATUS_ALL_DATA_LOADED:
510
// Everything is loaded, so nothing needs to be read.
511
bufferInfo->first.writePosPtr = info.buffer;
512
bufferInfo->first.writableBytes = 0;
513
bufferInfo->first.minWriteBytes = 0;
514
bufferInfo->first.filePos = 0;
515
break;
516
case ATRAC_STATUS_HALFWAY_BUFFER:
517
{
518
// Not too hard, we just ask to fill up the missing part of the buffer.
519
const int streamPos = info.dataOff + info.streamDataByte;
520
const int fileOff = info.dataOff + (seekPos / info.SamplesPerFrame() + 1) * info.sampleSize;
521
bufferInfo->first.writePosPtr = info.buffer + streamPos;
522
bufferInfo->first.writableBytes = info.fileDataEnd - streamPos;
523
bufferInfo->first.filePos = streamPos;
524
bufferInfo->first.minWriteBytes = std::max(0, fileOff - streamPos);
525
break;
526
}
527
528
case ATRAC_STATUS_STREAMED_WITHOUT_LOOP:
529
case ATRAC_STATUS_STREAMED_LOOP_FROM_END:
530
{
531
// Relatively easy, just can't forget those skipped frames.
532
const int curFileOffset = ComputeFileOffset(info, seekPos);
533
const int bufferEnd = RoundDownToMultiple(info.bufferByte, info.sampleSize);
534
bufferInfo->first.writePosPtr = info.buffer;
535
bufferInfo->first.writableBytes = std::min(info.fileDataEnd - curFileOffset, bufferEnd);
536
bufferInfo->first.minWriteBytes = (ComputeSkipFrames(info, seekPos) + 1) * info.sampleSize;
537
bufferInfo->first.filePos = curFileOffset;
538
break;
539
}
540
case ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER:
541
{
542
// As usual, with the second buffer, things get crazy complicated...
543
const int seekFileOffset = ComputeFileOffset(info, seekPos);
544
const int loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd) - 1;
545
const int bufferEnd = RoundDownToMultiple(info.bufferByte, info.sampleSize);
546
const int skipBytes = (ComputeSkipFrames(info, seekPos) + 1) * info.sampleSize;
547
const int secondBufferEnd = RoundDownToMultiple(info.secondBufferByte, info.sampleSize);
548
if (seekFileOffset < loopEndFileOffset) {
549
const int remainingBeforeLoop = (loopEndFileOffset - seekFileOffset) + 1;
550
bufferInfo->first.writePosPtr = info.buffer;
551
bufferInfo->first.writableBytes = std::min(bufferEnd, remainingBeforeLoop);
552
bufferInfo->first.minWriteBytes = std::min(skipBytes, remainingBeforeLoop);
553
bufferInfo->first.filePos = seekFileOffset;
554
} else if (loopEndFileOffset + secondBufferEnd <= seekFileOffset) {
555
bufferInfo->first.writePosPtr = info.buffer;
556
bufferInfo->first.writableBytes = std::min(info.fileDataEnd - seekFileOffset, bufferEnd);
557
bufferInfo->first.minWriteBytes = skipBytes;
558
bufferInfo->first.filePos = seekFileOffset;
559
} else if (loopEndFileOffset + (int)info.secondBufferByte + 1 < info.fileDataEnd) {
560
const int endOffset = loopEndFileOffset + secondBufferEnd + 1;
561
bufferInfo->first.writePosPtr = info.buffer;
562
bufferInfo->first.writableBytes = std::min(info.fileDataEnd - endOffset, bufferEnd);
563
bufferInfo->first.minWriteBytes = std::max(0, seekFileOffset + skipBytes - endOffset);
564
bufferInfo->first.filePos = endOffset;
565
} else {
566
bufferInfo->first.writePosPtr = info.buffer;
567
bufferInfo->first.writableBytes = 0;
568
bufferInfo->first.minWriteBytes = 0;
569
bufferInfo->first.filePos = 0;
570
}
571
break;
572
}
573
default:
574
_dbg_assert_(false);
575
break;
576
}
577
578
// Reset never needs a second buffer write, since the loop is in a fixed place.
579
// It seems like second.writePosPtr is always the same as the first buffer's pos, weirdly (doesn't make sense).
580
bufferInfo->second.writePosPtr = info.buffer;
581
bufferInfo->second.writableBytes = 0;
582
bufferInfo->second.minWriteBytes = 0;
583
bufferInfo->second.filePos = 0;
584
}
585
586
int Atrac2::SetLoopNum(int loopNum) {
587
SceAtracIdInfo &info = context_->info;
588
if (info.loopEnd <= 0) {
589
// File doesn't contain loop information, looping isn't allowed.
590
return SCE_ERROR_ATRAC_NO_LOOP_INFORMATION;
591
}
592
// Just override the current loop counter.
593
info.loopNum = loopNum;
594
return 0;
595
}
596
597
int Atrac2::LoopNum() const {
598
return context_->info.loopNum;
599
}
600
601
int Atrac2::LoopStatus() const {
602
const SceAtracIdInfo &info = context_->info;
603
if (info.loopEnd == 0) {
604
// No loop available.
605
return 0;
606
} else if (info.loopNum != 0) {
607
// We've got at least one loop to go.
608
return 1;
609
} else {
610
// Return 1 if we haven't passed the loop point.
611
return info.decodePos <= info.loopEnd ? 1 : 0;
612
}
613
}
614
615
u32 Atrac2::GetNextSamples() {
616
SceAtracIdInfo &info = context_->info;
617
// TODO: Need to reformulate this.
618
const int endOfCurrentFrame = info.decodePos | info.SamplesFrameMask(); // bit trick!
619
const int remainder = std::max(0, endOfCurrentFrame - info.endSample);
620
const int adjusted = (info.decodePos & info.SamplesFrameMask()) + remainder;
621
return std::max(0, info.SamplesPerFrame() - adjusted);
622
}
623
624
int Atrac2::GetNextDecodePosition(int *pos) const {
625
const SceAtracIdInfo &info = context_->info;
626
// Check if we reached the end.
627
if (info.decodePos > info.endSample) {
628
return SCE_ERROR_ATRAC_ALL_DATA_DECODED;
629
}
630
// Check if remaining data in the file is smaller than a frame.
631
if (info.fileDataEnd - info.curFileOff < info.sampleSize) {
632
return SCE_ERROR_ATRAC_ALL_DATA_DECODED;
633
}
634
*pos = info.decodePos - info.firstValidSample;
635
return 0;
636
}
637
638
int Atrac2::AddStreamData(u32 bytesToAdd) {
639
SceAtracIdInfo &info = context_->info;
640
641
// WARNING: bytesToAdd might not be sampleSize aligned, even though we return a sampleSize-aligned size
642
// in GetStreamDataInfo, so other parts of the code still has to handle unaligned data amounts.
643
if (info.state == ATRAC_STATUS_HALFWAY_BUFFER) {
644
const int newFileOffset = info.streamDataByte + info.dataOff + bytesToAdd;
645
if (newFileOffset == info.fileDataEnd) {
646
info.state = ATRAC_STATUS_ALL_DATA_LOADED;
647
} else if (newFileOffset > info.fileDataEnd) {
648
return SCE_ERROR_ATRAC_ADD_DATA_IS_TOO_BIG;
649
}
650
info.streamDataByte += bytesToAdd;
651
} else {
652
// TODO: Check for SCE_ERROR_ATRAC_ADD_DATA_IS_TOO_BIG in the other modes too.
653
info.streamDataByte += bytesToAdd;
654
}
655
return 0;
656
}
657
658
static int ComputeLoopedStreamWritableBytes(const SceAtracIdInfo &info, const int loopStartFileOffset, const u32 loopEndFileOffset) {
659
const u32 writeOffset = info.curFileOff + info.streamDataByte;
660
if (writeOffset >= loopEndFileOffset) {
661
const int loopLength = loopEndFileOffset - loopStartFileOffset;
662
return loopLength - ((info.curFileOff + info.streamDataByte) - loopEndFileOffset) % loopLength;
663
} else {
664
return loopEndFileOffset - writeOffset;
665
}
666
}
667
668
static int IncrementAndLoop(int curOffset, int increment, int loopStart, int loopEnd) {
669
const int sum = curOffset + increment;
670
if (sum >= loopEnd) {
671
return loopStart + (sum - loopEnd) % (loopEnd - loopStart);
672
} else {
673
return sum;
674
}
675
}
676
677
static int WrapAroundRoundedBufferSize(int offset, int bufferSize, int addend, int grainSize) {
678
bufferSize = RoundDownToMultipleWithOffset(offset, bufferSize, grainSize);
679
const int sum = offset + addend;
680
if (bufferSize <= sum) {
681
return sum - bufferSize;
682
} else {
683
return sum;
684
}
685
}
686
687
static void ComputeStreamBufferDataInfo(const SceAtracIdInfo &info, u32 *writePtr, u32 *bytesToWrite, u32 *readFileOffset) {
688
// Streaming data info
689
//
690
// This really is the core logic of sceAtrac. Initially I had it quite complicated, but boiled it
691
// all down to fairly simple logic. And then boiled it down further and fixed bugs.
692
// And then had to make it WAY complicated again to support looping... Sigh.
693
const u32 streamOff = info.curBuffer != 1 ? info.streamOff : 0;
694
const int spaceUsed = ComputeSpaceUsed(info);
695
const int spaceLeftAfterStreamOff = RoundDownToMultipleWithOffset(streamOff, info.bufferByte, info.sampleSize);
696
const int streamPos = streamOff + spaceUsed;
697
int spaceLeftInBuffer;
698
if (streamPos >= spaceLeftAfterStreamOff) {
699
spaceLeftInBuffer = spaceLeftAfterStreamOff - spaceUsed;
700
} else {
701
spaceLeftInBuffer = spaceLeftAfterStreamOff - streamPos;
702
}
703
const int loopStartFileOffset = ComputeFileOffset(info, info.loopStart);
704
const int loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd);
705
706
if (spaceLeftInBuffer < 0) {
707
// Most likely, the file was truncated.
708
WARN_LOG(Log::Atrac, "File corruption detected: spaceLeftInBuffer < 0: %d. Stumbling along.", spaceLeftInBuffer);
709
spaceLeftInBuffer = 0;
710
}
711
712
switch (info.state) {
713
case ATRAC_STATUS_STREAMED_WITHOUT_LOOP:
714
{
715
*bytesToWrite = std::clamp(info.fileDataEnd - (info.curFileOff + info.streamDataByte), 0, spaceLeftInBuffer);
716
const int streamFileOff = info.curFileOff + info.streamDataByte;
717
if (streamFileOff < info.fileDataEnd) {
718
*readFileOffset = streamFileOff;
719
*writePtr = info.buffer + WrapAroundRoundedBufferSize(info.streamOff, info.bufferByte, info.streamDataByte, info.sampleSize);
720
} else {
721
*readFileOffset = 0;
722
*writePtr = info.buffer;
723
}
724
break;
725
}
726
727
case ATRAC_STATUS_STREAMED_LOOP_FROM_END:
728
*bytesToWrite = std::min(ComputeLoopedStreamWritableBytes(info, loopStartFileOffset, loopEndFileOffset), spaceLeftInBuffer);
729
*readFileOffset = IncrementAndLoop(info.curFileOff, info.streamDataByte, loopStartFileOffset, loopEndFileOffset);
730
*writePtr = info.buffer + WrapAroundRoundedBufferSize(info.streamOff, info.bufferByte, info.streamDataByte, info.sampleSize);
731
break;
732
733
case ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER:
734
{
735
// Behaves like WITHOUT_LOOP or STREAMED_LOOP_FROM_END depending on the decode position.
736
if (info.decodePos <= info.loopEnd) {
737
*bytesToWrite = std::min(ComputeLoopedStreamWritableBytes(info, loopStartFileOffset, loopEndFileOffset), spaceLeftInBuffer);
738
*readFileOffset = IncrementAndLoop(info.curFileOff, info.streamDataByte, loopStartFileOffset, loopEndFileOffset);
739
} else {
740
const int streamFileOff = info.curFileOff + info.streamDataByte;
741
*bytesToWrite = std::clamp(info.fileDataEnd - streamFileOff, 0, spaceLeftInBuffer);
742
if (streamFileOff < info.fileDataEnd) {
743
*readFileOffset = streamFileOff;
744
} else {
745
*readFileOffset = 0;
746
}
747
}
748
if (info.decodePos <= info.loopEnd || info.curBuffer != 1) {
749
*writePtr = info.buffer + WrapAroundRoundedBufferSize(info.streamOff, info.bufferByte, info.streamDataByte, info.sampleSize);
750
} else {
751
*writePtr = info.buffer + WrapAroundRoundedBufferSize(0, info.bufferByte, spaceUsed, info.sampleSize);
752
}
753
break;
754
}
755
756
default:
757
// unreachable
758
_dbg_assert_(false);
759
break;
760
}
761
}
762
763
void Atrac2::GetStreamDataInfo(u32 *writePtr, u32 *bytesToWrite, u32 *readFileOffset) {
764
const SceAtracIdInfo &info = context_->info;
765
766
switch (info.state) {
767
case ATRAC_STATUS_ALL_DATA_LOADED:
768
// Nothing to do, the whole track is loaded already.
769
*writePtr = info.buffer;
770
*bytesToWrite = 0;
771
*readFileOffset = 0;
772
break;
773
774
case ATRAC_STATUS_HALFWAY_BUFFER:
775
{
776
// This is both the file offset and the offset in the buffer, since it's direct mapped
777
// in this mode (no wrapping or any other trickery).
778
const int fileOffset = (int)info.dataOff + (int)info.streamDataByte;
779
const int bytesLeftInFile = (int)info.fileDataEnd - fileOffset;
780
// Just ask for the rest of the data. The game can supply as much of it as it wants at a time.
781
*writePtr = info.buffer + fileOffset;
782
*bytesToWrite = bytesLeftInFile;
783
*readFileOffset = fileOffset;
784
break;
785
}
786
787
default:
788
ComputeStreamBufferDataInfo(info, writePtr, bytesToWrite, readFileOffset);
789
break;
790
}
791
}
792
793
u32 Atrac2::DecodeData(u8 *outbuf, u32 outbufAddr, int *SamplesNum, int *finish, int *remains) {
794
SceAtracIdInfo &info = context_->info;
795
796
const int tries = info.numSkipFrames + 1;
797
for (int i = 0; i < tries; i++) {
798
u32 result = DecodeInternal(outbufAddr, SamplesNum, finish);
799
if (result != 0) {
800
*SamplesNum = 0;
801
return result;
802
}
803
}
804
805
*remains = RemainingFrames();
806
return 0;
807
}
808
809
u32 Atrac2::DecodeInternal(u32 outbufAddr, int *SamplesNum, int *finish) {
810
SceAtracIdInfo &info = context_->info;
811
812
// Check that there's enough data to decode.
813
814
// Check for end of file.
815
const int samplesToDecode = GetNextSamples();
816
const int nextFileOff = info.curFileOff + info.sampleSize;
817
if (nextFileOff > info.fileDataEnd || info.decodePos > info.endSample) {
818
*finish = 1;
819
return SCE_ERROR_ATRAC_ALL_DATA_DECODED;
820
}
821
822
DEBUG_LOG(Log::Atrac, "Decode(%08x): samplesToDecode: %d nextFileOff: %d", outbufAddr, samplesToDecode, nextFileOff);
823
824
// Check for streaming buffer run-out.
825
if (AtracStatusIsStreaming(info.state) && info.streamDataByte < info.sampleSize) {
826
*finish = 0;
827
return SCE_ERROR_ATRAC_BUFFER_IS_EMPTY;
828
}
829
830
// Check for halfway buffer end.
831
if (info.state == ATRAC_STATUS_HALFWAY_BUFFER && info.dataOff + info.streamDataByte < nextFileOff) {
832
*finish = 0;
833
return SCE_ERROR_ATRAC_BUFFER_IS_EMPTY;
834
}
835
836
if (info.state == ATRAC_STATUS_FOR_SCESAS) {
837
_dbg_assert_(false);
838
}
839
840
u32 streamOff;
841
u32 bufferPtr;
842
if (!AtracStatusIsStreaming(info.state)) {
843
bufferPtr = info.buffer;
844
streamOff = info.curFileOff;
845
} else {
846
const int bufferIndex = info.curBuffer & 1;
847
bufferPtr = bufferIndex == 0 ? info.buffer : info.secondBuffer;
848
streamOff = bufferIndex == 0 ? info.streamOff : info.secondStreamOff;
849
}
850
851
u32 inAddr = bufferPtr + streamOff;
852
int16_t *outPtr;
853
854
_dbg_assert_(samplesToDecode <= info.SamplesPerFrame());
855
if (samplesToDecode != info.SamplesPerFrame()) {
856
if (!decodeTemp_) {
857
decodeTemp_ = new int16_t[info.SamplesPerFrame() * outputChannels_];
858
}
859
outPtr = decodeTemp_;
860
} else {
861
outPtr = outbufAddr ? (int16_t *)Memory::GetPointer(outbufAddr) : 0; // outbufAddr can be 0 during skip!
862
}
863
864
context_->codec.inBuf = inAddr;
865
context_->codec.outBuf = outbufAddr;
866
867
if (!Memory::IsValidAddress(inAddr)) {
868
ERROR_LOG(Log::Atrac, "DecodeInternal: Bad inAddr %08x", inAddr);
869
return SCE_ERROR_ATRAC_API_FAIL;
870
}
871
872
int bytesConsumed = 0;
873
int outSamples = 0;
874
if (!decoder_->Decode(Memory::GetPointerUnchecked(inAddr), info.sampleSize, &bytesConsumed, outputChannels_, outPtr, &outSamples)) {
875
// Decode failed.
876
*finish = 0;
877
// TODO: The error code here varies based on what the problem is, but not sure of the right values.
878
// 0000020b and 0000020c have been observed for 0xFF and/or garbage data, there may be more codes.
879
context_->codec.err = 0x20b;
880
return SCE_ERROR_ATRAC_API_FAIL; // tested.
881
} else {
882
context_->codec.err = 0;
883
}
884
885
_dbg_assert_(bytesConsumed == info.sampleSize);
886
887
// Advance the file offset.
888
info.curFileOff += info.sampleSize;
889
890
if (info.numSkipFrames == 0) {
891
*SamplesNum = samplesToDecode;
892
if (info.endSample < info.decodePos + samplesToDecode) {
893
*finish = info.loopNum == 0;
894
} else {
895
*finish = 0;
896
}
897
u8 *outBuf = outbufAddr ? Memory::GetPointerWrite(outbufAddr) : nullptr;
898
if (samplesToDecode != info.SamplesPerFrame() && samplesToDecode != 0 && outBuf) {
899
memcpy(outBuf, decodeTemp_, samplesToDecode * outputChannels_ * sizeof(int16_t));
900
}
901
902
// Handle increments and looping.
903
info.decodePos += samplesToDecode;
904
if (info.loopEnd != 0 && info.loopNum != 0 && info.decodePos > info.loopEnd) {
905
info.curFileOff = ComputeFileOffset(info, info.loopStart);
906
info.numSkipFrames = ComputeSkipFrames(info, info.loopStart);
907
info.decodePos = info.loopStart;
908
if (info.loopNum > 0) {
909
info.loopNum--;
910
}
911
}
912
} else {
913
info.numSkipFrames--;
914
}
915
916
// Handle streaming special cases.
917
if (AtracStatusIsStreaming(info.state)) {
918
info.streamDataByte -= info.sampleSize;
919
if (info.curBuffer == 1) {
920
// If currently streaming from the second buffer...
921
int nextStreamOff = info.secondStreamOff + info.sampleSize;
922
if ((int)info.secondBufferByte < nextStreamOff + info.sampleSize) {
923
// Done/ran out
924
info.streamOff = 0;
925
info.secondStreamOff = 0;
926
info.curBuffer = 2;
927
} else {
928
info.secondStreamOff = nextStreamOff;
929
}
930
} else {
931
// Normal streaming from the main buffer. Let's first look at wrapping around the end...
932
const int nextStreamOff = info.streamOff + info.sampleSize;
933
if (nextStreamOff + info.sampleSize > (int)info.bufferByte) {
934
info.streamOff = 0;
935
} else {
936
info.streamOff = nextStreamOff;
937
}
938
939
// OK, that's the simple stuff done. Moving on to second buffer streaming...
940
// This is quite a condition!
941
// Basically, if we're in state LOOP_WITH_TRAILER and currently streaming from the main buffer,
942
// and either there's no loop or we're just done with the final loop and haven't reached the loop point yet...
943
if (info.state == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER && info.curBuffer == 0 &&
944
(info.loopEnd == 0 || (info.loopNum == 0 && info.loopEnd < info.decodePos))) {
945
// If, at that point, our file streaming offset has indeed reached the loop point...
946
if (info.curFileOff >= ComputeLoopEndFileOffset(info, info.loopEnd)) {
947
// Then we switch to streaming from the secondary buffer, and also let's copy the last partial
948
// packet from the second buffer back to the start of the main buffer...
949
info.curBuffer = 1;
950
info.streamDataByte = info.secondBufferByte;
951
info.secondStreamOff = 0;
952
memcpy(Memory::GetPointerWrite(info.buffer),
953
Memory::GetPointer(info.secondBuffer + (info.secondBufferByte - info.secondBufferByte % info.sampleSize)),
954
info.secondBufferByte % info.sampleSize);
955
}
956
}
957
}
958
}
959
return 0;
960
}
961
962
int Atrac2::SetData(const Track &track, u32 bufferAddr, u32 readSize, u32 bufferSize, u32 fileSize, int outputChannels, bool isAA3) {
963
_dbg_assert_(outputChannels == 1 || outputChannels == 2);
964
TrackInfo trackInfo{};
965
if (Memory::IsValidAddress(bufferAddr)) {
966
// Turns out that games can abuse bufferSize, so we can't verify that it's a valid length with GetPointerRange.
967
const u8 *bufferPtr = Memory::GetPointerUnchecked(bufferAddr);
968
if (!Memory::IsValidRange(bufferAddr, readSize)) {
969
WARN_LOG(Log::Atrac, "Atrac2::SetData: Bad buffer range %08x+%08x - however, proceeeding.", bufferAddr, readSize);
970
}
971
if (!isAA3) {
972
int retval = ParseWaveAT3(bufferPtr, readSize, &trackInfo);
973
if (retval < 0) {
974
ERROR_LOG(Log::Atrac, "Atrac2::SetData: ParseWaveAT3 failed with %08x", retval);
975
return retval;
976
}
977
} else {
978
int retval = ParseAA3(bufferPtr, readSize, fileSize, &trackInfo);
979
if (retval < 0) {
980
ERROR_LOG(Log::Atrac, "Atrac2::SetData: ParseAA3 failed with %08x", retval);
981
return retval;
982
}
983
}
984
}
985
986
int retval = InitContextFromTrackInfo(context_, &trackInfo, bufferAddr, readSize, bufferSize);
987
if (retval < 0) {
988
ERROR_LOG(Log::Atrac, "Atrac2::SetData: InitContextFromTrackInfo failed with %08x", retval);
989
return retval;
990
}
991
992
SceAtracIdInfo &info = context_->info;
993
994
CreateDecoder(info.codec, info.sampleSize, info.numChan);
995
996
outputChannels_ = outputChannels;
997
998
INFO_LOG(Log::Atrac,
999
"Atrac: sampleSize: %d buffer: %08x bufferByte: %d firstValidSample: %d\n"
1000
"endSample: %d loopStart: %d loopEnd: %d\n"
1001
"dataOff: %d curFileOff: %d streamOff: %d streamDataByte: %d\n"
1002
"fileDataEnd: %d decodePos: %d numSkipFrames: %d channels: %d",
1003
info.sampleSize, info.buffer, info.bufferByte, info.firstValidSample,
1004
info.endSample, info.loopStart, info.loopEnd,
1005
info.dataOff, info.curFileOff, info.streamOff, info.streamDataByte,
1006
info.fileDataEnd, info.decodePos, info.numSkipFrames, info.numChan
1007
);
1008
1009
int skipCount = 0; // TODO: use for delay
1010
retval = SkipFrames(&skipCount);
1011
1012
// Seen in Mui Mui house. Things go very wrong after this..
1013
if (retval == SCE_ERROR_ATRAC_API_FAIL) {
1014
ERROR_LOG(Log::Atrac, "Bad frame during initial skip");
1015
} else if (retval != 0) {
1016
ERROR_LOG(Log::Atrac, "SkipFrames during InitContext returned an error: %08x", retval);
1017
}
1018
WrapLastPacket();
1019
return retval;
1020
}
1021
1022
void Atrac2::WrapLastPacket() {
1023
SceAtracIdInfo &info = context_->info;
1024
1025
// If streaming, we need to handle wrapping the overshot partial packet at the end. If not we don't.
1026
if (!AtracStatusIsStreaming(info.state)) {
1027
return;
1028
}
1029
1030
// This logic is similar to GetStreamDataInfo.
1031
int distanceToEnd = RoundDownToMultiple(info.bufferByte - info.streamOff, info.sampleSize);
1032
if (info.streamDataByte < distanceToEnd) {
1033
// There's space left without wrapping. Don't do anything.
1034
// INFO_LOG(Log::Atrac, "Packets fit into the buffer fully. %08x < %08x", readSize, bufferSize);
1035
// In this case, seems we need to zero some bytes. In one test, this seems to be 336.
1036
// Maybe there's a logical bug and the copy happens even when not needed, it's just that it'll
1037
// copy zeroes. Either way, let's just copy some bytes to make our sanity check hexdump pass.
1038
Memory::Memset(info.buffer, 0, 128);
1039
} else {
1040
// Wraps around.
1041
const int copyStart = info.streamOff + distanceToEnd;
1042
const int copyLen = info.bufferByte - copyStart;
1043
// Then, let's copy it.
1044
DEBUG_LOG(Log::Atrac, "Packets didn't fit evenly. Last packet got split into %d/%d (sum=%d). Copying to start of buffer.",
1045
copyLen, info.sampleSize - copyLen, info.sampleSize);
1046
Memory::Memcpy(info.buffer, info.buffer + copyStart, copyLen);
1047
}
1048
}
1049
1050
u32 Atrac2::SkipFrames(int *skipCount) {
1051
SceAtracIdInfo &info = context_->info;
1052
*skipCount = 0;
1053
int finishIgnored;
1054
while (true) {
1055
if (info.numSkipFrames == 0) {
1056
return 0;
1057
}
1058
u32 retval = DecodeInternal(0, 0, &finishIgnored);
1059
if (retval != 0) {
1060
if (retval == SCE_ERROR_ATRAC_API_FAIL) {
1061
WARN_LOG(Log::Atrac, "Failed during skip-frame, ignoring: %08x", retval);
1062
(*skipCount)++;
1063
}
1064
return retval;
1065
} else {
1066
DEBUG_LOG(Log::Atrac, "Frame correctly decoded during skip. numSkipFrames == %d", info.numSkipFrames);
1067
}
1068
(*skipCount)++;
1069
}
1070
return 0;
1071
}
1072
1073
// Where to read from to fill the second buffer.
1074
int Atrac2::GetSecondBufferInfo(u32 *fileOffset, u32 *readSize) const {
1075
const SceAtracIdInfo &info = context_->info;
1076
if (info.state != ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
1077
// No second buffer needed in this state.
1078
*fileOffset = 0;
1079
*readSize = 0;
1080
return SCE_ERROR_ATRAC_SECOND_BUFFER_NOT_NEEDED;
1081
}
1082
1083
const int loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd);
1084
*fileOffset = loopEndFileOffset;
1085
*readSize = info.fileDataEnd - loopEndFileOffset;
1086
return 0;
1087
}
1088
1089
int Atrac2::SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) {
1090
SceAtracIdInfo &info = context_->info;
1091
1092
u32 loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd);
1093
if ((info.sampleSize * 3 <= (int)secondBufferSize ||
1094
(info.fileDataEnd - loopEndFileOffset) <= (int)secondBufferSize)) {
1095
if (info.state == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
1096
info.secondBuffer = secondBuffer;
1097
info.secondBufferByte = secondBufferSize;
1098
info.secondStreamOff = 0;
1099
return 0;
1100
} else {
1101
return SCE_ERROR_ATRAC_SECOND_BUFFER_NOT_NEEDED;
1102
}
1103
}
1104
return SCE_ERROR_ATRAC_SIZE_TOO_SMALL;
1105
}
1106
1107
u32 Atrac2::GetInternalCodecError() const {
1108
if (context_.IsValid()) {
1109
return context_->codec.err;
1110
} else {
1111
return 0;
1112
}
1113
}
1114
1115
int Atrac2::Bitrate() const {
1116
const SceAtracIdInfo &info = context_->info;
1117
return info.BitRate();
1118
}
1119
1120
void Atrac2::InitLowLevel(const Atrac3LowLevelParams &params, int codecType) {
1121
SceAtracIdInfo &info = context_->info;
1122
info.codec = codecType;
1123
info.numChan = params.encodedChannels;
1124
outputChannels_ = params.outputChannels;
1125
info.sampleSize = params.bytesPerFrame;
1126
info.dataOff = 0;
1127
info.decodePos = 0;
1128
info.state = ATRAC_STATUS_LOW_LEVEL;
1129
CreateDecoder(codecType, info.sampleSize, info.numChan);
1130
}
1131
1132
int Atrac2::DecodeLowLevel(const u8 *srcData, int *bytesConsumed, s16 *dstData, int *bytesWritten) {
1133
SceAtracIdInfo &info = context_->info;
1134
1135
const int channels = outputChannels_;
1136
int outSamples = 0;
1137
bool success = decoder_->Decode(srcData, info.sampleSize, bytesConsumed, channels, dstData, &outSamples);
1138
if (!success) {
1139
ERROR_LOG(Log::Atrac, "Low level decoding failed: sampleSize: %d bytesConsumed: %d", info.sampleSize, *bytesConsumed);
1140
// We proceed anyway, see issue #20452
1141
/*
1142
*bytesConsumed = 0;
1143
*bytesWritten = 0;
1144
return SCE_ERROR_ATRAC_API_FAIL; // need to check what return value we get here.
1145
*/
1146
}
1147
*bytesWritten = outSamples * channels * sizeof(int16_t);
1148
// TODO: Possibly return a decode error on bad data.
1149
return 0;
1150
}
1151
1152
void Atrac2::CheckForSas() {
1153
SceAtracIdInfo &info = context_->info;
1154
if (info.numChan != 1) {
1155
WARN_LOG(Log::Atrac, "Caller forgot to set channels to 1");
1156
}
1157
if (info.state != 0x10) {
1158
WARN_LOG(Log::Atrac, "Caller forgot to set state to 0x10");
1159
}
1160
sas_.isStreaming = info.fileDataEnd > (s32)info.bufferByte;
1161
if (sas_.isStreaming) {
1162
INFO_LOG(Log::Atrac, "SasAtrac stream mode");
1163
} else {
1164
INFO_LOG(Log::Atrac, "SasAtrac non-streaming mode");
1165
}
1166
}
1167
1168
int Atrac2::EnqueueForSas(u32 address, u32 ptr) {
1169
SceAtracIdInfo &info = context_->info;
1170
// Set the new buffer up to be adopted by the next call to Decode that needs more data.
1171
// Note: Can't call this if the decoder isn't asking for another buffer to be queued.
1172
if (info.secondBuffer != 0xFFFFFFFF) {
1173
return SCE_SAS_ERROR_ATRAC3_ALREADY_QUEUED;
1174
}
1175
1176
if (address == 0 && ptr == 0) {
1177
WARN_LOG(Log::Atrac, "Caller tries to send us a zero buffer. Something went wrong.");
1178
}
1179
1180
DEBUG_LOG(Log::Atrac, "EnqueueForSas: Second buffer updated to %08x, sz: %08x", address, ptr);
1181
info.secondBuffer = address;
1182
info.secondBufferByte = ptr;
1183
return 0;
1184
}
1185
1186
// Completely different streaming setup!
1187
void Atrac2::DecodeForSas(s16 *dstData, int *bytesWritten, int *finish) {
1188
SceAtracIdInfo &info = context_->info;
1189
*bytesWritten = 0;
1190
1191
// First frame handling. Not sure if accurate. Set up the initial buffer as the current streaming buffer.
1192
// Also works for the non-streaming case.
1193
if (info.buffer) {
1194
sas_.curBuffer = 0;
1195
sas_.bufPtr[0] = info.buffer;
1196
sas_.bufSize[0] = info.bufferByte - info.streamOff; // also equals info.streamDataByte
1197
sas_.streamOffset = 0;
1198
sas_.fileOffset = info.bufferByte; // Possibly should just set it to info.curFileOff
1199
info.buffer = 0; // yes, this happens.
1200
}
1201
1202
u8 assembly[1000];
1203
// Keep decoding from the current buffer until it runs out.
1204
if (sas_.streamOffset + (int)info.sampleSize <= (int)sas_.bufSize[sas_.curBuffer]) {
1205
// Just decode.
1206
const u8 *srcData = Memory::GetPointer(sas_.bufPtr[sas_.curBuffer] + sas_.streamOffset);
1207
int bytesConsumed = 0;
1208
bool decodeResult = decoder_->Decode(srcData, info.sampleSize, &bytesConsumed, 1, dstData, bytesWritten);
1209
if (!decodeResult) {
1210
ERROR_LOG(Log::Atrac, "SAS failed to decode regular packet");
1211
}
1212
sas_.streamOffset += bytesConsumed;
1213
} else if (sas_.isStreaming) {
1214
// TODO: Do we need special handling for the first buffer, since SetData will wrap around that packet? I think yes!
1215
DEBUG_LOG(Log::Atrac, "Streaming atrac through sas, and hit the end of buffer %d", sas_.curBuffer);
1216
1217
// Compute the part sizes using the current size.
1218
int part1Size = sas_.bufSize[sas_.curBuffer] - sas_.streamOffset;
1219
int part2Size = info.sampleSize - part1Size;
1220
_dbg_assert_(part1Size >= 0);
1221
if (part1Size >= 0) {
1222
// Grab the partial packet, before we switch over to the other buffer.
1223
Memory::Memcpy(assembly, sas_.bufPtr[sas_.curBuffer] + sas_.streamOffset, part1Size);
1224
}
1225
1226
// Check if we hit the end.
1227
if (sas_.fileOffset >= info.fileDataEnd) {
1228
DEBUG_LOG(Log::Atrac, "Streaming and hit the file end.");
1229
*bytesWritten = 0;
1230
*finish = 1;
1231
return;
1232
}
1233
1234
// Check that a new buffer actually exists
1235
if (info.secondBuffer == sas_.bufPtr[sas_.curBuffer]) {
1236
ERROR_LOG(Log::Atrac, "Can't enqueue the same buffer twice in a row!");
1237
*bytesWritten = 0;
1238
*finish = 1;
1239
return;
1240
}
1241
1242
if ((int)info.secondBuffer < 0) {
1243
ERROR_LOG(Log::Atrac, "AtracSas streaming ran out of data, no secondbuffer pending");
1244
*bytesWritten = 0;
1245
*finish = 1;
1246
return;
1247
}
1248
1249
// Switch to the other buffer.
1250
sas_.curBuffer ^= 1;
1251
1252
sas_.bufPtr[sas_.curBuffer] = info.secondBuffer;
1253
sas_.bufSize[sas_.curBuffer] = info.secondBufferByte;
1254
sas_.fileOffset += info.secondBufferByte;
1255
1256
sas_.streamOffset = part2Size;
1257
1258
// If we'll reach the end during this buffer, set second buffer to 0, signaling that we don't need more data.
1259
if (sas_.fileOffset >= info.fileDataEnd) {
1260
// We've reached the end.
1261
info.secondBuffer = 0;
1262
DEBUG_LOG(Log::Atrac, "%08x >= %08x: Reached the end.", sas_.fileOffset, info.fileDataEnd);
1263
} else {
1264
// Signal to the caller that we accept a new next buffer.
1265
info.secondBuffer = 0xFFFFFFFF;
1266
}
1267
1268
DEBUG_LOG(Log::Atrac, "Switching over to buffer %d, updating buffer to %08x, sz: %08x. %s", sas_.curBuffer, info.secondBuffer, info.secondBufferByte, info.secondBuffer == 0xFFFFFFFF ? "Signalling for more data." : "");
1269
1270
// Copy the second half (or if part1Size == 0, the whole packet) to the assembly buffer.
1271
Memory::Memcpy(assembly + part1Size, sas_.bufPtr[sas_.curBuffer], part2Size);
1272
// Decode the packet from the assembly, whether it's was assembled from two or one.
1273
const u8 *srcData = assembly;
1274
int bytesConsumed = 0;
1275
bool decodeResult = decoder_->Decode(srcData, info.sampleSize, &bytesConsumed, 1, dstData, bytesWritten);
1276
if (!decodeResult) {
1277
ERROR_LOG(Log::Atrac, "SAS failed to decode assembled packet");
1278
}
1279
}
1280
}
1281
1282