/*
This file is part of jpcsp.
Jpcsp 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, either version 3 of the License, or
(at your option) any later version.
Jpcsp 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 for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.HLE.modules;
import static jpcsp.HLE.kernel.types.SceKernelErrors.ERROR_SAS_INVALID_ADDRESS;
import static jpcsp.HLE.kernel.types.SceKernelErrors.ERROR_SAS_INVALID_ADSR_CURVE_MODE;
import jpcsp.HLE.CanBeNull;
import jpcsp.HLE.CheckArgument;
import jpcsp.HLE.HLEFunction;
import jpcsp.HLE.HLELogging;
import jpcsp.HLE.HLEModule;
import jpcsp.HLE.HLEUnimplemented;
import jpcsp.HLE.SceKernelErrorException;
import jpcsp.HLE.TPointer;
import jpcsp.HLE.TPointer32;
import jpcsp.Emulator;
import jpcsp.Memory;
import jpcsp.Processor;
import jpcsp.HLE.Modules;
import jpcsp.HLE.kernel.managers.SceUidManager;
import jpcsp.HLE.kernel.types.SceKernelErrors;
import jpcsp.HLE.modules.sceAtrac3plus.AtracID;
import jpcsp.memory.IMemoryWriter;
import jpcsp.memory.MemoryWriter;
import jpcsp.sound.SoundVoice;
import jpcsp.sound.SoundMixer;
import jpcsp.sound.SoundVoice.VoiceADSREnvelope;
import org.apache.log4j.Logger;
public class sceSasCore extends HLEModule {
public static Logger log = Modules.getLogger("sceSasCore");
@Override
public void start() {
sasCoreUid = -1;
voices = new SoundVoice[32];
for (int i = 0; i < voices.length; i++) {
voices[i] = new SoundVoice(i);
}
mixer = new SoundMixer(voices);
grainSamples = PSP_SAS_GRAIN_SAMPLES;
outputMode = PSP_SAS_OUTPUTMODE_STEREO;
super.start();
}
public static final int PSP_SAS_VOICES_MAX = 32;
public static final int PSP_SAS_GRAIN_SAMPLES = 256;
public static final int PSP_SAS_VOL_MAX = 0x1000;
public static final int PSP_SAS_LOOP_MODE_OFF = 0;
public static final int PSP_SAS_LOOP_MODE_ON = 1;
public static final int PSP_SAS_PITCH_MIN = 0x1;
public static final int PSP_SAS_PITCH_BASE = 0x1000;
public static final int PSP_SAS_PITCH_MAX = 0x4000;
public static final int PSP_SAS_NOISE_FREQ_MAX = 0x3F;
public static final int PSP_SAS_ENVELOPE_HEIGHT_MAX = 0x40000000;
public static final int PSP_SAS_ENVELOPE_FREQ_MAX = 0x7FFFFFFF;
public static final int PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE = 0;
public static final int PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE = 1;
public static final int PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT = 2;
public static final int PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE = 3;
public static final int PSP_SAS_ADSR_CURVE_MODE_EXPONENT_INCREASE = 4;
public static final int PSP_SAS_ADSR_CURVE_MODE_DIRECT = 5;
public static final int PSP_SAS_ADSR_ATTACK = 1;
public static final int PSP_SAS_ADSR_DECAY = 2;
public static final int PSP_SAS_ADSR_SUSTAIN = 4;
public static final int PSP_SAS_ADSR_RELEASE = 8;
public static final int PSP_SAS_OUTPUTMODE_STEREO = 0;
public static final int PSP_SAS_OUTPUTMODE_MONO = 1;
public static final int PSP_SAS_EFFECT_TYPE_OFF = -1;
public static final int PSP_SAS_EFFECT_TYPE_ROOM = 0;
public static final int PSP_SAS_EFFECT_TYPE_UNK1 = 1;
public static final int PSP_SAS_EFFECT_TYPE_UNK2 = 2;
public static final int PSP_SAS_EFFECT_TYPE_UNK3 = 3;
public static final int PSP_SAS_EFFECT_TYPE_HALL = 4;
public static final int PSP_SAS_EFFECT_TYPE_SPACE = 5;
public static final int PSP_SAS_EFFECT_TYPE_ECHO = 6;
public static final int PSP_SAS_EFFECT_TYPE_DELAY = 7;
public static final int PSP_SAS_EFFECT_TYPE_PIPE = 8;
private static final String[] sasADSRCurveTypeNames = new String[] {
"LINEAR_INCREASE",
"LINEAR_DECREASE",
"LINEAR_BENT",
"EXPONENT_REV",
"EXPONENT",
"DIRECT"
};
private static final int SASCORE_ATRAC3_CONTEXT_OFFSET = 20;
private static final int SASCORE_VOICE_SIZE = 56;
protected int sasCoreUid;
protected SoundVoice[] voices;
protected SoundMixer mixer;
protected int grainSamples;
protected int outputMode;
protected static final int waveformBufMaxSize = 1024; // 256 sound samples.
protected int waveformEffectType;
protected int waveformEffectLeftVol;
protected int waveformEffectRightVol;
protected int waveformEffectDelay;
protected int waveformEffectFeedback;
protected boolean waveformEffectIsDryOn;
protected boolean waveformEffectIsWetOn;
protected static final int sasCoreDelay = 5000; // Average microseconds, based on PSP tests.
protected static final String sasCodeUidPurpose = "sceSasCore-SasCore";
public static String getSasADSRCurveTypeName(int curveType) {
if (curveType < 0 || curveType >= sasADSRCurveTypeNames.length) {
return String.format("UNKNOWN_%d", curveType);
}
return sasADSRCurveTypeNames[curveType];
}
protected void checkSasAddressGood(int sasCore) {
if (!Memory.isAddressGood(sasCore)) {
log.warn(String.format("%s bad sasCore Address 0x%08X", getCallingFunctionName(3), sasCore));
throw(new SceKernelErrorException(ERROR_SAS_INVALID_ADDRESS));
}
if (!Memory.isAddressAlignedTo(sasCore, 64)) {
log.warn(String.format("%s bad sasCore Address 0x%08X (not aligned to 64)", getCallingFunctionName(3), sasCore));
throw(new SceKernelErrorException(ERROR_SAS_INVALID_ADDRESS));
}
}
protected void checkSasHandleGood(int sasCore) {
checkSasAddressGood(sasCore);
if (Processor.memory.read32(sasCore) != sasCoreUid) {
log.warn(String.format("%s bad sasCoreUid 0x%X (should be 0x%X)", getCallingFunctionName(3), Processor.memory.read32(sasCore), sasCoreUid));
throw(new SceKernelErrorException(SceKernelErrors.ERROR_SAS_NOT_INIT));
}
}
protected void checkVoiceNumberGood(int voice) {
if (voice < 0 || voice >= voices.length) {
log.warn(String.format("%s bad voice number %d", getCallingFunctionName(3), voice));
throw(new SceKernelErrorException(SceKernelErrors.ERROR_SAS_INVALID_VOICE_INDEX));
}
}
protected void checkSasAndVoiceHandlesGood(int sasCore, int voice) {
checkSasHandleGood(sasCore);
checkVoiceNumberGood(voice);
}
protected void checkADSRmode(int curveIndex, int flag, int curveType) {
int[] validCurveTypes = new int[] {
(1 << PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE) | (1 << PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT) | (1 << PSP_SAS_ADSR_CURVE_MODE_EXPONENT_INCREASE),
(1 << PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE) | (1 << PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE) | (1 << PSP_SAS_ADSR_CURVE_MODE_DIRECT),
(1 << PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE) | (1 << PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE) | (1 << PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT) | (1 << PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE) | (1 << PSP_SAS_ADSR_CURVE_MODE_EXPONENT_INCREASE) | (1 << PSP_SAS_ADSR_CURVE_MODE_DIRECT),
(1 << PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE) | (1 << PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE) | (1 << PSP_SAS_ADSR_CURVE_MODE_DIRECT)
};
if ((flag & (1 << curveIndex)) != 0) {
if ((validCurveTypes[curveIndex] & (1 << curveType)) == 0) {
throw new SceKernelErrorException(SceKernelErrors.ERROR_SAS_INVALID_ADSR_CURVE_MODE);
}
}
}
protected void checkVoiceNotPaused(int voice, boolean requiredOnState) {
if (voices[voice].isPaused() || voices[voice].isOn() != requiredOnState) {
if (log.isDebugEnabled()) {
log.debug(String.format("checkVoiceNotPaused returning 0x%08X(ERROR_SAS_VOICE_PAUSED)", SceKernelErrors.ERROR_SAS_VOICE_PAUSED));
}
throw new SceKernelErrorException(SceKernelErrors.ERROR_SAS_VOICE_PAUSED);
}
}
public int checkVolume(int volume) {
if (volume < -PSP_SAS_VOL_MAX || volume > PSP_SAS_VOL_MAX) {
throw new SceKernelErrorException(SceKernelErrors.ERROR_SAS_INVALID_VOLUME_VAL);
}
return volume;
}
private void delayThread(long startMicros, int delayMicros, int minimumDelayMicros) {
long now = Emulator.getClock().microTime();
int threadDelayMicros = delayMicros - (int) (now - startMicros);
threadDelayMicros = Math.max(threadDelayMicros, minimumDelayMicros);
if (threadDelayMicros > 0) {
Modules.ThreadManForUserModule.hleKernelDelayThread(threadDelayMicros, false);
} else {
Modules.ThreadManForUserModule.hleRescheduleCurrentThread();
}
}
private void delayThreadSasCore(long startMicros) {
// Based on PSP timings: the delay for __sceSasCore is always about
// 600 microseconds, independently of the number of samples generated
// and of the number of voices currently playing.
int delayMicros = 600;
delayThread(startMicros, delayMicros, delayMicros);
}
public int getOutputMode() {
return outputMode;
}
protected void setSasCoreAtrac3Context(int sasCore, int voice, int atrac3Context) {
Memory mem = Memory.getInstance();
mem.write32(sasCore + SASCORE_VOICE_SIZE * voice + SASCORE_ATRAC3_CONTEXT_OFFSET, atrac3Context);
}
protected int getSasCoreAtrac3Context(int sasCore, int voice) {
Memory mem = Memory.getInstance();
return mem.read32(sasCore + SASCORE_VOICE_SIZE * voice + SASCORE_ATRAC3_CONTEXT_OFFSET);
}
/**
* Set the ADSR rates for a specific voice.
*
* @param sasCore sasCore handle
* @param voice voice number, [0..31]
* @param flag Bitfield to indicate which of the following 4 parameters
* has to be updated. Logical OR of the following values:
* 0x1: update attack rate
* 0x2: update decay rate
* 0x4: update sustain rate
* 0x8: update release rate
* @param attack Envelope's attack rate, [0..0x7FFFFFFF].
* @param decay Envelope's decay rate, [0..0x7FFFFFFF].
* @param sustain Envelope's sustain rate, [0..0x7FFFFFFF].
* @param release Envelope's release rate, [0..0x7FFFFFFF].
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
*/
@HLEFunction(nid = 0x019B25EB, version = 150, checkInsideInterrupt = true)
public int __sceSasSetADSR(int sasCore, int voice, int flag, int attack, int decay, int sustain, int release) {
checkSasAndVoiceHandlesGood(sasCore, voice);
VoiceADSREnvelope envelope = voices[voice].getEnvelope();
if ((flag & 0x1) != 0) envelope.AttackRate = attack;
if ((flag & 0x2) != 0) envelope.DecayRate = decay;
if ((flag & 0x4) != 0) envelope.SustainRate = sustain;
if ((flag & 0x8) != 0) envelope.ReleaseRate = release;
if (log.isDebugEnabled()) {
log.debug(String.format("__sceSasSetADSR voice=0x%X: %s", voice, envelope.toString()));
}
return 0;
}
/**
* Set the wave form effect delay and feedback parameters (unknown parameters).
*
* @param sasCore sasCore handle
* @param delay (unknown) wave form effect delay
* @param feedback (unknown) wave form effect feedback
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* @return
*/
@HLEFunction(nid = 0x267A6DD2, version = 150, checkInsideInterrupt = true)
public int __sceSasRevParam(int sasCore, int delay, int feedback) {
checkSasHandleGood(sasCore);
waveformEffectDelay = delay;
waveformEffectFeedback = feedback;
return 0;
}
/**
* Get the pause flag for all the voices.
*
* @param sasCore sasCore handle
* @return bitfield with bit 0 for voice 0, bit 1 for voice 1...
* bit=0, corresponding voice is not paused
* bit=1, corresponding voice is paused
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
*/
@HLEFunction(nid = 0x2C8E6AB3, version = 150, checkInsideInterrupt = true)
public int __sceSasGetPauseFlag(int sasCore) {
checkSasHandleGood(sasCore);
int pauseFlag = 0;
for (int i = 0; i < voices.length; i++) {
if (voices[i].isPaused()) {
pauseFlag |= (1 << i);
}
}
return pauseFlag;
}
/**
* Set the wave form effect type (unknown parameter).
*
* @param sasCore sasCore handle
* @param type unknown parameter
* @return wave form effect type
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
*/
@HLEFunction(nid = 0x33D4AB37, version = 150, checkInsideInterrupt = true)
public int __sceSasRevType(int sasCore, int type) {
checkSasHandleGood(sasCore);
waveformEffectType = type;
return 0;
}
/**
* Initialize a new sasCore handle.
*
* @param sasCore sasCore handle, must be a valid address
* (Uid will be written at this address).
* @param grain number of samples processed by one call to __sceSasCore
* @param maxVoices number of voices (maximum 32)
* @param outputMode (unknown) 0 stereo
* 1 multichannel
* @param sampleRate the default sample rate (number of samples per second)
* for all the voices
* @return 0
*/
@HLEFunction(nid = 0x42778A9F, version = 150)
public int __sceSasInit(@CanBeNull TPointer sasCore, int grain, int maxVoices, int outputMode, int sampleRate) {
checkSasAddressGood(sasCore.getAddress());
if (grain < 0x40 || grain > 0x800 || (grain & 0x1F) != 0) {
return SceKernelErrors.ERROR_SAS_INVALID_GRAIN;
}
if (sampleRate != 44100) {
return SceKernelErrors.ERROR_SAS_INVALID_SAMPLE_RATE;
}
if (maxVoices <= 0 || maxVoices > PSP_SAS_VOICES_MAX) {
return SceKernelErrors.ERROR_SAS_INVALID_MAX_VOICES;
}
if (outputMode != PSP_SAS_OUTPUTMODE_STEREO && outputMode != PSP_SAS_OUTPUTMODE_MONO) {
return SceKernelErrors.ERROR_SAS_INVALID_OUTPUT_MODE;
}
if (sasCoreUid != -1) {
// Only one Sas core can be active at a time.
// If a previous Uid was allocated, release it.
SceUidManager.releaseUid(sasCoreUid, sasCodeUidPurpose);
}
// Size of SasCore structure is 0xE20 bytes
sasCore.clear(0xE20);
sasCoreUid = SceUidManager.getNewUid(sasCodeUidPurpose);
sasCore.setValue32(0, sasCoreUid);
grainSamples = grain;
this.outputMode = outputMode;
for (int i = 0; i < voices.length; i++) {
voices[i].setSampleRate(sampleRate); // Set default sample rate
}
return 0;
}
/**
* Set the volume for one voice.
*
* @param sasCore sasCore handle
* @param voice voice number
* @param leftVolume Left channel volume, [0..0x1000].
* @param rightVolume Right channel volume, [0..0x1000].
* @param effectLeftVolume (unknown) Left effect channel volume, [0..0x1000].
* @param effectRightVolume (unknown) Right effect channel volume, [0..0x1000].
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
*/
@HLEFunction(nid = 0x440CA7D8, version = 150, checkInsideInterrupt = true)
public int __sceSasSetVolume(int sasCore, int voice, @CheckArgument("checkVolume") int leftVolume, @CheckArgument("checkVolume") int rightVolume, @CheckArgument("checkVolume") int effectLeftVolumne, @CheckArgument("checkVolume") int effectRightVolume) {
checkSasAndVoiceHandlesGood(sasCore, voice);
voices[voice].setLeftVolume(leftVolume << 3); // 0 - 0x8000
voices[voice].setRightVolume(rightVolume << 3); // 0 - 0x8000
return 0;
}
/**
* Process the voices and generate the next samples.
* Mix the resulting samples in an exiting buffer.
*
* @param sasCore sasCore handle
* @param sasInOut address for the input and output buffer.
* Samples are stored as 2 16-bit values
* (left then right channel samples)
* @param leftVolume Left channel volume, [0..0x1000].
* @param rightVolume Right channel volume, [0..0x1000].
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
*/
@HLEFunction(nid = 0x50A14DFC, version = 150, checkInsideInterrupt = true)
public int __sceSasCoreWithMix(int sasCore, int sasInOut, int leftVolume, int rightVolume) {
checkSasHandleGood(sasCore);
long startTime = Emulator.getClock().microTime();
mixer.synthesizeWithMix(sasInOut, grainSamples, leftVolume << 3, rightVolume << 3);
delayThreadSasCore(startTime);
return 0;
}
/**
* Set the sustain level for one voice.
*
* @param sasCore sasCore handle
* @param voice voice number [0..31]
* @param level sustain level [0..0x40000000]
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
*/
@HLEFunction(nid = 0x5F9529F6, version = 150, checkInsideInterrupt = true)
public int __sceSasSetSL(int sasCore, int voice, int level) {
checkSasAndVoiceHandlesGood(sasCore, voice);
voices[voice].getEnvelope().SustainLevel = level;
return 0;
}
/**
* Get the end flag for all the voices.
*
* @param sasCore sasCore handle
* @return bitfield with bit 0 for voice 0, bit 1 for voice 1...
* bit=0, corresponding voice is not ended
* bit=1, corresponding voice is ended
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
*/
@HLEFunction(nid = 0x68A46B95, version = 150)
public int __sceSasGetEndFlag(int sasCore) {
checkSasHandleGood(sasCore);
int endFlag = 0;
for (int i = 0; i < voices.length; i++) {
if (voices[i].isEnded()) {
endFlag |= (1 << i);
}
}
return endFlag;
}
/**
* Get the current envelope height for one voice.
*
* @param sasCore sasCore handle
* @param voice voice number [0..31]
* @return envelope height [0..0x40000000]
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
*/
@HLEFunction(nid = 0x74AE582A, version = 150, checkInsideInterrupt = true)
public int __sceSasGetEnvelopeHeight(int sasCore, int voice) {
checkSasAndVoiceHandlesGood(sasCore, voice);
return voices[voice].getEnvelope().height;
}
/**
* Set one voice on.
*
* @param sasCore sasCore handle
* @param voice voice number [0..31]
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
* ERROR_SAS_VOICE_PAUSED if the voice was paused or already on
*/
@HLEFunction(nid = 0x76F01ACA, version = 150, checkInsideInterrupt = true)
public int __sceSasSetKeyOn(int sasCore, int voice) {
checkSasAndVoiceHandlesGood(sasCore, voice);
checkVoiceNotPaused(voice, false);
voices[voice].on();
return 0;
}
/**
* Set or reset the pause parameter for the voices.
*
* @param sasCore sasCore handle
* @param voice_bit a bitfield with bit 0 for voice 0, bit 1 for voice 1...
* Only the bits with 1 are processed.
* @param setPause when 0: reset the pause flag for all the voices
* having a bit 1 in the voice_bit field
* when non-0: set the pause flag for all the voices
* having a bit 1 in the voice_bit field
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
*/
@HLEFunction(nid = 0x787D04D5, version = 150, checkInsideInterrupt = true)
public int __sceSasSetPause(int sasCore, int voice_bit, boolean setPause) {
checkSasHandleGood(sasCore);
// Update only the pause flag of the voices
// where the corresponding bit is set:
// set or reset the pause flag according to the "setPause" parameter.
for (int i = 0; voice_bit != 0; i++, voice_bit >>>= 1) {
if ((voice_bit & 1) != 0) {
voices[i].setPaused(setPause);
}
}
return 0;
}
/**
* Set the VAG waveform data for one voice.
*
* @param sasCore sasCore handle
* @param voice voice number [0..31]
* @param vagAddr address of the VAG waveform data
* @param size size in bytes of the VAG waveform data
* @param loopmode 0 ignore the VAG looping information
* 1 process the VAG looping information
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
* ERROR_SAS_INVALID_PARAMETER if an invalid size is provided
*/
@HLEFunction(nid = 0x99944089, version = 150, checkInsideInterrupt = true)
public int __sceSasSetVoice(int sasCore, int voice, int vagAddr, int size, int loopmode) {
if (size <= 0 || (size & 0xF) != 0) {
log.warn(String.format("__sceSasSetVoice invalid size 0x%08X", size));
return SceKernelErrors.ERROR_SAS_INVALID_ADPCM_SIZE;
}
checkSasAndVoiceHandlesGood(sasCore, voice);
voices[voice].setVAG(vagAddr, size);
voices[voice].setLoopMode(loopmode);
return 0;
}
/**
* Set the ADSR curve types for a specific voice.
*
* @param sasCore sasCore handle
* @param voice voice number, [0..31]
* @param flag Bitfield to indicate which of the following 4 parameters
* has to be updated. Logical OR of the following values:
* 0x1: update attack curve type
* 0x2: update decay curve type
* 0x4: update sustain curve type
* 0x8: update release curve type
* @param attack Envelope's attack curve type, [0..5].
* @param decay Envelope's decay curve type, [0..5].
* @param sustain Envelope's sustain curve type, [0..5].
* @param release Envelope's release curve type, [0..5].
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
* ERROR_SAS_INVALID_ADSR_CURVE_MODE if an invalid curve mode or curve mode combination is provided
*/
@HLEFunction(nid = 0x9EC3676A, version = 150, checkInsideInterrupt = true)
public int __sceSasSetADSRmode(int sasCore, int voice, int flag, int attackType, int decayType, int sustainType, int releaseType) {
checkSasAndVoiceHandlesGood(sasCore, voice);
checkADSRmode(0, flag, attackType);
checkADSRmode(1, flag, decayType);
checkADSRmode(2, flag, sustainType);
checkADSRmode(3, flag, releaseType);
VoiceADSREnvelope envelope = voices[voice].getEnvelope();
if ((flag & 0x1) != 0) envelope.AttackCurveType = attackType;
if ((flag & 0x2) != 0) envelope.DecayCurveType = decayType;
if ((flag & 0x4) != 0) envelope.SustainCurveType = sustainType;
if ((flag & 0x8) != 0) envelope.ReleaseCurveType = releaseType;
if (log.isDebugEnabled()) {
log.debug(String.format("__sceSasSetADSRmode voice=0x%X: %s", voice, envelope.toString()));
}
return 0;
}
/**
* Set one voice off.
*
* @param sasCore sasCore handle
* @param voice voice number [0..31]
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
* ERROR_SAS_VOICE_PAUSED if the voice was paused or already off
*/
@HLEFunction(nid = 0xA0CF2FA4, version = 150, checkInsideInterrupt = true)
public int __sceSasSetKeyOff(int sasCore, int voice) {
checkSasAndVoiceHandlesGood(sasCore, voice);
checkVoiceNotPaused(voice, true);
voices[voice].off();
return 0;
}
/**
* (Unknown) Set a triangular waveform for one voice.
*
* @param sasCore sasCore handle
* @param voice voice number [0..31]
* @param unknown unknown parameter
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
*/
@HLEUnimplemented
@HLEFunction(nid = 0xA232CBE6, version = 150, checkInsideInterrupt = true)
public int __sceSasSetTriangularWave(int sasCore, int voice, int unknown) {
checkSasAndVoiceHandlesGood(sasCore, voice);
return 0;
}
/**
* Process the voices and generate the next samples.
*
* @param sasCore sasCore handle
* @param sasOut address for the output buffer.
* Samples are stored as 2 16-bit values
* (left then right channel samples)
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
*/
@HLEFunction(nid = 0xA3589D81, version = 150, checkInsideInterrupt = true)
public int __sceSasCore(int sasCore, int sasOut) {
checkSasHandleGood(sasCore);
long startTime = Emulator.getClock().microTime();
mixer.synthesize(sasOut, grainSamples);
delayThreadSasCore(startTime);
return 0;
}
/**
* Set the pitch of one voice.
*
* @param sasCore sasCore handle
* @param voice voice number [0..31]
* @param pitch the pitch value, [1..0x4000]
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
*/
@HLEFunction(nid = 0xAD84D37F, version = 150, checkInsideInterrupt = true)
public int __sceSasSetPitch(int sasCore, int voice, int pitch) {
checkSasAndVoiceHandlesGood(sasCore, voice);
voices[voice].setPitch(pitch);
return 0;
}
/**
* (Unknown) Set a noise waveform for one voice.
*
* @param sasCore sasCore handle
* @param voice voice number [0..31]
* @param freq unknown parameter
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
*/
@HLEFunction(nid = 0xB7660A23, version = 150, checkInsideInterrupt = true)
public int __sceSasSetNoise(int sasCore, int voice, int freq) {
checkSasAndVoiceHandlesGood(sasCore, voice);
voices[voice].setNoise(freq);
return 0;
}
/**
* Get the number of samples generated by one __sceSasCore call.
*
* @param sasCore sasCore handle
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* @return
*/
@HLEFunction(nid = 0xBD11B7C2, version = 150, checkInsideInterrupt = true)
public int __sceSasGetGrain(int sasCore) {
checkSasHandleGood(sasCore);
return grainSamples;
}
private int getSimpleSustainLevel(int bitfield1) {
return ((bitfield1 & 0x000F) + 1) << 26;
}
private int getSimpleDecayRate(int bitfield1) {
return 0x80000000 >>> ((bitfield1 >> 4) & 0x000F);
}
private int getSimpleRate(int n) {
n &= 0x7F;
if (n == 0x7F) {
return 0;
}
int rate = ((7 - (n & 0x3)) << 26) >>> (n >> 2);
if (rate == 0) {
return 1;
}
return rate;
}
private int getSimpleAttackRate(int bitfield1) {
return getSimpleRate(bitfield1 >> 8);
}
private int getSimpleAttackCurveType(int bitfield1) {
return (bitfield1 & 0x8000) == 0 ? PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE : PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT;
}
private int getSimpleReleaseRate(int bitfield2) {
int n = bitfield2 & 0x001F;
if (n == 31) {
return 0;
}
if (getSimpleReleaseCurveType(bitfield2) == PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE) {
return (0x40000000 >>> (n + 2));
}
return (0x8000000 >>> n);
}
private int getSimpleReleaseCurveType(int bitfield2) {
return (bitfield2 & 0x0020) == 0 ? PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE : PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE;
}
private int getSimpleSustainRate(int bitfield2) {
return getSimpleRate(bitfield2 >> 6);
}
private int getSimpleSustainCurveType(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;
}
throw new SceKernelErrorException(ERROR_SAS_INVALID_ADSR_CURVE_MODE);
}
/**
* Set the ADSR parameters for a specific voice with simplified parameters.
* The Decay curve type is always exponential decrease.
*
* Simple Rate coding: bitfield [0..0x7F]
* 0x7F: rate = 0
* Bits [0..1]: 0x0: base rate=0x1C000000
* 0x1: base rate=0x18000000
* 0x2: base rate=0x14000000
* 0x3: base rate=0x10000000
* Bits [2..6]: number of bits to logically shift the base rate to the right
*
* @param sasCore sasCore handle
* @param voice voice number, [0..31]
* @param ADSREnv1 ADSR bitfield 1
* Bits [0..3]: Sustain Level, coded as the bits [29..26]-1
* of the sustain level
* Bits [4..7]: Decay Rate, coded as the number of bits to
* logically shift 0x80000000 to the right
* Bits [8..14]: Attack Rate, coded as a Simple Rate
* Bit [15]: Attack curve type
* (0=linear increase, 1=linear bent)
* @param ADSREnv2 ADSR bitfield 2
* Bits [0..4]: Release Rate
* 0x1F: release rate = 0
* [0..0x1E]: n
* if release curve type is linear decrease
* release rate = 0x40000000 >>> (n+2)
* else
* release rate = 0x80000000 >>> n
* Bit [5]: Release curve type
* (0=linear decrease, 1=exponential decrease)
* Bits [6..12]: Sustain Rate, coded as a Simple Rate
* Bits [13..15]: Sustain curve type
* (0=linear increase,
* 2=linear decrease,
* 4=linear bent,
* 6=exponential decrease,
* other values are invalid)
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
* ERROR_SAS_INVALID_ADSR_CURVE_MODE if an invalid sustain curve type is provided
*/
@HLEFunction(nid = 0xCBCD4F79, version = 150, checkInsideInterrupt = true)
public int __sceSasSetSimpleADSR(int sasCore, int voice, int ADSREnv1, int ADSREnv2) {
checkSasAndVoiceHandlesGood(sasCore, voice);
// Only the low-order 16 bits are valid for both parameters.
int env1Bitfield = (ADSREnv1 & 0xFFFF);
int env2Bitfield = (ADSREnv2 & 0xFFFF);
// The bitfields represent every value except for the decay curve shape,
// which seems to be unchanged in simple mode.
VoiceADSREnvelope envelope = voices[voice].getEnvelope();
envelope.SustainLevel = getSimpleSustainLevel(env1Bitfield);
envelope.DecayRate = getSimpleDecayRate(env1Bitfield);
envelope.DecayCurveType = PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE;
envelope.AttackRate = getSimpleAttackRate(env1Bitfield);
envelope.AttackCurveType = getSimpleAttackCurveType(env1Bitfield);
envelope.ReleaseRate = getSimpleReleaseRate(env2Bitfield);
envelope.ReleaseCurveType = getSimpleReleaseCurveType(env2Bitfield);
envelope.SustainRate = getSimpleSustainRate(env2Bitfield);
envelope.SustainCurveType = getSimpleSustainCurveType(env2Bitfield);
if (log.isDebugEnabled()) {
log.debug(String.format("__sceSasSetSimpleADSR voice=0x%X: %s", voice, envelope.toString()));
}
return 0;
}
/**
* Set the number of samples generated by one __sceSasCore call.
*
* @param sasCore sasCore handle
* @param grain number of samples
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
*/
@HLEFunction(nid = 0xD1E0A01E, version = 150, checkInsideInterrupt = true)
public int __sceSasSetGrain(int sasCore, int grain) {
checkSasHandleGood(sasCore);
grainSamples = grain;
return 0;
}
/**
* Set the wave form effect volume (unknown parameters).
*
* @param sasCore sasCore handle
* @param leftVolume unknown parameter
* @param rightVolume unknown parameter
* @return wave form effect type
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
*/
@HLEFunction(nid = 0xD5A229C9, version = 150, checkInsideInterrupt = true)
public int __sceSasRevEVOL(int sasCore, int leftVolume, int rightVolume) {
checkSasHandleGood(sasCore);
waveformEffectLeftVol = leftVolume;
waveformEffectRightVol = rightVolume;
return 0;
}
/**
* (Unknown) Set a steep waveform for one voice.
*
* @param sasCore sasCore handle
* @param voice voice number [0..31]
* @param unknown unknown parameter
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
* ERROR_SAS_INVALID_VOICE if an invalid voice number is provided
*/
@HLEUnimplemented
@HLEFunction(nid = 0xD5EBBBCD, version = 150, checkInsideInterrupt = true)
public int __sceSasSetSteepWave(int sasCore, int voice, int unknown) {
checkSasAndVoiceHandlesGood(sasCore, voice);
return 0;
}
/**
* (Unknown) Get the output mode.
*
* @param sasCore sasCore handle
* @return (unknown) 0 stereo
* 1 multichannel
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
*/
@HLEFunction(nid = 0xE175EF66, version = 150, checkInsideInterrupt = true)
public int __sceSasGetOutputmode(int sasCore) {
checkSasHandleGood(sasCore);
return getOutputMode();
}
/**
* (Unknown) Set the output mode.
*
* @param sasCore sasCore handle
* @param outputMode (unknown) 0 stereo
* 1 multichannel
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
*/
@HLEFunction(nid = 0xE855BF76, version = 150, checkInsideInterrupt = true)
public int __sceSasSetOutputmode(int sasCore, int outputMode) {
checkSasHandleGood(sasCore);
this.outputMode = outputMode;
return 0;
}
/**
* Set the wave form effect dry and wet status (unknown parameters).
*
* @param sasCore sasCore handle
* @param dry unknown parameter
* @param wet unknown parameter
* @return wave form effect type
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
*/
@HLEFunction(nid = 0xF983B186, version = 150, checkInsideInterrupt = true)
public int __sceSasRevVON(int sasCore, int dry, int wet) {
checkSasHandleGood(sasCore);
waveformEffectIsDryOn = (dry > 0);
waveformEffectIsWetOn = (wet > 0);
return 0;
}
/**
* Get the current envelope height for all the voices.
*
* @param sasCore sasCore handle
* @param heightsAddr (int *) address where to return the envelope heights,
* stored as 32 bit values [0..0x40000000].
* heightsAddr[0] = envelope height of voice 0
* heightsAddr[1] = envelope height of voice 1
* ...
* @return 0 if OK
* ERROR_SAS_NOT_INIT if an invalid sasCore handle is provided
*/
@HLEFunction(nid = 0x07F58C24, version = 150, checkInsideInterrupt = true)
public int __sceSasGetAllEnvelopeHeights(int sasCore, TPointer32 heightsAddr) {
checkSasHandleGood(sasCore);
IMemoryWriter memoryWriter = MemoryWriter.getMemoryWriter(heightsAddr.getAddress(), voices.length * 4, 4);
for (int i = 0; i < voices.length; i++) {
int voiceHeight = voices[i].getEnvelope().height;
memoryWriter.writeNext(voiceHeight);
if (log.isTraceEnabled() && voiceHeight != 0) {
log.trace(String.format("__sceSasGetAllEnvelopeHeights height voice #%d=0x%08X", i, voiceHeight));
}
}
memoryWriter.flush();
return 0;
}
/** Identical to __sceSasSetVoice, but for raw PCM data (VAG/ADPCM is not allowed). */
@HLEFunction(nid = 0xE1CD9561, version = 500, checkInsideInterrupt = true)
public int __sceSasSetVoicePCM(int sasCore, int voice, TPointer pcmAddr, int size, int loopmode) {
if (size <= 0 || size > 0x10000) {
log.warn(String.format("__sceSasSetVoicePCM invalid size 0x%08X", size));
return SceKernelErrors.ERROR_SAS_INVALID_SIZE;
}
checkSasAndVoiceHandlesGood(sasCore, voice);
voices[voice].setPCM(pcmAddr.getAddress(), size);
voices[voice].setLoopMode(loopmode);
return 0;
}
@HLELogging(level="info")
@HLEFunction(nid = 0x4AA9EAD6, version = 600, checkInsideInterrupt = true)
public int __sceSasSetVoiceATRAC3(int sasCore, int voice, int atrac3Context) {
// atrac3Context is the value returned by _sceAtracGetContextAddress
checkSasAndVoiceHandlesGood(sasCore, voice);
AtracID atracId = Modules.sceAtrac3plusModule.getAtracIdFromContext(atrac3Context);
if (atracId != null) {
voices[voice].setAtracId(atracId);
// Store the atrac3Context address into the sasCore structure.
setSasCoreAtrac3Context(sasCore, voice, atrac3Context);
}
return 0;
}
@HLEFunction(nid = 0x7497EA85, version = 600, checkInsideInterrupt = true)
public int __sceSasConcatenateATRAC3(int sasCore, int voice, @CanBeNull TPointer atrac3DataAddr, int atrac3DataLength) {
checkSasAndVoiceHandlesGood(sasCore, voice);
int atrac3Context = getSasCoreAtrac3Context(sasCore, voice);
AtracID atracID = Modules.sceAtrac3plusModule.getAtracIdFromContext(atrac3Context);
if (atracID.getSecondBufferAddr() != -1) {
return SceKernelErrors.ERROR_SAS_CANNOT_CONCATENATE_ATRA3;
}
atracID.setSecondBuffer(atrac3DataAddr.getAddress(), atrac3DataLength);
return 0;
}
@HLEFunction(nid = 0xF6107F00, version = 600, checkInsideInterrupt = true)
public int __sceSasUnsetATRAC3(int sasCore, int voice) {
checkSasAndVoiceHandlesGood(sasCore, voice);
voices[voice].setAtracId(null);
// Reset the atrac3Context address
setSasCoreAtrac3Context(sasCore, voice, 0);
return 0;
}
}