diff --git a/Core/HLE/AtracBase.h b/Core/HLE/AtracBase.h index 73b373f7b9..e5329befa1 100644 --- a/Core/HLE/AtracBase.h +++ b/Core/HLE/AtracBase.h @@ -206,7 +206,8 @@ public: return decoder_; } - void CreateDecoder(int codecType, int bytesPerFrame, int channels); + // jointStereo only matters for Atrac3. Take it from the track header where there is one. + void CreateDecoder(int codecType, int bytesPerFrame, int channels, bool jointStereo); virtual void NotifyGetContextAddress() = 0; diff --git a/Core/HLE/AtracCtx.cpp b/Core/HLE/AtracCtx.cpp index a18337ea90..8ae9d2814a 100644 --- a/Core/HLE/AtracCtx.cpp +++ b/Core/HLE/AtracCtx.cpp @@ -141,7 +141,7 @@ void Atrac::DoState(PointerWrap &p) { // Make sure to do this late; it depends on track parameters. if (p.mode == p.MODE_READ && bufferState_ != ATRAC_STATUS_NO_DATA) { - CreateDecoder(track_.codecType, track_.bytesPerFrame, track_.channels); + CreateDecoder(track_.codecType, track_.bytesPerFrame, track_.channels, track_.jointStereo != 0); } if (s >= 2 && s < 9) { @@ -301,26 +301,27 @@ void Atrac::CalculateStreamInfo(u32 *outReadOffset) { } } -void AtracBase::CreateDecoder(int codecType, int bytesPerFrame, int channels) { +void AtracBase::CreateDecoder(int codecType, int bytesPerFrame, int channels, bool jointStereo) { if (decoder_) { delete decoder_; } // First, init the standalone decoder. if (codecType == PSP_CODEC_AT3) { - // TODO: This is maybe not entirely reliable? Mui Mui house in LocoRoco 2 fails. Although also fails - // when I override this, so maybe the issue is something different... - bool jointStereo = IsAtrac3StreamJointStereo(codecType, bytesPerFrame, channels); - - // We don't pull this from the RIFF so that we can support OMA also. + // The layout comes from the frame size and the joint stereo flag, as on hardware. A mono + // decoder still fills both output channels when the track says stereo. + int decoderChannels = channels; + if (!Atrac3DecoderChannels(bytesPerFrame, jointStereo, &decoderChannels)) { + WARN_LOG(Log::ME, "Atrac3: %d-byte frames with joint stereo %d aren't in libatrac3plus's table, decoding as %d channels", bytesPerFrame, (int)jointStereo, channels); + } + // Built here rather than taken from the RIFF, so that OMA works too. uint8_t extraData[14]{}; - // The only thing that changes are the jointStereo_ values. extraData[0] = 1; - extraData[3] = channels << 3; + extraData[3] = decoderChannels << 3; extraData[6] = jointStereo; extraData[8] = jointStereo; extraData[10] = 1; - decoder_ = CreateAtrac3Audio(channels, bytesPerFrame, extraData, sizeof(extraData)); + decoder_ = CreateAtrac3Audio(decoderChannels, bytesPerFrame, extraData, sizeof(extraData)); } else { decoder_ = CreateAtrac3PlusAudio(channels, bytesPerFrame); } @@ -465,7 +466,7 @@ int Atrac::SetData(const Track &track, u32 buffer, u32 readSize, u32 bufferSize, u32 copybytes = std::min(bufferSize, track_.fileSize); Memory::Memcpy(dataBuf_, buffer, copybytes, "AtracSetData"); } - CreateDecoder(track.codecType, track.bytesPerFrame, track.channels); + CreateDecoder(track.codecType, track.bytesPerFrame, track.channels, track.jointStereo != 0); INFO_LOG(Log::Atrac, "Atrac::SetData (buffer=%08x, readSize=%d, bufferSize=%d): %s %s (%d channels) audio", buffer, readSize, bufferSize, codecName, channelName, track_.channels); INFO_LOG(Log::Atrac, "BufferState: %s", AtracStatusToString(bufferState_)); INFO_LOG(Log::Atrac, @@ -942,7 +943,7 @@ void Atrac::InitLowLevel(const Atrac3LowLevelParams ¶ms, int codecType) { track_.fileSize = track_.bytesPerFrame; // not really meaningful bufferState_ = ATRAC_STATUS_LOW_LEVEL; currentSample_ = 0; - CreateDecoder(codecType, track_.bytesPerFrame, track_.channels); + CreateDecoder(codecType, track_.bytesPerFrame, track_.channels, track_.jointStereo != 0); WriteContextToPSPMem(); } diff --git a/Core/HLE/AtracCtx2.cpp b/Core/HLE/AtracCtx2.cpp index 5e7d4f5858..501876cb0a 100644 --- a/Core/HLE/AtracCtx2.cpp +++ b/Core/HLE/AtracCtx2.cpp @@ -337,7 +337,7 @@ void Atrac2::DumpBufferToFile() { } void Atrac2::DoState(PointerWrap &p) { - auto s = p.Section("Atrac2", 1, 3); + auto s = p.Section("Atrac2", 1, 4); if (!s) return; @@ -362,8 +362,14 @@ void Atrac2::DoState(PointerWrap &p) { } const SceAtracIdInfo &info = context_->info; + if (s >= 4) { + Do(p, jointStereo_); + } else if (p.mode == p.MODE_READ) { + jointStereo_ = IsAtrac3StreamJointStereo(info.codec, info.sampleSize, info.numChan); + } + if (p.mode == p.MODE_READ && info.state != ATRAC_STATUS_NO_DATA) { - CreateDecoder(info.codec, info.sampleSize, info.numChan); + CreateDecoder(info.codec, info.sampleSize, info.numChan, jointStereo_); } } @@ -1004,7 +1010,8 @@ int Atrac2::SetData(const Track &track, u32 bufferAddr, u32 readSize, u32 buffer SceAtracIdInfo &info = context_->info; - CreateDecoder(info.codec, info.sampleSize, info.numChan); + jointStereo_ = track.jointStereo != 0; + CreateDecoder(info.codec, info.sampleSize, info.numChan, jointStereo_); outputChannels_ = outputChannels; @@ -1140,7 +1147,9 @@ void Atrac2::InitLowLevel(const Atrac3LowLevelParams ¶ms, int codecType) { info.dataOff = 0; info.decodePos = 0; info.state = ATRAC_STATUS_LOW_LEVEL; - CreateDecoder(codecType, info.sampleSize, info.numChan); + // There's no track header here, so go by the bitrate. + jointStereo_ = IsAtrac3StreamJointStereo(codecType, info.sampleSize, info.numChan); + CreateDecoder(codecType, info.sampleSize, info.numChan, jointStereo_); } int Atrac2::DecodeLowLevel(const u8 *srcData, int *bytesConsumed, s16 *dstData, int *bytesWritten) { diff --git a/Core/HLE/AtracCtx2.h b/Core/HLE/AtracCtx2.h index ab553aa4c5..1ee03dfd89 100644 --- a/Core/HLE/AtracCtx2.h +++ b/Core/HLE/AtracCtx2.h @@ -77,6 +77,9 @@ private: // But it doesn't really matter whether it's here or there. AtracSasStreamState sas_; + // From the track header (RIFF fmt or OMA). The bitrate alone doesn't determine it (#8647). + bool jointStereo_ = false; + std::vector dumpBuffer_; // Used for dumping audio data to files. bool dumped_ = false; // Whether we already dumped the audio data to a file. }; diff --git a/Core/HLE/sceAudiocodec.cpp b/Core/HLE/sceAudiocodec.cpp index c6947622c8..d16e2736ed 100644 --- a/Core/HLE/sceAudiocodec.cpp +++ b/Core/HLE/sceAudiocodec.cpp @@ -757,6 +757,20 @@ static bool Atrac3LayoutFromContext(const SceAudiocodecCodec *ctx, int *bytesPer return true; } +// libatrac3plus.prx's SetData picks the parameter by frame size and the header's joint +// stereo flag, scanning from the last entry, and the channel count plays no part. LocoRoco 2 writes +// 2 channels into every track header it builds, and its 0xC0 MuiMui house track is mono (#8647). +// No match fails SetData with 0x80630008. +bool Atrac3DecoderChannels(int bytesPerFrame, bool jointStereo, int *channels) { + for (int i = ARRAY_SIZE(at3Params) - 1; i >= 0; i--) { + if (at3Params[i].bytes == bytesPerFrame && (at3Params[i].jointStereo != 0) == jointStereo) { + *channels = at3Params[i].channels; + return true; + } + } + return false; +} + bool IsAtrac3StreamJointStereo(int codecType, int bytesPerFrame, int channels) { if (codecType != PSP_CODEC_AT3) { // Well, might actually be, but it's not used in codec setup. diff --git a/Core/HLE/sceAudiocodec.h b/Core/HLE/sceAudiocodec.h index 138005cd3a..15f51bd3c4 100644 --- a/Core/HLE/sceAudiocodec.h +++ b/Core/HLE/sceAudiocodec.h @@ -128,6 +128,8 @@ class AudioDecoder; extern std::map g_audioDecoderContexts; bool IsAtrac3StreamJointStereo(int codecType, int bytesPerFrame, int channels); +// The channel count the Atrac3 decoder for a track works with, which can differ from the header's. +bool Atrac3DecoderChannels(int bytesPerFrame, bool jointStereo, int *channels); // ME time at the default clock, for sceAtrac too (it drives the same decoder): setting up a decoder, // and decoding one frame. AudioCodecDecodeUs takes Atrac3 and Atrac3+ only.