CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/HLE/AtracCtx.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "Common/Serialize/Serializer.h"18#include "Common/Serialize/SerializeFuncs.h"19#include "Common/Log.h"20#include "Core/Reporting.h"21#include "Core/MemMapHelpers.h"22#include "Core/System.h"23#include "Core/HLE/HLE.h"24#include "Core/HLE/FunctionWrappers.h"25#include "Core/HLE/sceAtrac.h"26#include "Core/HLE/AtracCtx.h"27#include "Core/HW/Atrac3Standalone.h"28#include "Core/HLE/sceKernelMemory.h"2930const size_t overAllocBytes = 16384;3132const int RIFF_CHUNK_MAGIC = 0x46464952;33const int RIFF_WAVE_MAGIC = 0x45564157;34const int FMT_CHUNK_MAGIC = 0x20746D66;35const int DATA_CHUNK_MAGIC = 0x61746164;36const int SMPL_CHUNK_MAGIC = 0x6C706D73;37const int FACT_CHUNK_MAGIC = 0x74636166;3839void Atrac::DoState(PointerWrap &p) {40auto s = p.Section("Atrac", 1, 9);41if (!s)42return;4344Do(p, track_.channels);45Do(p, outputChannels_);46if (s >= 5) {47Do(p, track_.jointStereo);48}4950Do(p, atracID_);51if (p.mode != p.MODE_READ) {52first_._filesize_dontuse = track_.fileSize;53}54Do(p, first_);55if (p.mode == p.MODE_READ) {56track_.fileSize = first_._filesize_dontuse;57}5859Do(p, bufferMaxSize_);60Do(p, track_.codecType);6162Do(p, currentSample_);63Do(p, track_.endSample);64Do(p, track_.firstSampleOffset);65if (s >= 3) {66Do(p, track_.dataByteOffset);67} else {68track_.dataByteOffset = track_.firstSampleOffset;69}7071u32 hasDataBuf = dataBuf_ != nullptr;72Do(p, hasDataBuf);73if (hasDataBuf) {74if (p.mode == p.MODE_READ) {75if (dataBuf_)76delete[] dataBuf_;77dataBuf_ = new u8[track_.fileSize + overAllocBytes];78memset(dataBuf_, 0, track_.fileSize + overAllocBytes);79}80DoArray(p, dataBuf_, track_.fileSize);81}82Do(p, second_);8384Do(p, decodePos_);85if (s < 9) {86u32 oldDecodeEnd = 0;87Do(p, oldDecodeEnd);88}89if (s >= 4) {90Do(p, bufferPos_);91} else {92bufferPos_ = decodePos_;93}9495Do(p, track_.bitrate);96Do(p, track_.bytesPerFrame);9798Do(p, track_.loopinfo);99if (s < 9) {100int oldLoopInfoNum = 42;101Do(p, oldLoopInfoNum);102}103104Do(p, track_.loopStartSample);105Do(p, track_.loopEndSample);106Do(p, loopNum_);107108Do(p, context_);109if (s >= 6) {110Do(p, bufferState_);111} else {112if (dataBuf_ == nullptr) {113bufferState_ = ATRAC_STATUS_NO_DATA;114} else {115UpdateBufferState();116}117}118119if (s >= 7) {120Do(p, ignoreDataBuf_);121} else {122ignoreDataBuf_ = false;123}124125if (s >= 9) {126Do(p, bufferValidBytes_);127Do(p, bufferHeaderSize_);128} else {129bufferHeaderSize_ = track_.dataByteOffset;130bufferValidBytes_ = std::min(first_.size - track_.dataByteOffset, StreamBufferEnd() - track_.dataByteOffset);131if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {132bufferPos_ = track_.dataByteOffset;133}134}135136if (s < 8 && bufferState_ == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {137// We didn't actually allow the second buffer to be set this far back.138// Pretend it's a regular loop, we'll just try our best.139bufferState_ = ATRAC_STATUS_STREAMED_LOOP_FROM_END;140}141142// Make sure to do this late; it depends on things like bytesPerFrame_.143if (p.mode == p.MODE_READ && bufferState_ != ATRAC_STATUS_NO_DATA) {144CreateDecoder();145}146147if (s >= 2 && s < 9) {148bool oldResetBuffer = false;149Do(p, oldResetBuffer);150}151}152153void Atrac::ResetData() {154delete decoder_;155decoder_ = nullptr;156157if (dataBuf_)158delete[] dataBuf_;159dataBuf_ = 0;160ignoreDataBuf_ = false;161bufferState_ = ATRAC_STATUS_NO_DATA;162163if (context_.IsValid())164kernelMemory.Free(context_.ptr);165}166167void Atrac::AnalyzeReset() {168// Reset some values.169track_.AnalyzeReset();170171currentSample_ = 0;172loopNum_ = 0;173decodePos_ = 0;174bufferPos_ = 0;175}176177u8 *Atrac::BufferStart() {178return ignoreDataBuf_ ? Memory::GetPointerWrite(first_.addr) : dataBuf_;179}180181void AtracBase::UpdateContextFromPSPMem() {182if (!context_.IsValid()) {183return;184}185186// Read in any changes from the game to the context.187// TODO: Might be better to just always track in RAM.188bufferState_ = context_->info.state;189// This value is actually abused by games to store the SAS voice number.190loopNum_ = context_->info.loopNum;191}192193void Atrac::WriteContextToPSPMem() {194if (!context_.IsValid()) {195return;196}197// context points into PSP memory.198SceAtracContext *context = context_;199context->info.buffer = first_.addr;200context->info.bufferByte = bufferMaxSize_;201context->info.secondBuffer = second_.addr;202context->info.secondBufferByte = second_.size;203context->info.codec = track_.codecType;204context->info.loopNum = loopNum_;205context->info.loopStart = track_.loopStartSample > 0 ? track_.loopStartSample : 0;206context->info.loopEnd = track_.loopEndSample > 0 ? track_.loopEndSample : 0;207208// Note that we read in the state when loading the atrac object, so it's safe209// to update it back here all the time. Some games, like Sol Trigger, change it.210// TODO: Should we just keep this in PSP ram then, or something?211context->info.state = bufferState_;212if (track_.firstSampleOffset != 0) {213context->info.samplesPerChan = track_.FirstSampleOffsetFull();214} else {215context->info.samplesPerChan = (track_.codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);216}217context->info.sampleSize = track_.bytesPerFrame;218context->info.numChan = track_.channels;219context->info.dataOff = track_.dataByteOffset;220context->info.endSample = track_.endSample + track_.FirstSampleOffsetFull();221context->info.dataEnd = track_.fileSize;222context->info.curOff = first_.fileoffset;223context->info.decodePos = track_.DecodePosBySample(currentSample_);224context->info.streamDataByte = first_.size - track_.dataByteOffset;225226u8 *buf = (u8 *)context;227*(u32_le *)(buf + 0xfc) = atracID_;228229NotifyMemInfo(MemBlockFlags::WRITE, context_.ptr, sizeof(SceAtracContext), "AtracContext");230}231232void Track::DebugLog() {233DEBUG_LOG(Log::ME, "ATRAC analyzed: %s channels: %d filesize: %d bitrate: %d kbps jointStereo: %d",234codecType == PSP_MODE_AT_3 ? "AT3" : "AT3Plus", channels, fileSize, bitrate / 1024, jointStereo);235DEBUG_LOG(Log::ME, "dataoff: %d firstSampleOffset: %d endSample: %d", dataByteOffset, firstSampleOffset, endSample);236DEBUG_LOG(Log::ME, "loopStartSample: %d loopEndSample: %d", loopStartSample, loopEndSample);237}238239int Atrac::Analyze(u32 addr, u32 size) {240track_ = {};241first_ = {};242first_.addr = addr;243first_.size = size;244245AnalyzeReset();246247// 72 is about the size of the minimum required data to even be valid.248if (size < 72) {249return hleReportError(Log::ME, ATRAC_ERROR_SIZE_TOO_SMALL, "buffer too small");250}251252// TODO: Check the range (addr, size) instead.253if (!Memory::IsValidAddress(addr)) {254return hleReportWarning(Log::ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "invalid buffer address");255}256257// TODO: Validate stuff.258if (Memory::ReadUnchecked_U32(addr) != RIFF_CHUNK_MAGIC) {259return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid RIFF header");260}261262int retval = AnalyzeAtracTrack(addr, size, &track_);263first_._filesize_dontuse = track_.fileSize;264track_.DebugLog();265return retval;266}267268int AnalyzeAtracTrack(u32 addr, u32 size, Track *track) {269struct RIFFFmtChunk {270u16_le fmtTag;271u16_le channels;272u32_le samplerate;273u32_le avgBytesPerSec;274u16_le blockAlign;275};276277u32 offset = 8;278track->firstSampleOffset = 0;279280while (Memory::Read_U32(addr + offset) != RIFF_WAVE_MAGIC) {281// Get the size preceding the magic.282int chunk = Memory::Read_U32(addr + offset - 4);283// Round the chunk size up to the nearest 2.284offset += chunk + (chunk & 1);285if (offset + 12 > size) {286return hleReportError(Log::ME, ATRAC_ERROR_SIZE_TOO_SMALL, "too small for WAVE chunk at %d", offset);287}288if (Memory::Read_U32(addr + offset) != RIFF_CHUNK_MAGIC) {289return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "RIFF chunk did not contain WAVE");290}291offset += 8;292}293offset += 4;294295if (offset != 12) {296WARN_LOG_REPORT(Log::ME, "RIFF chunk at offset: %d", offset);297}298299// RIFF size excluding chunk header.300track->fileSize = Memory::Read_U32(addr + offset - 8) + 8;301302// Even if the RIFF size is too low, it may simply be incorrect. This works on real firmware.303u32 maxSize = std::max(track->fileSize, size);304305bool bfoundData = false;306u32 dataChunkSize = 0;307int sampleOffsetAdjust = 0;308309while (maxSize >= offset + 8 && !bfoundData) {310int chunkMagic = Memory::Read_U32(addr + offset);311u32 chunkSize = Memory::Read_U32(addr + offset + 4);312// Account for odd sized chunks.313if (chunkSize & 1) {314WARN_LOG_REPORT_ONCE(oddchunk, Log::ME, "RIFF chunk had uneven size");315}316chunkSize += (chunkSize & 1);317offset += 8;318if (chunkSize > maxSize - offset)319break;320switch (chunkMagic) {321case FMT_CHUNK_MAGIC:322{323if (track->codecType != 0) {324return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "multiple fmt definitions");325}326327auto at3fmt = PSPPointer<const RIFFFmtChunk>::Create(addr + offset);328if (chunkSize < 32 || (at3fmt->fmtTag == AT3_PLUS_MAGIC && chunkSize < 52)) {329return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "fmt definition too small (%d)", chunkSize);330}331332if (at3fmt->fmtTag == AT3_MAGIC)333track->codecType = PSP_MODE_AT_3;334else if (at3fmt->fmtTag == AT3_PLUS_MAGIC)335track->codecType = PSP_MODE_AT_3_PLUS;336else {337return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid fmt magic: %04x", at3fmt->fmtTag);338}339track->channels = at3fmt->channels;340if (track->channels != 1 && track->channels != 2) {341return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid channel count: %d", track->channels);342}343if (at3fmt->samplerate != 44100) {344return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "unsupported sample rate: %d", at3fmt->samplerate);345}346track->bitrate = at3fmt->avgBytesPerSec * 8;347track->bytesPerFrame = at3fmt->blockAlign;348if (track->bytesPerFrame == 0) {349return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid bytes per frame: %d", track->bytesPerFrame);350}351352// TODO: There are some format specific bytes here which seem to have fixed values?353// Probably don't need them.354355if (at3fmt->fmtTag == AT3_MAGIC) {356// This is the offset to the jointStereo_ field.357track->jointStereo = Memory::Read_U32(addr + offset + 24);358}359}360break;361case FACT_CHUNK_MAGIC:362{363track->endSample = Memory::Read_U32(addr + offset);364if (chunkSize >= 8) {365track->firstSampleOffset = Memory::Read_U32(addr + offset + 4);366}367if (chunkSize >= 12) {368u32 largerOffset = Memory::Read_U32(addr + offset + 8);369sampleOffsetAdjust = track->firstSampleOffset - largerOffset;370}371}372break;373case SMPL_CHUNK_MAGIC:374{375if (chunkSize < 32) {376return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "smpl chunk too small (%d)", chunkSize);377}378int checkNumLoops = Memory::Read_U32(addr + offset + 28);379if (checkNumLoops != 0 && chunkSize < 36 + 20) {380return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "smpl chunk too small for loop (%d, %d)", checkNumLoops, chunkSize);381}382if (checkNumLoops < 0) {383return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "bad checkNumLoops (%d)", checkNumLoops);384}385386track->loopinfo.resize(checkNumLoops);387u32 loopinfoAddr = addr + offset + 36;388// The PSP only cares about the first loop start and end, it seems.389// Most likely can skip the rest of this data, but it's not hurting anyone.390for (int i = 0; i < checkNumLoops && 36 + (u32)i < chunkSize; i++, loopinfoAddr += 24) {391track->loopinfo[i].cuePointID = Memory::Read_U32(loopinfoAddr);392track->loopinfo[i].type = Memory::Read_U32(loopinfoAddr + 4);393track->loopinfo[i].startSample = Memory::Read_U32(loopinfoAddr + 8);394track->loopinfo[i].endSample = Memory::Read_U32(loopinfoAddr + 12);395track->loopinfo[i].fraction = Memory::Read_U32(loopinfoAddr + 16);396track->loopinfo[i].playCount = Memory::Read_U32(loopinfoAddr + 20);397398if (track->loopinfo[i].startSample >= track->loopinfo[i].endSample) {399return hleReportError(Log::ME, ATRAC_ERROR_BAD_CODEC_PARAMS, "loop starts after it ends");400}401}402}403break;404case DATA_CHUNK_MAGIC:405{406bfoundData = true;407track->dataByteOffset = offset;408dataChunkSize = chunkSize;409if (track->fileSize < offset + chunkSize) {410WARN_LOG_REPORT(Log::ME, "Atrac data chunk extends beyond riff chunk");411track->fileSize = offset + chunkSize;412}413}414break;415}416offset += chunkSize;417}418419if (track->codecType == 0) {420return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "could not detect codec");421}422423if (!bfoundData) {424return hleReportError(Log::ME, ATRAC_ERROR_SIZE_TOO_SMALL, "no data chunk");425}426427// set the loopStartSample_ and loopEndSample_ by loopinfo_428if (track->loopinfo.size() > 0) {429track->loopStartSample = track->loopinfo[0].startSample + track->FirstOffsetExtra() + sampleOffsetAdjust;430track->loopEndSample = track->loopinfo[0].endSample + track->FirstOffsetExtra() + sampleOffsetAdjust;431} else {432track->loopStartSample = -1;433track->loopEndSample = -1;434}435436// if there is no correct endsample, try to guess it437if (track->endSample <= 0 && track->bytesPerFrame != 0) {438track->endSample = (dataChunkSize / track->bytesPerFrame) * track->SamplesPerFrame();439track->endSample -= track->FirstSampleOffsetFull();440}441track->endSample -= 1;442443if (track->loopEndSample != -1 && track->loopEndSample > track->endSample + track->FirstSampleOffsetFull()) {444return hleReportError(Log::ME, ATRAC_ERROR_BAD_CODEC_PARAMS, "loop after end of data");445}446447return 0;448}449450int Atrac::AnalyzeAA3(u32 addr, u32 size, u32 fileSize) {451first_.addr = addr;452first_.size = size;453first_._filesize_dontuse = fileSize;454455AnalyzeReset();456457return AnalyzeAA3Track(addr, size, fileSize, &track_);458}459460int AnalyzeAA3Track(u32 addr, u32 size, u32 fileSize, Track *track) {461if (size < 10) {462return hleReportError(Log::ME, ATRAC_ERROR_AA3_SIZE_TOO_SMALL, "buffer too small");463}464// TODO: Make sure this validation is correct, more testing.465466const u8 *buffer = Memory::GetPointer(addr);467if (buffer[0] != 'e' || buffer[1] != 'a' || buffer[2] != '3') {468return hleReportError(Log::ME, ATRAC_ERROR_AA3_INVALID_DATA, "invalid ea3 magic bytes");469}470471// It starts with an id3 header (replaced with ea3.) This is the size.472u32 tagSize = buffer[9] | (buffer[8] << 7) | (buffer[7] << 14) | (buffer[6] << 21);473if (size < tagSize + 36) {474return hleReportError(Log::ME, ATRAC_ERROR_AA3_SIZE_TOO_SMALL, "truncated before id3 end");475}476477// EA3 header starts at id3 header (10) + tagSize.478buffer = Memory::GetPointer(addr + 10 + tagSize);479if (buffer[0] != 'E' || buffer[1] != 'A' || buffer[2] != '3') {480return hleReportError(Log::ME, ATRAC_ERROR_AA3_INVALID_DATA, "invalid EA3 magic bytes");481}482483track->fileSize = fileSize;484485// Based on FFmpeg's code.486u32 codecParams = buffer[35] | (buffer[34] << 8) | (buffer[35] << 16);487const u32 at3SampleRates[8] = { 32000, 44100, 48000, 88200, 96000, 0 };488489switch (buffer[32]) {490case 0:491track->codecType = PSP_MODE_AT_3;492track->bytesPerFrame = (codecParams & 0x03FF) * 8;493track->bitrate = at3SampleRates[(codecParams >> 13) & 7] * track->bytesPerFrame * 8 / 1024;494track->channels = 2;495track->jointStereo = (codecParams >> 17) & 1;496break;497case 1:498track->codecType = PSP_MODE_AT_3_PLUS;499track->bytesPerFrame = ((codecParams & 0x03FF) * 8) + 8;500track->bitrate = at3SampleRates[(codecParams >> 13) & 7] * track->bytesPerFrame * 8 / 2048;501track->channels = (codecParams >> 10) & 7;502break;503case 3:504case 4:505case 5:506return hleReportError(Log::ME, ATRAC_ERROR_AA3_INVALID_DATA, "unsupported codec type %d", buffer[32]);507default:508return hleReportError(Log::ME, ATRAC_ERROR_AA3_INVALID_DATA, "invalid codec type %d", buffer[32]);509}510511track->dataByteOffset = 10 + tagSize + 96;512track->firstSampleOffset = 0;513if (track->endSample < 0 && track->bytesPerFrame != 0) {514track->endSample = ((track->fileSize - track->dataByteOffset) / track->bytesPerFrame) * track->SamplesPerFrame();515}516track->endSample -= 1;517return 0;518}519520void Atrac::CalculateStreamInfo(u32 *outReadOffset) {521u32 readOffset = first_.fileoffset;522if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {523// Nothing to write.524readOffset = 0;525first_.offset = 0;526first_.writableBytes = 0;527} else if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {528// If we're buffering the entire file, just give the same as readOffset.529first_.offset = readOffset;530// In this case, the bytes writable are just the remaining bytes, always.531first_.writableBytes = track_.fileSize - readOffset;532} else {533u32 bufferEnd = StreamBufferEnd();534u32 bufferValidExtended = bufferPos_ + bufferValidBytes_;535if (bufferValidExtended < bufferEnd) {536first_.offset = bufferValidExtended;537first_.writableBytes = bufferEnd - bufferValidExtended;538} else {539u32 bufferStartUsed = bufferValidExtended - bufferEnd;540first_.offset = bufferStartUsed;541first_.writableBytes = bufferPos_ - bufferStartUsed;542}543544if (readOffset >= track_.fileSize) {545if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {546// We don't need anything more, so all 0s.547readOffset = 0;548first_.offset = 0;549first_.writableBytes = 0;550} else {551readOffset = track_.FileOffsetBySample(track_.loopStartSample - track_.FirstSampleOffsetFull() - track_.SamplesPerFrame() * 2);552}553}554555if (readOffset + first_.writableBytes > track_.fileSize) {556// Never ask for past the end of file, even when the space is free.557first_.writableBytes = track_.fileSize - readOffset;558}559560// If you don't think this should be here, remove it. It's just a temporary safety check.561if (first_.offset + first_.writableBytes > bufferMaxSize_) {562ERROR_LOG_REPORT(Log::ME, "Somehow calculated too many writable bytes: %d + %d > %d", first_.offset, first_.writableBytes, bufferMaxSize_);563first_.offset = 0;564first_.writableBytes = bufferMaxSize_;565}566}567568if (outReadOffset) {569*outReadOffset = readOffset;570}571}572573void AtracBase::CreateDecoder() {574if (decoder_) {575delete decoder_;576}577578// First, init the standalone decoder. Only used for low-level-decode initially, but simple.579if (track_.codecType == PSP_MODE_AT_3) {580// We don't pull this from the RIFF so that we can support OMA also.581uint8_t extraData[14]{};582// The only thing that changes are the jointStereo_ values.583extraData[0] = 1;584extraData[3] = track_.channels << 3;585extraData[6] = track_.jointStereo;586extraData[8] = track_.jointStereo;587extraData[10] = 1;588decoder_ = CreateAtrac3Audio(track_.channels, track_.bytesPerFrame, extraData, sizeof(extraData));589} else {590decoder_ = CreateAtrac3PlusAudio(track_.channels, track_.bytesPerFrame);591}592}593594void Atrac::GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample) {595if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {596bufferInfo->first.writePosPtr = first_.addr;597// Everything is loaded, so nothing needs to be read.598bufferInfo->first.writableBytes = 0;599bufferInfo->first.minWriteBytes = 0;600bufferInfo->first.filePos = 0;601} else if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {602// Here the message is: you need to read at least this many bytes to get to that position.603// This is because we're filling the buffer start to finish, not streaming.604bufferInfo->first.writePosPtr = first_.addr + first_.size;605bufferInfo->first.writableBytes = track_.fileSize - first_.size;606int minWriteBytes = track_.FileOffsetBySample(sample) - first_.size;607if (minWriteBytes > 0) {608bufferInfo->first.minWriteBytes = minWriteBytes;609} else {610bufferInfo->first.minWriteBytes = 0;611}612bufferInfo->first.filePos = first_.size;613} else {614// This is without the sample offset. The file offset also includes the previous batch of samples?615int sampleFileOffset = track_.FileOffsetBySample(sample - track_.firstSampleOffset - track_.SamplesPerFrame());616617// Update the writable bytes. When streaming, this is just the number of bytes until the end.618const u32 bufSizeAligned = (bufferMaxSize_ / track_.bytesPerFrame) * track_.bytesPerFrame;619const int needsMoreFrames = track_.FirstOffsetExtra(); // ?620621bufferInfo->first.writePosPtr = first_.addr;622bufferInfo->first.writableBytes = std::min(track_.fileSize - sampleFileOffset, bufSizeAligned);623if (((sample + track_.firstSampleOffset) % (int)track_.SamplesPerFrame()) >= (int)track_.SamplesPerFrame() - needsMoreFrames) {624// Not clear why, but it seems it wants a bit extra in case the sample is late?625bufferInfo->first.minWriteBytes = track_.bytesPerFrame * 3;626} else {627bufferInfo->first.minWriteBytes = track_.bytesPerFrame * 2;628}629if ((u32)sample < (u32)track_.firstSampleOffset && sampleFileOffset != track_.dataByteOffset) {630sampleFileOffset -= track_.bytesPerFrame;631}632bufferInfo->first.filePos = sampleFileOffset;633634if (second_.size != 0) {635// TODO: We have a second buffer. Within it, minWriteBytes should be zero.636// The filePos should be after the end of the second buffer (or zero.)637// We actually need to ensure we READ from the second buffer before implementing that.638}639}640641// It seems like this is always the same as the first buffer's pos, weirdly.642bufferInfo->second.writePosPtr = first_.addr;643// Reset never needs a second buffer write, since the loop is in a fixed place.644bufferInfo->second.writableBytes = 0;645bufferInfo->second.minWriteBytes = 0;646bufferInfo->second.filePos = 0;647}648649int Atrac::SetData(u32 buffer, u32 readSize, u32 bufferSize, int outputChannels, int successCode) {650outputChannels_ = outputChannels;651652first_.addr = buffer;653first_.size = readSize;654655if (first_.size > track_.fileSize)656first_.size = track_.fileSize;657first_.fileoffset = first_.size;658659// got the size of temp buf, and calculate offset660bufferMaxSize_ = bufferSize;661first_.offset = first_.size;662663// some games may reuse an atracID for playing sound664ResetData();665UpdateBufferState();666667if (track_.codecType != PSP_MODE_AT_3 && track_.codecType != PSP_MODE_AT_3_PLUS) {668// Shouldn't have gotten here, Analyze() checks this.669bufferState_ = ATRAC_STATUS_NO_DATA;670return hleReportError(Log::ME, ATRAC_ERROR_UNKNOWN_FORMAT, "unexpected codec type in set data");671}672673if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED || bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {674// This says, don't use the dataBuf_ array, use the PSP RAM.675// This way, games can load data async into the buffer, and it still works.676// TODO: Support this always, even for streaming.677ignoreDataBuf_ = true;678}679if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP || bufferState_ == ATRAC_STATUS_STREAMED_LOOP_FROM_END || bufferState_ == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {680bufferHeaderSize_ = track_.dataByteOffset;681bufferPos_ = track_.dataByteOffset + track_.bytesPerFrame;682bufferValidBytes_ = first_.size - bufferPos_;683}684685const char *codecName = track_.codecType == PSP_MODE_AT_3 ? "atrac3" : "atrac3+";686const char *channelName = track_.channels == 1 ? "mono" : "stereo";687688// Over-allocate databuf to prevent going off the end if the bitstream is bad or if there are689// bugs in the decoder. This happens, see issue #15788. Arbitrary, but let's make it a whole page on the popular690// architecture that has the largest pages (M1).691dataBuf_ = new u8[track_.fileSize + overAllocBytes];692memset(dataBuf_, 0, track_.fileSize + overAllocBytes);693if (!ignoreDataBuf_) {694u32 copybytes = std::min(bufferSize, track_.fileSize);695Memory::Memcpy(dataBuf_, buffer, copybytes, "AtracSetData");696}697CreateDecoder();698return hleLogSuccessInfoI(Log::ME, successCode, "%s %s audio", codecName, channelName);699}700701u32 Atrac::SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) {702u32 secondFileOffset = track_.FileOffsetBySample(track_.loopEndSample - track_.firstSampleOffset);703u32 desiredSize = track_.fileSize - secondFileOffset;704705// 3 seems to be the number of frames required to handle a loop.706if (secondBufferSize < desiredSize && secondBufferSize < (u32)track_.BytesPerFrame() * 3) {707return hleReportError(Log::ME, ATRAC_ERROR_SIZE_TOO_SMALL, "too small");708}709if (BufferState() != ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {710return hleReportError(Log::ME, ATRAC_ERROR_SECOND_BUFFER_NOT_NEEDED, "not needed");711}712713second_.addr = secondBuffer;714second_.size = secondBufferSize;715second_.fileoffset = secondFileOffset;716return hleLogSuccessI(Log::ME, 0);717}718719int AtracBase::GetSecondBufferInfo(u32 *fileOffset, u32 *desiredSize) {720if (BufferState() != ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {721// Writes zeroes in this error case.722*fileOffset = 0;723*desiredSize = 0;724return hleLogWarning(Log::ME, ATRAC_ERROR_SECOND_BUFFER_NOT_NEEDED, "not needed");725}726727*fileOffset = track_.FileOffsetBySample(track_.loopEndSample - track_.firstSampleOffset);728*desiredSize = track_.fileSize - *fileOffset;729return hleLogSuccessI(Log::ME, 0);730}731732void Atrac::GetStreamDataInfo(u32 *writePtr, u32 *writableBytes, u32 *readOffset) {733u32 calculatedReadOffset;734// TODO: Feels like this should already have been computed?735CalculateStreamInfo(&calculatedReadOffset);736737*writePtr = first_.addr + first_.offset;738*writableBytes = first_.writableBytes;739*readOffset = calculatedReadOffset;740}741742void Atrac::UpdateBufferState() {743if (bufferMaxSize_ >= track_.fileSize) {744if (first_.size < track_.fileSize) {745// The buffer is big enough, but we don't have all the data yet.746bufferState_ = ATRAC_STATUS_HALFWAY_BUFFER;747} else {748bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;749}750} else {751if (track_.loopEndSample <= 0) {752// There's no looping, but we need to stream the data in our buffer.753bufferState_ = ATRAC_STATUS_STREAMED_WITHOUT_LOOP;754} else if (track_.loopEndSample == track_.endSample + track_.FirstSampleOffsetFull()) {755bufferState_ = ATRAC_STATUS_STREAMED_LOOP_FROM_END;756} else {757bufferState_ = ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER;758}759}760}761762int Atrac::AddStreamData(u32 bytesToAdd) {763u32 readOffset;764CalculateStreamInfo(&readOffset);765if (bytesToAdd > first_.writableBytes)766return hleLogWarning(Log::ME, ATRAC_ERROR_ADD_DATA_IS_TOO_BIG, "too many bytes");767768if (bytesToAdd > 0) {769first_.fileoffset = readOffset;770int addbytes = std::min(bytesToAdd, track_.fileSize - first_.fileoffset);771if (!ignoreDataBuf_) {772Memory::Memcpy(dataBuf_ + first_.fileoffset, first_.addr + first_.offset, addbytes, "AtracAddStreamData");773}774first_.fileoffset += addbytes;775}776first_.size += bytesToAdd;777if (first_.size >= track_.fileSize) {778first_.size = track_.fileSize;779if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER)780bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;781WriteContextToPSPMem();782}783784first_.offset += bytesToAdd;785bufferValidBytes_ += bytesToAdd;786787if (PSP_CoreParameter().compat.flags().AtracLoopHack && bufferState_ == ATRAC_STATUS_STREAMED_LOOP_FROM_END && RemainingFrames() > 2) {788loopNum_++;789SeekToSample(track_.loopStartSample - track_.FirstSampleOffsetFull());790}791792return 0;793}794795u32 Atrac::AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) {796int addbytes = std::min(bytesToAdd, track_.fileSize - first_.fileoffset - track_.FirstOffsetExtra());797Memory::Memcpy(dataBuf_ + first_.fileoffset + track_.FirstOffsetExtra(), bufPtr, addbytes, "AtracAddStreamData");798first_.size += bytesToAdd;799if (first_.size >= track_.fileSize) {800first_.size = track_.fileSize;801if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER)802bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;803}804first_.fileoffset += addbytes;805// refresh context_806WriteContextToPSPMem();807return 0;808}809810u32 Atrac::GetNextSamples() {811// It seems like the PSP aligns the sample position to 0x800...?812u32 skipSamples = track_.FirstSampleOffsetFull();813u32 firstSamples = (track_.SamplesPerFrame() - skipSamples) % track_.SamplesPerFrame();814u32 numSamples = track_.endSample + 1 - currentSample_;815if (currentSample_ == 0 && firstSamples != 0) {816numSamples = firstSamples;817}818u32 unalignedSamples = (skipSamples + currentSample_) % track_.SamplesPerFrame();819if (unalignedSamples != 0) {820// We're off alignment, possibly due to a loop. Force it back on.821numSamples = track_.SamplesPerFrame() - unalignedSamples;822}823if (numSamples > track_.SamplesPerFrame())824numSamples = track_.SamplesPerFrame();825if (bufferState_ == ATRAC_STATUS_STREAMED_LOOP_FROM_END && (int)numSamples + currentSample_ > track_.endSample) {826bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;827}828return numSamples;829}830831void Atrac::ForceSeekToSample(int sample) {832if (decoder_) {833decoder_->FlushBuffers();834}835currentSample_ = sample;836}837838void Atrac::SeekToSample(int sample) {839// It seems like the PSP aligns the sample position to 0x800...?840const u32 offsetSamples = track_.FirstSampleOffsetFull();841const u32 unalignedSamples = (offsetSamples + sample) % track_.SamplesPerFrame();842int seekFrame = sample + offsetSamples - unalignedSamples;843844if ((sample != currentSample_ || sample == 0) && decoder_ != nullptr) {845// Prefill the decode buffer with packets before the first sample offset.846decoder_->FlushBuffers();847848int adjust = 0;849if (sample == 0) {850int offsetSamples = track_.FirstSampleOffsetFull();851adjust = -(int)(offsetSamples % track_.SamplesPerFrame());852}853const u32 off = track_.FileOffsetBySample(sample + adjust);854const u32 backfill = track_.bytesPerFrame * 2;855const u32 start = off - track_.dataByteOffset < backfill ? track_.dataByteOffset : off - backfill;856857for (u32 pos = start; pos < off; pos += track_.bytesPerFrame) {858decoder_->Decode(BufferStart() + pos, track_.bytesPerFrame, nullptr, 2, nullptr, nullptr);859}860}861862currentSample_ = sample;863}864865int Atrac::RemainingFrames() const {866if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {867// Meaning, infinite I guess? We've got it all.868return PSP_ATRAC_ALLDATA_IS_ON_MEMORY;869}870871u32 currentFileOffset = track_.FileOffsetBySample(currentSample_ - track_.SamplesPerFrame() + track_.FirstOffsetExtra());872if (first_.fileoffset >= track_.fileSize) {873if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {874return PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY;875}876int loopEndAdjusted = track_.loopEndSample - track_.FirstOffsetExtra() - track_.firstSampleOffset;877if (bufferState_ == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER && currentSample_ > loopEndAdjusted) {878// No longer looping in this case, outside the loop.879return PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY;880}881if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK && loopNum_ == 0) {882return PSP_ATRAC_LOOP_STREAM_DATA_IS_ON_MEMORY;883}884}885886if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {887// Since we're streaming, the remaining frames are what's valid in the buffer.888return bufferValidBytes_ / track_.bytesPerFrame;889}890891// Since the first frame is shorter by this offset, add to round up at this offset.892const int remainingBytes = first_.fileoffset - currentFileOffset;893if (remainingBytes < 0) {894// Just in case. Shouldn't happen, but once did by mistake.895return 0;896}897return remainingBytes / track_.bytesPerFrame;898}899900void Atrac::ConsumeFrame() {901bufferPos_ += track_.bytesPerFrame;902if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {903if (bufferValidBytes_ > track_.bytesPerFrame) {904bufferValidBytes_ -= track_.bytesPerFrame;905} else {906bufferValidBytes_ = 0;907}908}909if (bufferPos_ >= StreamBufferEnd()) {910// Wrap around... theoretically, this should only happen at exactly StreamBufferEnd.911bufferPos_ -= StreamBufferEnd();912bufferHeaderSize_ = 0;913}914}915916u32 Atrac::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, int *remains) {917int loopNum = loopNum_;918if (bufferState_ == ATRAC_STATUS_FOR_SCESAS) {919// TODO: Might need more testing.920loopNum = 0;921}922923// We already passed the end - return an error (many games check for this.)924if (currentSample_ >= track_.endSample && loopNum == 0) {925*SamplesNum = 0;926*finish = 1;927// refresh context_928WriteContextToPSPMem();929return ATRAC_ERROR_ALL_DATA_DECODED;930}931932// TODO: This isn't at all right, but at least it makes the music "last" some time.933u32 numSamples = 0;934935// It seems like the PSP aligns the sample position to 0x800...?936int offsetSamples = track_.FirstSampleOffsetFull();937int skipSamples = 0;938u32 maxSamples = track_.endSample + 1 - currentSample_;939u32 unalignedSamples = (offsetSamples + currentSample_) % track_.SamplesPerFrame();940if (unalignedSamples != 0) {941// We're off alignment, possibly due to a loop. Force it back on.942maxSamples = track_.SamplesPerFrame() - unalignedSamples;943skipSamples = unalignedSamples;944}945946if (skipSamples != 0 && bufferHeaderSize_ == 0) {947// Skip the initial frame used to load state for the looped frame.948// TODO: We will want to actually read this in.949// TODO again: This seems to happen on the first frame of playback regardless of loops.950// Can't be good.951ConsumeFrame();952}953954SeekToSample(currentSample_);955956bool gotFrame = false;957u32 off = track_.FileOffsetBySample(currentSample_ - skipSamples);958if (off < first_.size) {959uint8_t *indata = BufferStart() + off;960int bytesConsumed = 0;961int outSamples = track_.SamplesPerFrame();962int outBytes = outSamples * outputChannels_ * sizeof(int16_t);963gotFrame = true;964965numSamples = outSamples;966uint32_t packetAddr = CurBufferAddress(-skipSamples);967// got a frame968int skipped = std::min((u32)skipSamples, numSamples);969skipSamples -= skipped;970numSamples = numSamples - skipped;971// If we're at the end, clamp to samples we want. It always returns a full chunk.972numSamples = std::min(maxSamples, numSamples);973974outSamples = numSamples;975if (!decoder_->Decode(indata, track_.bytesPerFrame, &bytesConsumed, outputChannels_, (int16_t *)outbuf, &outSamples)) {976// Decode failed.977*SamplesNum = 0;978*finish = 1;979return ATRAC_ERROR_ALL_DATA_DECODED;980}981982if (packetAddr != 0 && MemBlockInfoDetailed()) {983char tagData[128];984size_t tagSize = FormatMemWriteTagAt(tagData, sizeof(tagData), "AtracDecode/", packetAddr, track_.bytesPerFrame);985NotifyMemInfo(MemBlockFlags::READ, packetAddr, track_.bytesPerFrame, tagData, tagSize);986NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, tagData, tagSize);987} else {988NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, "AtracDecode");989}990// We only want one frame per call, let's continue the next time.991}992993if (!gotFrame && currentSample_ < track_.endSample) {994// Never got a frame. We may have dropped a GHA frame or otherwise have a bug.995// For now, let's try to provide an extra "frame" if possible so games don't infinite loop.996if (track_.FileOffsetBySample(currentSample_) < track_.fileSize) {997numSamples = std::min(maxSamples, track_.SamplesPerFrame());998u32 outBytes = numSamples * outputChannels_ * sizeof(s16);999if (outbuf != nullptr) {1000memset(outbuf, 0, outBytes);1001NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, "AtracDecode");1002}1003}1004}10051006*SamplesNum = numSamples;1007// update current sample and decodePos1008currentSample_ += numSamples;1009decodePos_ = track_.DecodePosBySample(currentSample_);10101011ConsumeFrame();10121013int finishFlag = 0;1014// TODO: Verify.1015bool hitEnd = currentSample_ >= track_.endSample || (numSamples == 0 && first_.size >= track_.fileSize);1016int loopEndAdjusted = track_.loopEndSample - track_.FirstSampleOffsetFull();1017if ((hitEnd || currentSample_ > loopEndAdjusted) && loopNum != 0) {1018SeekToSample(track_.loopStartSample - track_.FirstSampleOffsetFull());1019if (bufferState_ != ATRAC_STATUS_FOR_SCESAS) {1020if (loopNum_ > 0)1021loopNum_--;1022}1023if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {1024// Whatever bytes we have left were added from the loop.1025u32 loopOffset = track_.FileOffsetBySample(track_.loopStartSample - track_.FirstSampleOffsetFull() - track_.SamplesPerFrame() * 2);1026// TODO: Hmm, need to manage the buffer better. But don't move fileoffset if we already have valid data.1027if (loopOffset > first_.fileoffset || loopOffset + bufferValidBytes_ < first_.fileoffset) {1028// Skip the initial frame at the start.1029first_.fileoffset = track_.FileOffsetBySample(track_.loopStartSample - track_.FirstSampleOffsetFull() - track_.SamplesPerFrame() * 2);1030}1031}1032} else if (hitEnd) {1033finishFlag = 1;10341035// Still move forward, so we know that we've read everything.1036// This seems to be reflected in the context as well.1037currentSample_ += track_.SamplesPerFrame() - numSamples;1038}10391040*finish = finishFlag;1041*remains = RemainingFrames();1042// refresh context_1043WriteContextToPSPMem();1044return 0;1045}10461047void AtracBase::SetLoopNum(int loopNum) {1048// Spammed in MHU1049loopNum_ = loopNum;1050if (loopNum != 0 && track_.loopinfo.size() == 0) {1051// Just loop the whole audio1052// This is a rare modification of track_ after the fact.1053// Maybe we can get away with setting these by default.1054track_.loopStartSample = track_.FirstSampleOffsetFull();1055track_.loopEndSample = track_.endSample + track_.FirstSampleOffsetFull();1056}1057WriteContextToPSPMem();1058}10591060u32 Atrac::ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf) {1061// Reuse the same calculation as before.1062AtracResetBufferInfo bufferInfo;1063GetResetBufferInfo(&bufferInfo, sample);10641065if ((u32)bytesWrittenFirstBuf < bufferInfo.first.minWriteBytes || (u32)bytesWrittenFirstBuf > bufferInfo.first.writableBytes) {1066return hleLogError(Log::ME, ATRAC_ERROR_BAD_FIRST_RESET_SIZE, "first byte count not in valid range");1067}1068if ((u32)bytesWrittenSecondBuf < bufferInfo.second.minWriteBytes || (u32)bytesWrittenSecondBuf > bufferInfo.second.writableBytes) {1069return hleLogError(Log::ME, ATRAC_ERROR_BAD_SECOND_RESET_SIZE, "second byte count not in valid range");1070}10711072if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {1073// Always adds zero bytes.1074} else if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {1075// Okay, it's a valid number of bytes. Let's set them up.1076if (bytesWrittenFirstBuf != 0) {1077if (!ignoreDataBuf_) {1078Memory::Memcpy(dataBuf_ + first_.size, first_.addr + first_.size, bytesWrittenFirstBuf, "AtracResetPlayPosition");1079}1080first_.fileoffset += bytesWrittenFirstBuf;1081first_.size += bytesWrittenFirstBuf;1082first_.offset += bytesWrittenFirstBuf;1083}10841085// Did we transition to a full buffer?1086if (first_.size >= track_.fileSize) {1087first_.size = track_.fileSize;1088bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;1089}1090} else {1091if (bufferInfo.first.filePos > track_.fileSize) {1092return hleDelayResult(hleLogError(Log::ME, ATRAC_ERROR_API_FAIL, "invalid file position"), "reset play pos", 200);1093}10941095// Move the offset to the specified position.1096first_.fileoffset = bufferInfo.first.filePos;10971098if (bytesWrittenFirstBuf != 0) {1099if (!ignoreDataBuf_) {1100Memory::Memcpy(dataBuf_ + first_.fileoffset, first_.addr, bytesWrittenFirstBuf, "AtracResetPlayPosition");1101}1102first_.fileoffset += bytesWrittenFirstBuf;1103}1104first_.size = first_.fileoffset;1105first_.offset = bytesWrittenFirstBuf;11061107bufferHeaderSize_ = 0;1108bufferPos_ = track_.bytesPerFrame;1109bufferValidBytes_ = bytesWrittenFirstBuf - bufferPos_;1110}11111112if (track_.codecType == PSP_MODE_AT_3 || track_.codecType == PSP_MODE_AT_3_PLUS) {1113SeekToSample(sample);1114}11151116WriteContextToPSPMem();1117return 0;1118}11191120void Atrac::InitLowLevel(u32 paramsAddr, bool jointStereo) {1121track_.channels = Memory::Read_U32(paramsAddr);1122outputChannels_ = Memory::Read_U32(paramsAddr + 4);1123bufferMaxSize_ = Memory::Read_U32(paramsAddr + 8);1124track_.bytesPerFrame = bufferMaxSize_;1125first_.writableBytes = track_.bytesPerFrame;1126ResetData();11271128if (track_.codecType == PSP_MODE_AT_3) {1129track_.bitrate = (track_.bytesPerFrame * 352800) / 1000;1130track_.bitrate = (track_.bitrate + 511) >> 10;1131track_.jointStereo = false;1132} else if (track_.codecType == PSP_MODE_AT_3_PLUS) {1133track_.bitrate = (track_.bytesPerFrame * 352800) / 1000;1134track_.bitrate = ((track_.bitrate >> 11) + 8) & 0xFFFFFFF0;1135track_.jointStereo = false;1136}11371138track_.dataByteOffset = 0;1139first_.size = 0;1140track_.fileSize = track_.bytesPerFrame; // not really meaningful1141bufferState_ = ATRAC_STATUS_LOW_LEVEL;1142currentSample_ = 0;1143CreateDecoder();1144WriteContextToPSPMem();1145}114611471148