|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include "Common/CommonTypes.h" |
| 4 | +#include "Common/Swap.h" |
| 5 | + |
| 6 | +#include "Core/MemMap.h" |
| 7 | +#include "Core/HLE/sceAudiocodec.h" |
| 8 | + |
| 9 | +constexpr u32 ATRAC3_MAX_SAMPLES = 0x400; // 1024 |
| 10 | +constexpr u32 ATRAC3PLUS_MAX_SAMPLES = 0x800; // 2048 |
| 11 | + |
| 12 | +// The "state" member of SceAtracIdInfo. |
| 13 | +enum AtracStatus : u8 { |
| 14 | + ATRAC_STATUS_UNINITIALIZED = 0, // bad state |
| 15 | + |
| 16 | + ATRAC_STATUS_NO_DATA = 1, |
| 17 | + |
| 18 | + // The entire file is loaded into memory, no further file access needed. |
| 19 | + ATRAC_STATUS_ALL_DATA_LOADED = 2, |
| 20 | + |
| 21 | + // The buffer is sized to fit the entire file, but it's only partially loaded, so you can start playback before loading the whole file. |
| 22 | + ATRAC_STATUS_HALFWAY_BUFFER = 3, |
| 23 | + |
| 24 | + // In these ones, the buffer is smaller than the file, and data is streamed into it as needed for playback. |
| 25 | + // These are the most complex modes, both to implement and use. |
| 26 | + ATRAC_STATUS_STREAMED_WITHOUT_LOOP = 4, |
| 27 | + ATRAC_STATUS_STREAMED_LOOP_FROM_END = 5, |
| 28 | + // This means there's additional audio after the loop. |
| 29 | + // i.e. ~~before loop~~ [ ~~this part loops~~ ] ~~after loop~~ |
| 30 | + // The "fork in the road" means a second buffer is needed for the second path. |
| 31 | + ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER = 6, |
| 32 | + |
| 33 | + // In this mode, the only API to call is sceAtracLowLevelDecode, which decodes a stream packet by packet without any other metadata. |
| 34 | + ATRAC_STATUS_LOW_LEVEL = 8, |
| 35 | + |
| 36 | + // This mode is for using an Atrac context as the audio source for an sceSas channel. Not used a lot (Sol Trigger). |
| 37 | + ATRAC_STATUS_FOR_SCESAS = 16, |
| 38 | + |
| 39 | + // Bitwise-and the status with this to check for any of the streaming modes in a single test. |
| 40 | + ATRAC_STATUS_STREAMED_MASK = 4, |
| 41 | +}; |
| 42 | + |
| 43 | +const char *AtracStatusToString(AtracStatus status); |
| 44 | + |
| 45 | +inline bool AtracStatusIsStreaming(AtracStatus status) { |
| 46 | + return (status & ATRAC_STATUS_STREAMED_MASK) != 0; |
| 47 | +} |
| 48 | +inline bool AtracStatusIsNormal(AtracStatus status) { |
| 49 | + return (int)status >= ATRAC_STATUS_ALL_DATA_LOADED && (int)status <= ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER; |
| 50 | +} |
| 51 | + |
| 52 | +struct SceAtracIdInfo { |
| 53 | + s32 decodePos; // Sample position in the song that we'll next be decoding from. |
| 54 | + s32 endSample; // Last sample index of the track. |
| 55 | + s32 loopStart; // Start of the loop (sample index) |
| 56 | + s32 loopEnd; // End of the loop (sample index) |
| 57 | + s32 firstValidSample; // Seems to be the number of skipped samples at the start. After SetID, decodePos will match this. Was previously misnamed 'samplesPerChan'. |
| 58 | + u8 numSkipFrames; // This is 1 for a single frame when a loop is triggered, otherwise seems to stay at 0. Likely mis-named. |
| 59 | + AtracStatus state; // State enum, see AtracStatus. |
| 60 | + u8 curBuffer; // Current buffer (1 == second, 2 == done?) Previously unk |
| 61 | + u8 numChan; // Number of audio channels, usually 2 but 1 is possible. |
| 62 | + u16 sampleSize; // Size in bytes of an encoded audio frame. |
| 63 | + u16 codec; // Codec. 0x1000 is Atrac3+, 0x1001 is Atrac3. See the PSP_CODEC_ enum (only these two are supported). |
| 64 | + s32 dataOff; // File offset in bytes where the Atrac3+ frames start appearing. The first dummy packet starts here. |
| 65 | + s32 curFileOff; // File offset in bytes corresponding to the start of next packet that will be *decoded* (on the next call to sceAtracDecodeData). |
| 66 | + s32 fileDataEnd; // File size in bytes. |
| 67 | + s32 loopNum; // Current loop counter. If 0, will not loop. -1 loops for ever, positive numbers get decremented on the loop end. So to play a song 3 times and then end, set this to 2. |
| 68 | + s32 streamDataByte; // Number of bytes of queued/buffered/uploaded data. In full and half-way modes, this isn't decremented as you decode. |
| 69 | + s32 streamOff; // Streaming modes only: The byte offset inside the RAM buffer where sceAtracDecodeData will read from next. ONLY points to even packet boundaries. |
| 70 | + s32 secondStreamOff; // A kind of stream position in the secondary buffer. |
| 71 | + u32 buffer; // Address in RAM of the main buffer. |
| 72 | + u32 secondBuffer; // Address in RAM of the second buffer, or 0 if not used. |
| 73 | + u32 bufferByte; // Size in bytes of the main buffer. |
| 74 | + u32 secondBufferByte; // Size in bytes of the second buffer. |
| 75 | + // Offset 72 here. |
| 76 | + // make sure the size is 128 |
| 77 | + u32 unk[14]; |
| 78 | + |
| 79 | + // Simple helpers. Similar ones are on track_, but we shouldn't need track_ anymore when playing back. |
| 80 | + |
| 81 | + int SamplesPerFrame() const { |
| 82 | + return codec == 0x1000 ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES; |
| 83 | + } |
| 84 | + int SamplesFrameMask() const { |
| 85 | + return SamplesPerFrame() - 1; |
| 86 | + } |
| 87 | + int SkipSamples() const { |
| 88 | + // These first samples are skipped, after first possibly skipping 0-2 full frames, it seems. |
| 89 | + return codec == 0x1000 ? 0x170 : 0x45; |
| 90 | + } |
| 91 | + int BitRate() const { |
| 92 | + int bitrate = (sampleSize * 352800) / 1000; |
| 93 | + if (codec == PSP_CODEC_AT3PLUS) { |
| 94 | + bitrate = ((bitrate >> 11) + 8) & 0xFFFFFFF0; |
| 95 | + } else { |
| 96 | + bitrate = (bitrate + 511) >> 10; |
| 97 | + } |
| 98 | + return bitrate; |
| 99 | + } |
| 100 | +}; |
| 101 | + |
| 102 | +// One of these structs is allocated for each Atrac context. |
| 103 | +// The raw codec state is stored in 'codec'. |
| 104 | +// The internal playback state is stored in 'info', and that is used for all state keeping in the Atrac2 implementation, |
| 105 | +// imitating what happens on hardware as closely as possible. |
| 106 | +struct SceAtracContext { |
| 107 | + // size 128 |
| 108 | + SceAudiocodecCodec codec; |
| 109 | + // size 128 |
| 110 | + SceAtracIdInfo info; |
| 111 | +}; |
| 112 | + |
| 113 | +struct Atrac3LowLevelParams { |
| 114 | + int encodedChannels; |
| 115 | + int outputChannels; |
| 116 | + int bytesPerFrame; |
| 117 | +}; |
| 118 | + |
| 119 | +struct AtracSingleResetBufferInfo { |
| 120 | + u32_le writePosPtr; |
| 121 | + u32_le writableBytes; |
| 122 | + u32_le minWriteBytes; |
| 123 | + u32_le filePos; |
| 124 | +}; |
| 125 | + |
| 126 | +struct AtracResetBufferInfo { |
| 127 | + AtracSingleResetBufferInfo first; |
| 128 | + AtracSingleResetBufferInfo second; |
| 129 | +}; |
| 130 | + |
| 131 | +struct AtracSasStreamState { |
| 132 | + u32 bufPtr[2]{}; |
| 133 | + u32 bufSize[2]{}; |
| 134 | + int streamOffset = 0; |
| 135 | + int fileOffset = 0; |
| 136 | + int curBuffer = 0; |
| 137 | + bool isStreaming = false; |
| 138 | + |
| 139 | + int CurPos() const { |
| 140 | + int retval = fileOffset - bufSize[curBuffer] + streamOffset; |
| 141 | + _dbg_assert_(retval >= 0); |
| 142 | + return retval; |
| 143 | + } |
| 144 | +}; |
| 145 | + |
| 146 | +const int PSP_ATRAC_ALLDATA_IS_ON_MEMORY = -1; |
| 147 | +const int PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY = -2; |
| 148 | +const int PSP_ATRAC_LOOP_STREAM_DATA_IS_ON_MEMORY = -3; |
| 149 | + |
| 150 | +// This is not a PSP-native struct. |
| 151 | +// But, it's stored in its entirety in savestates, which makes it awkward to change it. |
| 152 | +// This is used for both first_ and second_, but the latter doesn't use all the fields. |
| 153 | +struct InputBuffer { |
| 154 | + // Address of the buffer. |
| 155 | + u32 addr; |
| 156 | + // Size of data read so far into dataBuf_ (to be removed.) |
| 157 | + u32 size; |
| 158 | + // Offset into addr at which new data is added. |
| 159 | + u32 offset; |
| 160 | + // Last writableBytes number (to be removed.) |
| 161 | + u32 writableBytes; |
| 162 | + // Unused, always 0. |
| 163 | + u32 neededBytes; |
| 164 | + // Total size of the entire file data. |
| 165 | + u32 _filesize_dontuse; |
| 166 | + // Offset into the file at which new data is read. |
| 167 | + u32 fileoffset; |
| 168 | +}; |
| 169 | + |
| 170 | +class AudioDecoder; |
| 171 | +class PointerWrap; |
| 172 | +struct Track; |
| 173 | + |
| 174 | +class AtracBase { |
| 175 | +public: |
| 176 | + virtual ~AtracBase(); |
| 177 | + |
| 178 | + virtual void DoState(PointerWrap &p) = 0; |
| 179 | + |
| 180 | + // TODO: Find a way to get rid of this from the base class. |
| 181 | + virtual void UpdateContextFromPSPMem() = 0; |
| 182 | + |
| 183 | + virtual int Channels() const = 0; |
| 184 | + |
| 185 | + int GetOutputChannels() const { |
| 186 | + return outputChannels_; |
| 187 | + } |
| 188 | + void SetOutputChannels(int channels) { |
| 189 | + // Only used for sceSas audio. To be refactored away in the future. |
| 190 | + outputChannels_ = channels; |
| 191 | + } |
| 192 | + |
| 193 | + virtual u32 GetInternalCodecError() const { return 0; } |
| 194 | + |
| 195 | + PSPPointer<SceAtracContext> context_{}; |
| 196 | + |
| 197 | + virtual AtracStatus BufferState() const = 0; |
| 198 | + |
| 199 | + virtual int SetLoopNum(int loopNum) = 0; |
| 200 | + virtual int LoopNum() const = 0; |
| 201 | + virtual int LoopStatus() const = 0; |
| 202 | + |
| 203 | + virtual int CodecType() const = 0; |
| 204 | + |
| 205 | + AudioDecoder *Decoder() const { |
| 206 | + return decoder_; |
| 207 | + } |
| 208 | + |
| 209 | + void CreateDecoder(int codecType, int bytesPerFrame, int channels); |
| 210 | + |
| 211 | + virtual void NotifyGetContextAddress() = 0; |
| 212 | + |
| 213 | + virtual int GetNextDecodePosition(int *pos) const = 0; |
| 214 | + virtual int RemainingFrames() const = 0; |
| 215 | + virtual bool HasSecondBuffer() const = 0; |
| 216 | + virtual int Bitrate() const = 0; |
| 217 | + virtual int BytesPerFrame() const = 0; |
| 218 | + virtual int SamplesPerFrame() const = 0; |
| 219 | + |
| 220 | + virtual void GetStreamDataInfo(u32 *writePtr, u32 *writableBytes, u32 *readOffset) = 0; // This should be const, but the legacy impl stops it (it's wrong). |
| 221 | + virtual int AddStreamData(u32 bytesToAdd) = 0; |
| 222 | + virtual int ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf, bool *delay) = 0; |
| 223 | + virtual int GetBufferInfoForResetting(AtracResetBufferInfo *bufferInfo, int sample, bool *delay) = 0; // NOTE: Not const! This can cause SkipFrames! |
| 224 | + virtual int SetData(const Track &track, u32 buffer, u32 readSize, u32 bufferSize, int outputChannels) = 0; |
| 225 | + |
| 226 | + virtual int GetSecondBufferInfo(u32 *fileOffset, u32 *desiredSize) const = 0; |
| 227 | + virtual int SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) = 0; |
| 228 | + virtual u32 DecodeData(u8 *outbuf, u32 outbufPtr, int *SamplesNum, int *finish, int *remains) = 0; |
| 229 | + virtual int DecodeLowLevel(const u8 *srcData, int *bytesConsumed, s16 *dstData, int *bytesWritten) = 0; |
| 230 | + |
| 231 | + virtual u32 GetNextSamples() = 0; // This should be const, but the legacy impl stops it (it's wrong). |
| 232 | + virtual void InitLowLevel(const Atrac3LowLevelParams ¶ms, int codecType) = 0; |
| 233 | + |
| 234 | + virtual void CheckForSas() = 0; |
| 235 | + virtual int EnqueueForSas(u32 address, u32 ptr) = 0; |
| 236 | + virtual void DecodeForSas(s16 *dstData, int *bytesWritten, int *finish) = 0; |
| 237 | + virtual const AtracSasStreamState *StreamStateForSas() const { return nullptr; } |
| 238 | + |
| 239 | + virtual int GetSoundSample(int *endSample, int *loopStartSample, int *loopEndSample) const = 0; |
| 240 | + |
| 241 | + virtual int GetContextVersion() const = 0; |
| 242 | + |
| 243 | +protected: |
| 244 | + u16 outputChannels_ = 2; |
| 245 | + |
| 246 | + // TODO: Save the internal state of this, now technically possible. |
| 247 | + AudioDecoder *decoder_ = nullptr; |
| 248 | +}; |
0 commit comments