mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
228 lines
No EOL
6.2 KiB
C++
228 lines
No EOL
6.2 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#include "base/basictypes.h"
|
|
#include "../Globals.h"
|
|
#include "../MemMap.h"
|
|
#include "SasAudio.h"
|
|
|
|
static const double f[5][2] =
|
|
{ { 0.0, 0.0 },
|
|
{ 60.0 / 64.0, 0.0 },
|
|
{ 115.0 / 64.0, -52.0 / 64.0 },
|
|
{ 98.0 / 64.0, -55.0 / 64.0 },
|
|
{ 122.0 / 64.0, -60.0 / 64.0 } };
|
|
|
|
void VagDecoder::Start(u8 *data)
|
|
{
|
|
data_ = data;
|
|
curSample = 28;
|
|
s_1 = 0.0; // per block?
|
|
s_2 = 0.0;
|
|
}
|
|
|
|
bool VagDecoder::Decode()
|
|
{
|
|
int predict_nr = GetByte();
|
|
int shift_factor = predict_nr & 0xf;
|
|
predict_nr >>= 4;
|
|
int flags = GetByte();
|
|
if (flags == 7)
|
|
{
|
|
end_ = true;
|
|
return false;
|
|
}
|
|
for (int i = 0; i < 28; i += 2)
|
|
{
|
|
int d = GetByte();
|
|
int s = (d & 0xf) << 12;
|
|
if (s & 0x8000)
|
|
s |= 0xffff0000;
|
|
samples[i] = (double)(s >> shift_factor);
|
|
s = (d & 0xf0) << 8;
|
|
if (s & 0x8000)
|
|
s |= 0xffff0000;
|
|
samples[i + 1] = (double)(s >> shift_factor);
|
|
}
|
|
for (int i = 0; i < 28; i++)
|
|
{
|
|
samples[i] = samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1];
|
|
s_2 = s_1;
|
|
s_1 = samples[i];
|
|
}
|
|
curSample = 0;
|
|
return true;
|
|
}
|
|
|
|
// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/sceSasCore.java
|
|
|
|
int simpleRate(int n) {
|
|
n &= 0x7F;
|
|
if (n == 0x7F) {
|
|
return 0;
|
|
}
|
|
int rate = ((7 - (n & 0x3)) << 26) >> (n >> 2);
|
|
if (rate == 0) {
|
|
return 1;
|
|
}
|
|
return rate;
|
|
}
|
|
|
|
static int getAttackRate(int bitfield1) {
|
|
return simpleRate(bitfield1 >> 8);
|
|
}
|
|
|
|
static int getAttackType(int bitfield1) {
|
|
return (bitfield1 & 0x8000) == 0 ? PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE : PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT;
|
|
}
|
|
|
|
static int getDecayRate(int bitfield1) {
|
|
return 0x80000000 >> ((bitfield1 >> 4) & 0x000F);
|
|
}
|
|
|
|
static int getSustainRate(int bitfield2) {
|
|
return simpleRate(bitfield2 >> 6);
|
|
}
|
|
|
|
static int getSustainType(int bitfield2) {
|
|
switch (bitfield2 >> 13) {
|
|
case 0: return PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE;
|
|
case 2: return PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE;
|
|
case 4: return PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT;
|
|
case 6: return PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE;
|
|
}
|
|
ERROR_LOG(HLE,"sasSetSimpleADSR,ERROR_SAS_INVALID_ADSR_CURVE_MODE");
|
|
return 0;
|
|
}
|
|
|
|
static int getReleaseType(int bitfield2) {
|
|
return (bitfield2 & 0x0020) == 0 ? PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE : PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE;
|
|
}
|
|
|
|
static int getReleaseRate(int bitfield2) {
|
|
int n = bitfield2 & 0x001F;
|
|
if (n == 31) {
|
|
return 0;
|
|
}
|
|
if (getReleaseType(bitfield2) == PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE) {
|
|
return (0x40000000 >> (n + 2));
|
|
}
|
|
return (0x8000000 >> n);
|
|
}
|
|
|
|
static int getSustainLevel(int bitfield1) {
|
|
return ((bitfield1 & 0x000F) + 1) << 26;
|
|
}
|
|
|
|
void ADSREnvelope::SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2)
|
|
{
|
|
attackRate = getAttackRate(ADSREnv1);
|
|
attackType = getAttackType(ADSREnv1);
|
|
decayRate = getDecayRate(ADSREnv1);
|
|
decayType = PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE;
|
|
sustainRate = getSustainRate(ADSREnv2);
|
|
sustainType = getSustainType(ADSREnv2);
|
|
releaseRate = getReleaseRate(ADSREnv2);
|
|
releaseType = getReleaseType(ADSREnv2);
|
|
sustainLevel = getSustainLevel(ADSREnv1);
|
|
}
|
|
|
|
void SasInstance::Mix(u32 outAddr)
|
|
{
|
|
for (int v = 0; v < PSP_SAS_VOICES_MAX; v++) {
|
|
SasVoice &voice = voices[v];
|
|
if (!voice.playing)
|
|
continue;
|
|
|
|
// TODO: Special case no-resample case for speed
|
|
|
|
if (voice.vagAddr != 0) {
|
|
// Load resample history (so we can use a wide filter)
|
|
resampleBuffer[0] = voice.resampleHist[0];
|
|
resampleBuffer[1] = voice.resampleHist[1];
|
|
|
|
// Figure out number of samples to read.
|
|
int curSample = voice.samplePos / PSP_SAS_PITCH_BASE;
|
|
int lastSample = (voice.samplePos + grainSize * voice.pitch) / PSP_SAS_PITCH_BASE;
|
|
int numSamples = lastSample - curSample;
|
|
|
|
// Read N samples into the resample buffer. Could do either PCM or VAG here.
|
|
|
|
for (int i = 0; i < numSamples; i++) {
|
|
int sample = voice.vag.GetSample();
|
|
if (voice.samplePos >= voice.vagSize || voice.vag.End()) {
|
|
if (voice.loop) {
|
|
voice.Loop();
|
|
} else {
|
|
voice.playing = false;
|
|
// TODO: clear rest of buffer
|
|
memset(resampleBuffer, 0, (numSamples - i) * sizeof(resampleBuffer[0]));
|
|
}
|
|
break;
|
|
}
|
|
resampleBuffer[i + 2] = sample;
|
|
}
|
|
|
|
// Save resample history
|
|
voice.resampleHist[0] = resampleBuffer[numSamples - 2];
|
|
voice.resampleHist[1] = resampleBuffer[numSamples - 1];
|
|
|
|
// Resample to the correct pitch, writing exactly "grainSize" samples.
|
|
|
|
int bufferPos = (voice.samplePos & (PSP_SAS_PITCH_BASE - 1)) + 2 * PSP_SAS_PITCH_BASE;
|
|
for (int i = 0; i < grainSize; i++) {
|
|
// For now: nearest neighbour, not even using the resample history at all.
|
|
int sample = resampleBuffer[bufferPos / PSP_SAS_PITCH_BASE];
|
|
bufferPos += voice.pitch;
|
|
|
|
// We mix into this 32-bit temp buffer and clip in a second loop
|
|
mixBuffer[i * 2] += sample * voice.volumeLeft >> 15;
|
|
mixBuffer[i * 2 + 1] += sample * voice.volumeRight >> 15;
|
|
}
|
|
}
|
|
else if (voice.pcmAddr != 0) {
|
|
// PCM mixing should be easy, can share code with VAG
|
|
}
|
|
else if (voice.noiseFreq != 0) {
|
|
// Generate noise?
|
|
}
|
|
}
|
|
|
|
// Alright, all voices mixed. Let's convert and clip, and at the same time, wipe mixBuffer for next time. Could also dither.
|
|
for (int i = 0; i < grainSize * 2; i++) {
|
|
s16 *out = (s16 *)Memory::GetPointer(outAddr);
|
|
int sample = mixBuffer[i];
|
|
mixBuffer[i] = 0;
|
|
if (sample > 32767) out[i] = 32767;
|
|
else if (sample < -32768) out[i] = -32768;
|
|
else out[i] = sample;
|
|
}
|
|
}
|
|
|
|
void SasVoice::Reset()
|
|
{
|
|
resampleHist[0] = 0;
|
|
resampleHist[1] = 0;
|
|
}
|
|
|
|
void SasVoice::Loop()
|
|
{
|
|
if (vagAddr) {
|
|
vag.Start(Memory::GetPointer(vagAddr));
|
|
samplePos = 0;
|
|
}
|
|
} |