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/HW/SasAudio.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 <algorithm>1819#include "Common/Profiler/Profiler.h"2021#include "Common/Serialize/SerializeFuncs.h"22#include "Core/MemMapHelpers.h"23#include "Core/HLE/sceAtrac.h"24#include "Core/Config.h"25#include "Core/Reporting.h"26#include "Core/Util/AudioFormat.h"27#include "Core/Core.h"28#include "SasAudio.h"2930// #define AUDIO_TO_FILE3132static const u8 f[16][2] = {33{ 0, 0 },34{ 60, 0 },35{ 115, 52 },36{ 98, 55 },37{ 122, 60 },38// TODO: The below values could use more testing, but match initial tests.39// Not sure if they are used by games, found by tests.40{ 0, 0 },41{ 0, 0 },42{ 52, 0 },43{ 55, 2 },44{ 60, 125 },45{ 0, 0 },46{ 0, 91 },47{ 0, 0 },48{ 2, 216 },49{ 125, 6 },50{ 0, 151 },51};5253void VagDecoder::Start(u32 data, u32 vagSize, bool loopEnabled) {54loopEnabled_ = loopEnabled;55loopAtNextBlock_ = false;56loopStartBlock_ = -1;57numBlocks_ = vagSize / 16;58end_ = false;59data_ = data;60read_ = data;61curSample = 28;62curBlock_ = -1;63s_1 = 0; // per block?64s_2 = 0;65}6667void VagDecoder::DecodeBlock(const u8 *&read_pointer) {68if (curBlock_ == numBlocks_ - 1) {69end_ = true;70return;71}7273_dbg_assert_(curBlock_ < numBlocks_);7475const u8 *readp = read_pointer;76int predict_nr = *readp++;77int shift_factor = predict_nr & 0xf;78predict_nr >>= 4;79int flags = *readp++;80if (flags == 7) {81VERBOSE_LOG(Log::SasMix, "VAG ending block at %d", curBlock_);82end_ = true;83return;84}85else if (flags == 6) {86loopStartBlock_ = curBlock_;87}88else if (flags == 3) {89if (loopEnabled_) {90loopAtNextBlock_ = true;91}92}9394// Keep state in locals to avoid bouncing to memory.95int s1 = s_1;96int s2 = s_2;9798int coef1 = f[predict_nr][0];99int coef2 = -f[predict_nr][1];100101// TODO: Unroll once more and interleave the unpacking with the decoding more?102for (int i = 0; i < 28; i += 2) {103u8 d = *readp++;104int sample1 = (short)((d & 0xf) << 12) >> shift_factor;105int sample2 = (short)((d & 0xf0) << 8) >> shift_factor;106s2 = clamp_s16(sample1 + ((s1 * coef1 + s2 * coef2) >> 6));107s1 = clamp_s16(sample2 + ((s2 * coef1 + s1 * coef2) >> 6));108samples[i] = s2;109samples[i + 1] = s1;110}111112s_1 = s1;113s_2 = s2;114curSample = 0;115curBlock_++;116117read_pointer = readp;118}119120void VagDecoder::GetSamples(s16 *outSamples, int numSamples) {121if (end_) {122memset(outSamples, 0, numSamples * sizeof(s16));123return;124}125if (!Memory::IsValidRange(read_, numBlocks_ * 16)) {126WARN_LOG_REPORT(Log::SasMix, "Bad VAG samples address? %08x / %d", read_, numBlocks_);127return;128}129130const u8 *readp = Memory::GetPointerUnchecked(read_);131const u8 *origp = readp;132133for (int i = 0; i < numSamples; i++) {134if (curSample == 28) {135if (loopAtNextBlock_) {136VERBOSE_LOG(Log::SasMix, "Looping VAG from block %d/%d to %d", curBlock_, numBlocks_, loopStartBlock_);137// data_ starts at curBlock = -1.138read_ = data_ + 16 * loopStartBlock_ + 16;139readp = Memory::GetPointerUnchecked(read_);140origp = readp;141curBlock_ = loopStartBlock_;142loopAtNextBlock_ = false;143}144DecodeBlock(readp);145if (end_) {146// Clear the rest of the buffer and return.147memset(&outSamples[i], 0, (numSamples - i) * sizeof(s16));148return;149}150}151_dbg_assert_(curSample < 28);152outSamples[i] = samples[curSample++];153}154155if (readp > origp) {156if (MemBlockInfoDetailed())157NotifyMemInfo(MemBlockFlags::READ, read_, readp - origp, "SasVagDecoder");158read_ += readp - origp;159}160}161162void VagDecoder::DoState(PointerWrap &p) {163auto s = p.Section("VagDecoder", 1, 2);164if (!s)165return;166167if (s >= 2) {168DoArray(p, samples, ARRAY_SIZE(samples));169} else {170int samplesOld[ARRAY_SIZE(samples)];171DoArray(p, samplesOld, ARRAY_SIZE(samples));172for (size_t i = 0; i < ARRAY_SIZE(samples); ++i) {173samples[i] = samplesOld[i];174}175}176Do(p, curSample);177178Do(p, data_);179Do(p, read_);180Do(p, curBlock_);181Do(p, loopStartBlock_);182Do(p, numBlocks_);183184Do(p, s_1);185Do(p, s_2);186187Do(p, loopEnabled_);188Do(p, loopAtNextBlock_);189Do(p, end_);190}191192int SasAtrac3::setContext(u32 context) {193contextAddr_ = context;194atracID_ = AtracSasGetIDByContext(context);195if (!sampleQueue_)196sampleQueue_ = new BufferQueue();197sampleQueue_->clear();198end_ = false;199return 0;200}201202void SasAtrac3::getNextSamples(s16 *outbuf, int wantedSamples) {203if (atracID_ < 0) {204end_ = true;205return;206}207u32 finish = 0;208int wantedbytes = wantedSamples * sizeof(s16);209while (!finish && sampleQueue_->getQueueSize() < wantedbytes) {210u32 numSamples = 0;211int remains = 0;212static s16 buf[0x800];213AtracSasDecodeData(atracID_, (u8*)buf, 0, &numSamples, &finish, &remains);214if (numSamples > 0)215sampleQueue_->push((u8*)buf, numSamples * sizeof(s16));216else217finish = 1;218}219sampleQueue_->pop_front((u8*)outbuf, wantedbytes);220end_ = finish == 1;221}222223int SasAtrac3::addStreamData(u32 bufPtr, u32 addbytes) {224if (atracID_ > 0) {225AtracSasAddStreamData(atracID_, bufPtr, addbytes);226}227return 0;228}229230void SasAtrac3::DoState(PointerWrap &p) {231auto s = p.Section("SasAtrac3", 1, 2);232if (!s)233return;234235Do(p, contextAddr_);236Do(p, atracID_);237if (p.mode == p.MODE_READ && atracID_ >= 0 && !sampleQueue_) {238sampleQueue_ = new BufferQueue();239}240if (s >= 2) {241Do(p, end_);242}243}244245// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/sceSasCore.java246247static int simpleRate(int n) {248n &= 0x7F;249if (n == 0x7F) {250return 0;251}252int rate = ((7 - (n & 0x3)) << 26) >> (n >> 2);253if (rate == 0) {254return 1;255}256return rate;257}258259static int exponentRate(int n) {260n &= 0x7F;261if (n == 0x7F) {262return 0;263}264int rate = ((7 - (n & 0x3)) << 24) >> (n >> 2);265if (rate == 0) {266return 1;267}268return rate;269}270271static int getAttackRate(int bitfield1) {272return simpleRate(bitfield1 >> 8);273}274275static int getAttackType(int bitfield1) {276return (bitfield1 & 0x8000) == 0 ? PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE : PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT;277}278279static int getDecayRate(int bitfield1) {280int n = (bitfield1 >> 4) & 0x000F;281if (n == 0)282return 0x7FFFFFFF;283return 0x80000000 >> n;284}285286static int getSustainType(int bitfield2) {287return (bitfield2 >> 14) & 3;288}289290static int getSustainRate(int bitfield2) {291if (getSustainType(bitfield2) == PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE) {292return exponentRate(bitfield2 >> 6);293} else {294return simpleRate(bitfield2 >> 6);295}296}297298static int getReleaseType(int bitfield2) {299return (bitfield2 & 0x0020) == 0 ? PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE : PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE;300}301302static int getReleaseRate(int bitfield2) {303int n = bitfield2 & 0x001F;304if (n == 31) {305return 0;306}307if (getReleaseType(bitfield2) == PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE) {308if (n == 30) {309return 0x40000000;310} else if (n == 29) {311return 1;312}313return 0x10000000 >> n;314}315if (n == 0)316return 0x7FFFFFFF;317return 0x80000000 >> n;318}319320static int getSustainLevel(int bitfield1) {321return ((bitfield1 & 0x000F) + 1) << 26;322}323324void ADSREnvelope::SetEnvelope(int flag, int a, int d, int s, int r) {325if ((flag & 0x1) != 0)326attackType = (SasADSRCurveMode)a;327if ((flag & 0x2) != 0)328decayType = (SasADSRCurveMode)d;329if ((flag & 0x4) != 0)330sustainType = (SasADSRCurveMode)s;331if ((flag & 0x8) != 0)332releaseType = (SasADSRCurveMode)r;333334if (PSP_CoreParameter().compat.flags().RockmanDash2SoundFix && sustainType == PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE) {335sustainType = PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE;336}337}338339void ADSREnvelope::SetRate(int flag, int a, int d, int s, int r) {340if ((flag & 0x1) != 0)341attackRate = a;342if ((flag & 0x2) != 0)343decayRate = d;344if ((flag & 0x4) != 0)345sustainRate = s;346if ((flag & 0x8) != 0)347releaseRate = r;348}349350void ADSREnvelope::SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2) {351attackRate = getAttackRate(ADSREnv1);352attackType = (SasADSRCurveMode)getAttackType(ADSREnv1);353decayRate = getDecayRate(ADSREnv1);354decayType = PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE;355sustainRate = getSustainRate(ADSREnv2);356sustainType = (SasADSRCurveMode)getSustainType(ADSREnv2);357releaseRate = getReleaseRate(ADSREnv2);358releaseType = (SasADSRCurveMode)getReleaseType(ADSREnv2);359sustainLevel = getSustainLevel(ADSREnv1);360361if (PSP_CoreParameter().compat.flags().RockmanDash2SoundFix && sustainType == PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE) {362sustainType = PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE;363}364365if (attackRate < 0 || decayRate < 0 || sustainRate < 0 || releaseRate < 0) {366ERROR_LOG_REPORT(Log::SasMix, "Simple ADSR resulted in invalid rates: %04x, %04x", ADSREnv1, ADSREnv2);367}368}369370SasInstance::SasInstance() {371#ifdef AUDIO_TO_FILE372audioDump = fopen("D:\\audio.raw", "wb");373#endif374memset(&waveformEffect, 0, sizeof(waveformEffect));375waveformEffect.type = PSP_SAS_EFFECT_TYPE_OFF;376waveformEffect.isDryOn = 1;377memset(mixTemp_, 0, sizeof(mixTemp_)); // just to avoid a static analysis warning.378}379380SasInstance::~SasInstance() {381ClearGrainSize();382}383384void SasInstance::GetDebugText(char *text, size_t bufsize) {385char voiceBuf[4096];386voiceBuf[0] = '\0';387char *p = voiceBuf;388for (int i = 0; i < maxVoices; i++) {389if (voices[i].playing) {390uint32_t readAddr = voices[i].GetReadAddress();391const char *indicator = "";392switch (voices[i].type) {393case VOICETYPE_VAG:394if (readAddr < voices[i].vagAddr || readAddr > voices[i].vagAddr + voices[i].vagSize) {395indicator = " (BAD!)";396}397break;398default:399break;400}401p += snprintf(p, sizeof(voiceBuf) - (p - voiceBuf), " %d: Pitch %04x L/R,FX: %d,%d|%d,%d VAG: %08x:%d:%08x%s Height:%d%%\n", i,402voices[i].pitch, voices[i].volumeLeft, voices[i].volumeRight, voices[i].effectLeft, voices[i].effectRight,403voices[i].vagAddr, voices[i].vagSize, voices[i].GetReadAddress(), indicator, (int)((int64_t)voices[i].envelope.GetHeight() * 100 / PSP_SAS_ENVELOPE_HEIGHT_MAX));404p += snprintf(p, sizeof(voiceBuf) - (p - voiceBuf), " - ADSR: %s/%s/%s/%s\n",405ADSRCurveModeAsString(voices[i].envelope.attackType),406ADSRCurveModeAsString(voices[i].envelope.decayType),407ADSRCurveModeAsString(voices[i].envelope.sustainType),408ADSRCurveModeAsString(voices[i].envelope.releaseType)409);410}411}412413snprintf(text, bufsize,414"SR: %d Mode: %s Grain: %d\n"415"Effect: Type: %d Dry: %d Wet: %d L: %d R: %d Delay: %d Feedback: %d\n"416"\n%s\n",417sampleRate, outputMode == PSP_SAS_OUTPUTMODE_RAW ? "Raw" : "Mixed", grainSize,418waveformEffect.type, waveformEffect.isDryOn, waveformEffect.isWetOn, waveformEffect.leftVol, waveformEffect.rightVol, waveformEffect.delay, waveformEffect.feedback,419voiceBuf);420421}422423void SasInstance::ClearGrainSize() {424delete[] mixBuffer;425delete[] sendBuffer;426delete[] sendBufferDownsampled;427delete[] sendBufferProcessed;428mixBuffer = nullptr;429sendBuffer = nullptr;430sendBufferDownsampled = nullptr;431sendBufferProcessed = nullptr;432}433434void SasInstance::SetGrainSize(int newGrainSize) {435grainSize = newGrainSize;436437// If you change the sizes here, don't forget DoState().438delete[] mixBuffer;439delete[] sendBuffer;440delete[] sendBufferDownsampled;441delete[] sendBufferProcessed;442443mixBuffer = new s32[grainSize * 2];444sendBuffer = new s32[grainSize * 2];445sendBufferDownsampled = new s16[grainSize];446sendBufferProcessed = new s16[grainSize * 2];447memset(mixBuffer, 0, sizeof(int) * grainSize * 2);448memset(sendBuffer, 0, sizeof(int) * grainSize * 2);449memset(sendBufferDownsampled, 0, sizeof(s16) * grainSize);450memset(sendBufferProcessed, 0, sizeof(s16) * grainSize * 2);451}452453int SasInstance::EstimateMixUs() {454int voicesPlayingCount = 0;455456for (int v = 0; v < PSP_SAS_VOICES_MAX; v++) {457SasVoice &voice = voices[v];458if (!voice.playing || voice.paused)459continue;460voicesPlayingCount++;461}462463// Each voice costs extra time, and each byte of grain costs extra time.464int cycles = 20 + voicesPlayingCount * 68 + (grainSize * 60) / 100;465// Cap to 1200 to fix FFT, see issue #9956.466return std::min(cycles, 1200);467}468469void SasVoice::ReadSamples(s16 *output, int numSamples) {470// Read N samples into the resample buffer. Could do either PCM or VAG here.471switch (type) {472case VOICETYPE_VAG:473vag.GetSamples(output, numSamples);474break;475case VOICETYPE_PCM:476{477int needed = numSamples;478s16 *out = output;479while (needed > 0) {480u32 size = std::min(pcmSize - pcmIndex, needed);481if (!on) {482pcmIndex = 0;483break;484}485Memory::Memcpy(out, pcmAddr + pcmIndex * sizeof(s16), size * sizeof(s16), "SasVoicePCM");486pcmIndex += size;487needed -= size;488out += size;489if (pcmIndex >= pcmSize) {490if (!loop) {491// All out, quit. We'll end in HaveSamplesEnded().492break;493}494pcmIndex = pcmLoopPos;495}496}497if (needed > 0) {498memset(out, 0, needed * sizeof(s16));499}500}501break;502case VOICETYPE_ATRAC3:503atrac3.getNextSamples(output, numSamples);504break;505default:506memset(output, 0, numSamples * sizeof(s16));507break;508}509}510511bool SasVoice::HaveSamplesEnded() const {512switch (type) {513case VOICETYPE_VAG:514return vag.End();515516case VOICETYPE_PCM:517return pcmIndex >= pcmSize;518519case VOICETYPE_ATRAC3:520return atrac3.End();521522default:523return false;524}525}526527void SasInstance::MixVoice(SasVoice &voice) {528switch (voice.type) {529case VOICETYPE_VAG:530if (voice.type == VOICETYPE_VAG && !voice.vagAddr)531break;532// else fallthrough! Don't change the check above.533case VOICETYPE_PCM:534if (voice.type == VOICETYPE_PCM && !voice.pcmAddr)535break;536// else fallthrough! Don't change the check above.537default:538// This feels a bit hacky. The first 32 samples after a keyon are 0s.539int delay = 0;540if (voice.envelope.NeedsKeyOn()) {541const bool ignorePitch = voice.type == VOICETYPE_PCM && voice.pitch > PSP_SAS_PITCH_BASE;542delay = ignorePitch ? 32 : (32 * (u32)voice.pitch) >> PSP_SAS_PITCH_BASE_SHIFT;543// VAG seems to have an extra sample delay (not shared by PCM.)544if (voice.type == VOICETYPE_VAG)545++delay;546}547548// Resample to the correct pitch, writing exactly "grainSize" samples. We need a buffer that can549// fit 4x that, as the max pitch is 0x4000.550// TODO: Special case no-resample case (and 2x and 0.5x) for speed, it's not uncommon551552// Two passes: First read, then resample.553mixTemp_[0] = voice.resampleHist[0];554mixTemp_[1] = voice.resampleHist[1];555556int voicePitch = voice.pitch;557u32 sampleFrac = voice.sampleFrac;558int samplesToRead = (sampleFrac + voicePitch * std::max(0, grainSize - delay)) >> PSP_SAS_PITCH_BASE_SHIFT;559if (samplesToRead > ARRAY_SIZE(mixTemp_) - 2) {560ERROR_LOG(Log::sceSas, "Too many samples to read (%d)! This shouldn't happen.", samplesToRead);561samplesToRead = ARRAY_SIZE(mixTemp_) - 2;562}563int readPos = 2;564if (voice.envelope.NeedsKeyOn()) {565readPos = 0;566samplesToRead += 2;567}568voice.ReadSamples(&mixTemp_[readPos], samplesToRead);569int tempPos = readPos + samplesToRead;570571for (int i = 0; i < delay; ++i) {572// Walk the curve. This means we'll reach ATTACK already, likely.573// This matches the results of tests (but maybe we can just remove the STATE_KEYON_STEP hack.)574voice.envelope.Step();575}576577const bool needsInterp = voicePitch != PSP_SAS_PITCH_BASE || (sampleFrac & PSP_SAS_PITCH_MASK) != 0;578for (int i = delay; i < grainSize; i++) {579const int16_t *s = mixTemp_ + (sampleFrac >> PSP_SAS_PITCH_BASE_SHIFT);580581// Linear interpolation. Good enough. Need to make resampleHist bigger if we want more.582int sample = s[0];583if (needsInterp) {584int f = sampleFrac & PSP_SAS_PITCH_MASK;585sample = (s[0] * (PSP_SAS_PITCH_MASK - f) + s[1] * f) >> PSP_SAS_PITCH_BASE_SHIFT;586}587sampleFrac += voicePitch;588589// The maximum envelope height (PSP_SAS_ENVELOPE_HEIGHT_MAX) is (1 << 30) - 1.590// Reduce it to 14 bits, by shifting off 15. Round up by adding (1 << 14) first.591int envelopeValue = voice.envelope.GetHeight();592voice.envelope.Step();593envelopeValue = (envelopeValue + (1 << 14)) >> 15;594595// We just scale by the envelope before we scale by volumes.596// Again, we round up by adding (1 << 14) first (*after* multiplying.)597sample = ((sample * envelopeValue) + (1 << 14)) >> 15;598599// We mix into this 32-bit temp buffer and clip in a second loop600// Ideally, the shift right should be there too but for now I'm concerned about601// not overflowing.602mixBuffer[i * 2] += (sample * voice.volumeLeft) >> 12;603mixBuffer[i * 2 + 1] += (sample * voice.volumeRight) >> 12;604sendBuffer[i * 2] += sample * voice.effectLeft >> 12;605sendBuffer[i * 2 + 1] += sample * voice.effectRight >> 12;606}607608voice.resampleHist[0] = mixTemp_[tempPos - 2];609voice.resampleHist[1] = mixTemp_[tempPos - 1];610611voice.sampleFrac = sampleFrac - (tempPos - 2) * PSP_SAS_PITCH_BASE;612613if (voice.HaveSamplesEnded())614voice.envelope.End();615if (voice.envelope.HasEnded()) {616// NOTICE_LOG(Log::SasMix, "Hit end of envelope");617voice.playing = false;618voice.on = false;619}620}621}622623void SasInstance::Mix(u32 outAddr, u32 inAddr, int leftVol, int rightVol) {624for (int v = 0; v < PSP_SAS_VOICES_MAX; v++) {625SasVoice &voice = voices[v];626if (!voice.playing || voice.paused)627continue;628MixVoice(voice);629}630631// Then mix the send buffer in with the rest.632633// Alright, all voices mixed. Let's convert and clip, and at the same time, wipe mixBuffer for next time. Could also dither.634s16 *outp = (s16 *)Memory::GetPointerWriteRange(outAddr, 4 * grainSize);635const s16 *inp = inAddr ? (const s16 *)Memory::GetPointerRange(inAddr, 4 * grainSize) : 0;636if (!outp) {637WARN_LOG_REPORT(Log::sceSas, "Bad SAS Mix output address: %08x, grain=%d", outAddr, grainSize);638} else if (outputMode == PSP_SAS_OUTPUTMODE_MIXED) {639// Okay, apply effects processing to the Send buffer.640WriteMixedOutput(outp, inp, leftVol, rightVol);641if (MemBlockInfoDetailed()) {642if (inp)643NotifyMemInfo(MemBlockFlags::READ, inAddr, grainSize * sizeof(u16) * 2, "SasMix");644NotifyMemInfo(MemBlockFlags::WRITE, outAddr, grainSize * sizeof(u16) * 2, "SasMix");645}646} else {647s16 *outpL = outp + grainSize * 0;648s16 *outpR = outp + grainSize * 1;649s16 *outpSendL = outp + grainSize * 2;650s16 *outpSendR = outp + grainSize * 3;651WARN_LOG_REPORT_ONCE(sasraw, Log::SasMix, "sceSasCore: raw outputMode");652for (int i = 0; i < grainSize * 2; i += 2) {653*outpL++ = clamp_s16(mixBuffer[i + 0]);654*outpR++ = clamp_s16(mixBuffer[i + 1]);655*outpSendL++ = clamp_s16(sendBuffer[i + 0]);656*outpSendR++ = clamp_s16(sendBuffer[i + 1]);657}658NotifyMemInfo(MemBlockFlags::WRITE, outAddr, grainSize * sizeof(u16) * 4, "SasMix");659}660memset(mixBuffer, 0, grainSize * sizeof(int) * 2);661memset(sendBuffer, 0, grainSize * sizeof(int) * 2);662663#ifdef AUDIO_TO_FILE664fwrite(Memory::GetPointer(outAddr, grainSize * 2 * 2), 1, grainSize * 2 * 2, audioDump);665#endif666}667668void SasInstance::WriteMixedOutput(s16 *outp, const s16 *inp, int leftVol, int rightVol) {669const bool dry = waveformEffect.isDryOn != 0;670const bool wet = waveformEffect.isWetOn != 0;671if (wet) {672ApplyWaveformEffect();673}674675if (inp) {676for (int i = 0; i < grainSize * 2; i += 2) {677int sampleL = ((*inp++) * leftVol >> 12);678int sampleR = ((*inp++) * rightVol >> 12);679if (dry) {680sampleL += mixBuffer[i + 0];681sampleR += mixBuffer[i + 1];682}683if (wet) {684sampleL += sendBufferProcessed[i + 0];685sampleR += sendBufferProcessed[i + 1];686}687*outp++ = clamp_s16(sampleL);688*outp++ = clamp_s16(sampleR);689}690} else {691// These are the optimal cases.692if (dry && wet) {693for (int i = 0; i < grainSize * 2; i += 2) {694*outp++ = clamp_s16(mixBuffer[i + 0] + sendBufferProcessed[i + 0]);695*outp++ = clamp_s16(mixBuffer[i + 1] + sendBufferProcessed[i + 1]);696}697} else if (dry) {698for (int i = 0; i < grainSize * 2; i += 2) {699*outp++ = clamp_s16(mixBuffer[i + 0]);700*outp++ = clamp_s16(mixBuffer[i + 1]);701}702} else {703// This is another uncommon case, dry must be off but let's keep it for clarity.704for (int i = 0; i < grainSize * 2; i += 2) {705int sampleL = 0;706int sampleR = 0;707if (dry) {708sampleL += mixBuffer[i + 0];709sampleR += mixBuffer[i + 1];710}711if (wet) {712sampleL += sendBufferProcessed[i + 0];713sampleR += sendBufferProcessed[i + 1];714}715*outp++ = clamp_s16(sampleL);716*outp++ = clamp_s16(sampleR);717}718}719}720}721722void SasInstance::SetWaveformEffectType(int type) {723if (type != waveformEffect.type) {724waveformEffect.type = type;725reverb_.SetPreset(type);726}727}728729// http://psx.rules.org/spu.txt has some information about setting up the delay time by modifying the delay preset.730// See http://report.ppsspp.org/logs/kind/772 for a list of games that use different types. Maybe can help us figure out731// which is which.732void SasInstance::ApplyWaveformEffect() {733// First, downsample the send buffer to 22khz. We do this naively for now.734for (int i = 0; i < grainSize / 2; i++) {735sendBufferDownsampled[i * 2] = clamp_s16(sendBuffer[i * 4]);736sendBufferDownsampled[i * 2 + 1] = clamp_s16(sendBuffer[i * 4 + 1]);737}738739// Volume max is 0x1000, while our factor is up to 0x8000. Shifting left by 3 fixes that.740reverb_.ProcessReverb(sendBufferProcessed, sendBufferDownsampled, grainSize / 2, waveformEffect.leftVol << 3, waveformEffect.rightVol << 3);741}742743void SasInstance::DoState(PointerWrap &p) {744auto s = p.Section("SasInstance", 1);745if (!s)746return;747748Do(p, grainSize);749if (p.mode == p.MODE_READ) {750if (grainSize > 0) {751SetGrainSize(grainSize);752} else {753ClearGrainSize();754}755}756757Do(p, maxVoices);758Do(p, sampleRate);759Do(p, outputMode);760761// SetGrainSize() / ClearGrainSize() should've made our buffers match.762if (mixBuffer != NULL && grainSize > 0) {763DoArray(p, mixBuffer, grainSize * 2);764}765if (sendBuffer != NULL && grainSize > 0) {766DoArray(p, sendBuffer, grainSize * 2);767}768if (sendBuffer != NULL && grainSize > 0) {769// Backwards compat770int16_t *resampleBuf = new int16_t[grainSize * 4 + 3]();771DoArray(p, resampleBuf, grainSize * 4 + 3);772delete[] resampleBuf;773}774775int n = PSP_SAS_VOICES_MAX;776Do(p, n);777if (n != PSP_SAS_VOICES_MAX) {778ERROR_LOG(Log::SaveState, "Wrong number of SAS voices");779return;780}781DoArray(p, voices, ARRAY_SIZE(voices));782Do(p, waveformEffect);783if (p.mode == p.MODE_READ) {784reverb_.SetPreset(waveformEffect.type);785}786}787788void SasVoice::Reset() {789resampleHist[0] = 0;790resampleHist[1] = 0;791}792793void SasVoice::KeyOn() {794envelope.KeyOn();795switch (type) {796case VOICETYPE_VAG:797if (Memory::IsValidAddress(vagAddr)) {798vag.Start(vagAddr, vagSize, loop);799} else {800ERROR_LOG(Log::SasMix, "Invalid VAG address %08x", vagAddr);801return;802}803break;804default:805break;806}807playing = true;808on = true;809paused = false;810sampleFrac = 0;811}812813void SasVoice::KeyOff() {814on = false;815envelope.KeyOff();816}817818void SasVoice::DoState(PointerWrap &p) {819auto s = p.Section("SasVoice", 1, 3);820if (!s)821return;822823Do(p, playing);824Do(p, paused);825Do(p, on);826827Do(p, type);828829Do(p, vagAddr);830Do(p, vagSize);831Do(p, pcmAddr);832Do(p, pcmSize);833Do(p, pcmIndex);834if (s >= 2) {835Do(p, pcmLoopPos);836} else {837pcmLoopPos = 0;838}839Do(p, sampleRate);840841Do(p, sampleFrac);842Do(p, pitch);843Do(p, loop);844if (s < 2 && type == VOICETYPE_PCM) {845// We set loop incorrectly before, and always looped.846// Let's keep always looping, since it's usually right.847loop = true;848}849850Do(p, noiseFreq);851852Do(p, volumeLeft);853Do(p, volumeRight);854if (s < 3) {855// There were extra variables here that were for the same purpose.856Do(p, effectLeft);857Do(p, effectRight);858}859Do(p, effectLeft);860Do(p, effectRight);861DoArray(p, resampleHist, ARRAY_SIZE(resampleHist));862863envelope.DoState(p);864vag.DoState(p);865atrac3.DoState(p);866}867868void ADSREnvelope::WalkCurve(int type, int rate) {869s64 expDelta;870switch (type) {871case PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE:872height_ += rate;873break;874875case PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE:876height_ -= rate;877break;878879case PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT:880if (height_ <= (s64)PSP_SAS_ENVELOPE_HEIGHT_MAX * 3 / 4) {881height_ += rate;882} else {883height_ += rate / 4;884}885break;886887case PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE:888expDelta = height_ - PSP_SAS_ENVELOPE_HEIGHT_MAX;889// Flipping the sign so that we can shift in the top bits.890expDelta += (-expDelta * rate) >> 32;891height_ = expDelta + PSP_SAS_ENVELOPE_HEIGHT_MAX - (rate + 3UL) / 4UL;892break;893894case PSP_SAS_ADSR_CURVE_MODE_EXPONENT_INCREASE:895expDelta = height_ - PSP_SAS_ENVELOPE_HEIGHT_MAX;896// Flipping the sign so that we can shift in the top bits.897expDelta += (-expDelta * rate) >> 32;898height_ = expDelta + 0x4000 + PSP_SAS_ENVELOPE_HEIGHT_MAX;899break;900901case PSP_SAS_ADSR_CURVE_MODE_DIRECT:902height_ = rate; // Simple :)903break;904}905}906907void ADSREnvelope::SetState(ADSRState state) {908if (height_ > PSP_SAS_ENVELOPE_HEIGHT_MAX) {909height_ = PSP_SAS_ENVELOPE_HEIGHT_MAX;910}911// TODO: Also check for height_ < 0 and set to 0?912state_ = state;913}914915inline void ADSREnvelope::Step() {916switch (state_) {917case STATE_ATTACK:918WalkCurve(attackType, attackRate);919if (height_ >= PSP_SAS_ENVELOPE_HEIGHT_MAX || height_ < 0)920SetState(STATE_DECAY);921break;922case STATE_DECAY:923WalkCurve(decayType, decayRate);924if (height_ < sustainLevel)925SetState(STATE_SUSTAIN);926break;927case STATE_SUSTAIN:928WalkCurve(sustainType, sustainRate);929if (height_ <= 0) {930height_ = 0;931SetState(STATE_RELEASE);932}933break;934case STATE_RELEASE:935WalkCurve(releaseType, releaseRate);936if (height_ <= 0) {937height_ = 0;938SetState(STATE_OFF);939}940break;941case STATE_OFF:942// Do nothing943break;944945case STATE_KEYON:946height_ = 0;947SetState(STATE_KEYON_STEP);948break;949case STATE_KEYON_STEP:950// This entire state is pretty much a hack to reproduce PSP behavior.951// The STATE_KEYON state is a real state, but not sure how it switches.952// It takes 32 steps at 0 for keyon to "kick in", 31 should shift to 0 anyway.953height_++;954if (height_ >= 31) {955height_ = 0;956SetState(STATE_ATTACK);957}958break;959}960}961962void ADSREnvelope::KeyOn() {963SetState(STATE_KEYON);964}965966void ADSREnvelope::KeyOff() {967SetState(STATE_RELEASE);968}969970void ADSREnvelope::End() {971SetState(STATE_OFF);972height_ = 0;973}974975void ADSREnvelope::DoState(PointerWrap &p) {976auto s = p.Section("ADSREnvelope", 1, 2);977if (!s) {978return;979}980981Do(p, attackRate);982Do(p, decayRate);983Do(p, sustainRate);984Do(p, releaseRate);985Do(p, attackType);986Do(p, decayType);987Do(p, sustainType);988Do(p, sustainLevel);989Do(p, releaseType);990if (s < 2) {991Do(p, state_);992if (state_ == 4) {993state_ = STATE_OFF;994}995int stepsLegacy;996Do(p, stepsLegacy);997} else {998Do(p, state_);999}1000Do(p, height_);1001}10021003const char *ADSRCurveModeAsString(SasADSRCurveMode mode) {1004switch (mode) {1005case PSP_SAS_ADSR_CURVE_MODE_DIRECT: return "D";1006case PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE: return "L+";1007case PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE: return "L-";1008case PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT: return "LB";1009case PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE: return "E-";1010case PSP_SAS_ADSR_CURVE_MODE_EXPONENT_INCREASE: return "E+";1011default: return "N/A";1012}1013}101410151016