/*
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 jpcsp.HLE.BufferInfo;
import jpcsp.HLE.BufferInfo.LengthInfo;
import jpcsp.HLE.BufferInfo.Usage;
import jpcsp.HLE.CheckArgument;
import jpcsp.HLE.HLEFunction;
import jpcsp.HLE.HLEModule;
import jpcsp.HLE.HLEUnimplemented;
import jpcsp.HLE.SceKernelErrorException;
import jpcsp.HLE.TPointer;
import jpcsp.HLE.Modules;
import jpcsp.HLE.kernel.types.SceKernelErrors;
import jpcsp.sound.SoundChannel;
import org.apache.log4j.Logger;
public class sceVaudio extends HLEModule {
public static Logger log = Modules.getLogger("sceVaudio");
@Override
public void start() {
SoundChannel.init();
// The PSP is using the same channel as the SRC channel(s)
pspVaudio1Channel = Modules.sceAudioModule.pspSRC1Channel;
pspVaudio2Channel = Modules.sceAudioModule.pspSRC2Channel;
pspVaudioChannelReserved = false;
super.start();
}
protected static final int PSP_VAUDIO_VOLUME_BASE = 0x8000;
protected static final int PSP_VAUDIO_SAMPLE_MIN = 256;
protected static final int PSP_VAUDIO_SAMPLE_MAX = 2048;
protected static final int PSP_VAUDIO_FORMAT_MONO = 0x0;
protected static final int PSP_VAUDIO_FORMAT_STEREO = 0x2;
protected static final int PSP_VAUDIO_EFFECT_TYPE_NONE = 0;
protected static final int PSP_VAUDIO_EFFECT_TYPE_1 = 1;
protected static final int PSP_VAUDIO_EFFECT_TYPE_2 = 2;
protected static final int PSP_VAUDIO_EFFECT_TYPE_3 = 3;
protected static final int PSP_VAUDIO_EFFECT_TYPE_4 = 4;
protected static final int PSP_VAUDIO_ALC_MODE_NONE = 0;
protected static final int PSP_VAUDIO_ALC_MODE_1 = 1;
protected SoundChannel pspVaudio1Channel;
protected SoundChannel pspVaudio2Channel;
protected boolean pspVaudioChannelReserved;
public int checkSampleCount(int sampleCount) {
if (sampleCount < 256 || sampleCount > 2048) {
if (log.isDebugEnabled()) {
log.debug(String.format("Invalid sampleCount 0x%X", sampleCount));
}
throw new SceKernelErrorException(SceKernelErrors.ERROR_INVALID_SIZE);
}
return sampleCount;
}
public int checkFrequency(int frequency) {
switch (frequency) {
case 0:
case 8000:
case 11025:
case 12000:
case 16000:
case 22050:
case 24000:
case 32000:
case 44100:
case 48000:
// OK
break;
default:
// PSP is yielding in this error code
Modules.ThreadManForUserModule.hleYieldCurrentThread();
throw new SceKernelErrorException(SceKernelErrors.ERROR_AUDIO_INVALID_FREQUENCY);
}
return frequency;
}
public int checkChannelCount(int channelCount) {
if (channelCount != 2 && channelCount != 4) {
throw new SceKernelErrorException(SceKernelErrors.ERROR_INVALID_FORMAT);
}
return channelCount;
}
protected int doAudioOutput(SoundChannel channel, int pvoid_buf) {
return sceAudio.doAudioOutput(channel, pvoid_buf);
}
protected void blockThreadOutput(SoundChannel channel, int addr, int leftVolume, int rightVolume) {
sceAudio.blockThreadOutput(channel, addr, leftVolume, rightVolume);
}
protected int changeChannelVolume(SoundChannel channel, int leftvol, int rightvol) {
return sceAudio.changeChannelVolume(channel, leftvol, rightvol);
}
protected int hleVaudioChReserve(int sampleCount, int freq, int format, boolean buffering) {
// Returning a different error code if the channel has been reserved by sceVaudioChReserve or by sceAudioSRCChReserve
if (pspVaudioChannelReserved) {
return SceKernelErrors.ERROR_BUSY;
}
if (pspVaudio1Channel.isReserved()) {
// PSP is yielding in this error case
Modules.ThreadManForUserModule.hleYieldCurrentThread();
return SceKernelErrors.ERROR_AUDIO_CHANNEL_ALREADY_RESERVED;
}
pspVaudioChannelReserved = true;
pspVaudio1Channel.setReserved(true);
pspVaudio1Channel.setSampleLength(sampleCount);
pspVaudio1Channel.setSampleRate(freq);
pspVaudio1Channel.setFormat(format == PSP_VAUDIO_FORMAT_MONO ? sceAudio.PSP_AUDIO_FORMAT_MONO : sceAudio.PSP_AUDIO_FORMAT_STEREO);
pspVaudio2Channel.setReserved(true);
pspVaudio2Channel.setSampleLength(sampleCount);
pspVaudio2Channel.setSampleRate(freq);
pspVaudio2Channel.setFormat(format == PSP_VAUDIO_FORMAT_MONO ? sceAudio.PSP_AUDIO_FORMAT_MONO : sceAudio.PSP_AUDIO_FORMAT_STEREO);
Modules.ThreadManForUserModule.hleYieldCurrentThread();
return 0;
}
@HLEFunction(nid = 0x67585DFD, version = 150, checkInsideInterrupt = true)
public int sceVaudioChRelease() {
if (!pspVaudio1Channel.isReserved()) {
return SceKernelErrors.ERROR_AUDIO_CHANNEL_NOT_RESERVED;
}
pspVaudioChannelReserved = false;
pspVaudio1Channel.release();
pspVaudio1Channel.setReserved(false);
pspVaudio2Channel.release();
pspVaudio2Channel.setReserved(false);
return 0;
}
@HLEFunction(nid = 0x03B6807D, version = 150, checkInsideInterrupt = true)
public int sceVaudioChReserve(@CheckArgument("checkSampleCount") int sampleCount, @CheckArgument("checkFrequency") int freq, @CheckArgument("checkChannelCount") int format) {
return hleVaudioChReserve(sampleCount, freq, format, false);
}
@HLEFunction(nid = 0x8986295E, version = 150, checkInsideInterrupt = true)
public int sceVaudioOutputBlocking(int vol, @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=32, usage=Usage.in) TPointer buf) {
int result = 0;
SoundChannel pspVaudioChannel = Modules.sceAudioModule.getFreeSRCChannel();
if (!pspVaudioChannel.isOutputBlocking()) {
if (log.isDebugEnabled()) {
log.debug(String.format("sceVaudioOutputBlocking[not blocking] %s", pspVaudioChannel));
}
if ((vol & PSP_VAUDIO_VOLUME_BASE) != PSP_VAUDIO_VOLUME_BASE) {
changeChannelVolume(pspVaudioChannel, vol, vol);
}
result = doAudioOutput(pspVaudioChannel, buf.getAddress());
if (log.isDebugEnabled()) {
log.debug(String.format("sceVaudioOutputBlocking[not blocking] returning %d (%s)", result, pspVaudioChannel));
}
Modules.ThreadManForUserModule.hleRescheduleCurrentThread();
} else {
if (log.isDebugEnabled()) {
log.debug(String.format("sceVaudioOutputBlocking[blocking] %s", pspVaudioChannel));
}
blockThreadOutput(pspVaudioChannel, buf.getAddress(), vol, vol);
}
return result;
}
@HLEUnimplemented
@HLEFunction(nid = 0x346FBE94, version = 150, checkInsideInterrupt = true)
public int sceVaudioSetEffectType(int type, int vol) {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xCBD4AC51, version = 150, checkInsideInterrupt = true)
public int sceVaudioSetAlcMode(int alcMode) {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xE8E78DC8, version = 150)
public int sceVaudio_E8E78DC8(@CheckArgument("checkSampleCount") int sampleCount, @CheckArgument("checkFrequency") int freq, @CheckArgument("checkChannelCount") int format) {
// What is the difference with sceVaudioChReserveBuffering?
return hleVaudioChReserve(sampleCount, freq, format, true);
}
@HLEUnimplemented
@HLEFunction(nid = 0x504E4745, version = 150)
public int sceVaudio_504E4745(int unknown) {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x27ACC20B, version = 150)
public int sceVaudioChReserveBuffering(@CheckArgument("checkSampleCount") int sampleCount, @CheckArgument("checkFrequency") int freq, @CheckArgument("checkChannelCount") int format) {
return hleVaudioChReserve(sampleCount, freq, format, true);
}
}