ppsspp/Core/HW/SasAudio.cpp
2012-12-16 21:20:16 +01:00

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;
}
}