VitaShell/audioplayer.c
2016-09-03 22:14:07 +02:00

456 lines
13 KiB
C

/*
VitaShell
Copyright (C) 2015-2016
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, either version 3 of the License, or
(at your option) any later version.
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 for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "main.h"
#include "archive.h"
#include "photo.h"
#include "file.h"
#include "theme.h"
#include "utils.h"
#include "audioplayer.h"
#define lerp(value, from_max, to_max) ((((value*10) * (to_max*10))/(from_max*10))/10)
static SceUID sEventAudioHolder = -1;
static SceUID sThreadAudioHolder = -1;
static AudioOutConfig sAudioOutConfigHolder;
static bool sIsPausing = false;
static bool sIsRunning = false;
static unsigned int sCodecType;
static char *sInputFileName;
void setCodecType(unsigned int codecType) {
sCodecType = codecType;
}
void setInputFileName(char *file) {
sInputFileName = file;
}
void togglePause() {
sIsPausing = !sIsPausing;
}
bool isPausing() {
return sIsPausing;
}
void stopAudioThread() {
sIsRunning = false;
}
bool isRunning() {
return sIsRunning;
}
void resetAudioPlayer() {
sIsRunning = true;
sIsPausing = false;
}
int initAudioDecoderByType(SceAudiodecCtrl *pAudiodecCtrl, AudioFileStream *pInputStream, AudioOutConfig *pAudioOutConfig, int codecType)
{
int returnValue = 0;
pInputStream->offsetRead = 0;
pAudiodecCtrl->wordLength = SCE_AUDIODEC_WORD_LENGTH_16BITS;
pAudiodecCtrl->size = sizeof(SceAudiodecCtrl);
if (codecType == SCE_AUDIODEC_TYPE_MP3) {
int sampleRateMpeg[4][3] = {
{11025, 12000, 8000}, {0, 0, 0}, {22050, 24000, 16000}, {44100, 48000, 32000}
};
int audioChannelsMpeg[4] = {2, 2, 2, 1};
int sync = (pInputStream->pBuffer[0] & 0xFF) << 4 | (pInputStream->pBuffer[1] & 0xE0) >> 4;
if (sync != 0xFFE) {
return -1;
}
int version = (pInputStream->pBuffer[1] & 0x18) >> 3;
pAudiodecCtrl->pInfo->mp3.version = version;
int chMode = audioChannelsMpeg[(pInputStream->pBuffer[3] & 0xC0) >> 6];
pAudiodecCtrl->pInfo->mp3.ch = chMode;
pAudiodecCtrl->pInfo->size = sizeof(pAudiodecCtrl->pInfo->mp3);
returnValue = sceAudiodecCreateDecoder(pAudiodecCtrl, SCE_AUDIODEC_TYPE_MP3);
if (returnValue >= 0 && pAudioOutConfig != NULL) {
int indexBitRate = sampleRateMpeg[(pInputStream->pBuffer[1] & 0x18) >> 3][(pInputStream->pBuffer[2] & 0x06) >> 2];
pAudioOutConfig->samplingRate = indexBitRate;
pAudioOutConfig->ch = pAudiodecCtrl->pInfo->mp3.ch;
pAudioOutConfig->param = SCE_AUDIO_OUT_PARAM_FORMAT_S16_STEREO;
pAudioOutConfig->grain = pAudiodecCtrl->maxPcmSize / pAudiodecCtrl->pInfo->mp3.ch / sizeof(int16_t);
}
} else if (codecType == SCE_AUDIODEC_TYPE_AT9) {
//TODO
} else if (codecType == SCE_AUDIODEC_TYPE_AAC) {
//TODO
} else {
return -1;
}
return returnValue;
}
int decodeCurrentAudio(SceAudiodecCtrl *pAudiodecCtrl, AudioFileStream *pInputStream, AudioFileStream *pOutputBuffer)
{
int returnValue = 0;
pAudiodecCtrl->pPcm = pOutputBuffer->pBuffer + pOutputBuffer->offsetWrite;
pAudiodecCtrl->pEs = pInputStream->pBuffer + pInputStream->offsetRead;
returnValue = sceAudiodecDecode(pAudiodecCtrl);
if (returnValue >= 0) {
pInputStream->offsetRead += pAudiodecCtrl->inputEsSize;
pOutputBuffer->offsetWrite = (pOutputBuffer->offsetWrite + pOutputBuffer->sizeBuffer / 2) % pOutputBuffer->sizeBuffer;
}
return returnValue;
}
int initAudioOut(AudioOutConfig *pAudioOutConfig) {
int returnValue = 0;
if (pAudioOutConfig->portId < 0) {
returnValue = sceAudioOutOpenPort(pAudioOutConfig->portType, pAudioOutConfig->grain, pAudioOutConfig->samplingRate, pAudioOutConfig->param);
if (returnValue >= 0) {
pAudioOutConfig->portId = returnValue;
returnValue = sceAudioOutSetVolume(pAudioOutConfig->portId, (SCE_AUDIO_VOLUME_FLAG_L_CH | SCE_AUDIO_VOLUME_FLAG_R_CH), pAudioOutConfig->volume);
}
}
return returnValue;
}
int outputCurrentAudio(AudioOutConfig *pAudioOutConfig, AudioFileStream *pOutputStream) {
int returnValue = 0;
if (pOutputStream != NULL) {
returnValue = sceAudioOutOutput(pAudioOutConfig->portId, pOutputStream->pBuffer + pOutputStream->offsetRead);
if (returnValue >= 0) {
pOutputStream->offsetRead = (pOutputStream->offsetRead + pOutputStream->sizeBuffer / 2) % pOutputStream->sizeBuffer;
}
} else {
returnValue = sceAudioOutOutput(pAudioOutConfig->portId, NULL);
}
return returnValue;
}
int audioOutThread(int argSize, void *pArgBlock) {
int returnValue = 0;
int pcmSize = 0;
AudioOutConfig *pAudioOutConfig;
pAudioOutConfig = &sAudioOutConfigHolder;
AudioFileStream input;
input.pNameFile = sInputFileName;
input.sizeFile = 0;
input.pBuffer = NULL;
input.offsetRead = 0;
input.offsetWrite = 0;
AudioFileStream output;
output.sizeFile = 0;
output.pBuffer = NULL;
output.offsetRead = 0;
output.offsetWrite = 0;
SceAudiodecInfo audiodecInfo;
SceAudiodecCtrl audiodecCtrl;
memset(&audiodecInfo, 0, sizeof(SceAudiodecInfo));
memset(&audiodecCtrl, 0, sizeof(SceAudiodecCtrl));
audiodecCtrl.pInfo = &audiodecInfo;
returnValue = getFileSize(input.pNameFile);
if (returnValue > 0) {
input.sizeFile = returnValue;
int maxEsSize = 0;
if (sCodecType == SCE_AUDIODEC_TYPE_MP3) {
maxEsSize = SCE_AUDIODEC_MP3_MAX_ES_SIZE;
} else if (sCodecType == SCE_AUDIODEC_TYPE_AT9) {
maxEsSize = SCE_AUDIODEC_AT9_MAX_ES_SIZE;
} else if (sCodecType == SCE_AUDIODEC_TYPE_AAC) {
maxEsSize = SCE_AUDIODEC_AAC_MAX_ES_SIZE;
}
if(maxEsSize > 0) {
input.sizeBuffer = SCE_AUDIODEC_ROUND_UP(input.sizeFile + maxEsSize);
input.pBuffer = memalign(SCE_AUDIODEC_ALIGNMENT_SIZE, input.sizeBuffer);
returnValue = ReadFile(input.pNameFile, input.pBuffer, input.sizeFile);
if (returnValue >= 0) {
input.offsetWrite = input.sizeFile;
returnValue = initAudioDecoderByType(&audiodecCtrl, &input, pAudioOutConfig, sCodecType);
if (returnValue >= 0) {
pcmSize = sizeof(int16_t) * pAudioOutConfig->ch * pAudioOutConfig->grain;
output.sizeBuffer = SCE_AUDIODEC_ROUND_UP(pcmSize) * 2;
output.pBuffer = memalign(SCE_AUDIODEC_ALIGNMENT_SIZE, output.sizeBuffer);
returnValue = initAudioOut(pAudioOutConfig);
if (returnValue >= 0) {
sceKernelSetEventFlag(sEventAudioHolder, 0x00000001U);
//Playing Track :)
while (isRunning()) {
if(isPausing()) {
sceKernelDelayThread(1000);
} else {
if (input.sizeFile <= input.offsetRead) {
break;
}
returnValue = decodeCurrentAudio(&audiodecCtrl, &input, &output);
if (returnValue < 0) {
break;
}
returnValue = outputCurrentAudio(pAudioOutConfig, &output);
if (returnValue < 0) {
break;
}
}
}
}
}
}
}
}
outputCurrentAudio(pAudioOutConfig, NULL);
if (pAudioOutConfig->portId >= 0) {
returnValue = sceAudioOutReleasePort(pAudioOutConfig->portId);
pAudioOutConfig->portId = -1;
}
returnValue = sceAudiodecDeleteDecoder(&audiodecCtrl);
if (output.pBuffer != NULL) {
free(output.pBuffer);
output.pBuffer = NULL;
}
if (input.pBuffer != NULL) {
free(input.pBuffer);
input.pBuffer = NULL;
}
return returnValue;
}
int shutdownAudioPlayer() {
int returnValue = 0;
if (sThreadAudioHolder >= 0) {
returnValue = sceKernelWaitThreadEnd(sThreadAudioHolder, NULL, NULL);
returnValue = sceKernelDeleteThread(sThreadAudioHolder);
sThreadAudioHolder = -1;
}
if (sEventAudioHolder >= 0) {
returnValue = sceKernelDeleteEventFlag(sEventAudioHolder);
sEventAudioHolder = -1;
}
returnValue = sceAudiodecTermLibrary(sCodecType);
return returnValue;
}
int startAudioPlayer()
{
int returnValue = 0;
AudioOutConfig *pAudioOutConfig = &sAudioOutConfigHolder;
pAudioOutConfig->portId = -1;
pAudioOutConfig->portType = SCE_AUDIO_OUT_PORT_TYPE_MAIN;
pAudioOutConfig->samplingRate = 0;
pAudioOutConfig->volume[0] = pAudioOutConfig->volume[1] = SCE_AUDIO_VOLUME_0DB / 4;
pAudioOutConfig->ch = 0;
pAudioOutConfig->param = 0;
pAudioOutConfig->grain = 0;
SceAudiodecInitParam audiodecInitParam;
memset(&audiodecInitParam, 0, sizeof(audiodecInitParam));
if(sCodecType == SCE_AUDIODEC_TYPE_AT9) {
//TODO
} else if (sCodecType == SCE_AUDIODEC_TYPE_MP3) {
audiodecInitParam.size = sizeof(audiodecInitParam.mp3);
audiodecInitParam.mp3.totalStreams = 1;
} else if (sCodecType == SCE_AUDIODEC_TYPE_AAC) {
//TODO
} else {
returnValue = -1;
}
if (returnValue >= 0) {
returnValue = sceAudiodecInitLibrary(sCodecType, &audiodecInitParam);
if (returnValue >= 0) {
returnValue = sceKernelCreateEventFlag("AudioOutThread", 0x00001000, 0, NULL);
if (returnValue >= 0) {
sEventAudioHolder = returnValue;
returnValue = sceKernelCreateThread("AudioOutThread", (void*)&audioOutThread, 64, 0x1000U, 0, 0, NULL);
if (returnValue >= 0) {
sThreadAudioHolder = returnValue;
returnValue = sceKernelStartThread(sThreadAudioHolder, 0, NULL);
if (returnValue >= 0) {
sceKernelWaitEventFlag(sEventAudioHolder, 0x00000001U, 0, NULL, NULL);
}
}
}
}
}
if (returnValue < 0) {
shutdownAudioPlayer();
}
return returnValue;
}
int audioPlayer(char *file, FileList *list, FileListEntry *entry, int *base_pos, int *rel_pos, unsigned int codec_type, int showInterface) {
int returnCode = 0;
bool isTouched = false;
SceTouchData touch;
if(isRunning()) {
stopAudioThread();
returnCode = shutdownAudioPlayer();
}
resetAudioPlayer();
setCodecType(codec_type);
setInputFileName(file);
returnCode = startAudioPlayer();
if(showInterface == 1) {
// Main loop
while (isRunning()) {
readPad();
sceTouchPeek(0, &touch, 1);
int checkPrevNexFlag = -1;
if (touch.reportNum > 0 && !isTouched) {
int x = lerp(touch.report[0].x, 1920, SCREEN_WIDTH);
int y = lerp(touch.report[0].y, 1088, SCREEN_HEIGHT);
if(x >= SCREEN_WIDTH / 2 - 200 && x <= SCREEN_WIDTH / 2 - 80 && y >= SCREEN_HEIGHT - 170 && y <= SCREEN_HEIGHT - 50) {
checkPrevNexFlag = 1;
} else if(x >= SCREEN_WIDTH / 2 - 60 && x <= SCREEN_WIDTH / 2 + 60 && y >= SCREEN_HEIGHT - 170 && y <= SCREEN_HEIGHT - 50) {
checkPrevNexFlag = 0;
} else if(x >= SCREEN_WIDTH / 2 + 80 && x <= SCREEN_WIDTH / 2 + 200 && y >= SCREEN_HEIGHT - 170 && y <= SCREEN_HEIGHT - 50) {
togglePause();
}
isTouched = true;
} else if (touch.reportNum <= 0) {
isTouched = false;
}
// Pause or resume if START button pressed
if (pressed_buttons & SCE_CTRL_START) {
togglePause();
}
// Exit if SELECT button pressed
if (pressed_buttons & SCE_CTRL_CANCEL) {
stopAudioThread();
}
// Next if R Trigger pressed
if (pressed_buttons & SCE_CTRL_RTRIGGER) {
checkPrevNexFlag = 0;
}
// Previous if L Trigger pressed
if (pressed_buttons & SCE_CTRL_LTRIGGER) {
checkPrevNexFlag = 1;
}
if(checkPrevNexFlag >= 0) {
int available = 0;
int old_base_pos = *base_pos;
int old_rel_pos = *rel_pos;
FileListEntry *old_entry = entry;
while (checkPrevNexFlag ? entry->previous : entry->next) {
entry = checkPrevNexFlag ? entry->previous : entry->next;
if (checkPrevNexFlag) {
if (*rel_pos > 0) {
(*rel_pos)--;
} else {
if (*base_pos > 0) {
(*base_pos)--;
}
}
} else {
if ((*rel_pos + 1) < list->length) {
if ((*rel_pos + 1) < MAX_POSITION) {
(*rel_pos)++;
} else {
if ((*base_pos + *rel_pos + 1) < list->length) {
(*base_pos)++;
}
}
}
}
if (!entry->is_folder) {
char path[MAX_PATH_LENGTH];
snprintf(path, MAX_PATH_LENGTH, "%s%s", list->path, entry->name);
int type = getFileType(path);
if (type == FILE_TYPE_MP3) {
stopAudioThread();
int returnCode = shutdownAudioPlayer();
resetAudioPlayer();
setCodecType(SCE_AUDIODEC_TYPE_MP3);
setInputFileName(path);
startAudioPlayer();
available = 1;
break;
}
}
}
if (!available) {
*base_pos = old_base_pos;
*rel_pos = old_rel_pos;
entry = old_entry;
}
}
// Start drawing
startDrawing(NULL);
int nameLength = strlen(entry->name);
vita2d_pgf_draw_text(font, SCREEN_WIDTH / 2 - nameLength * 5.5, SCREEN_HEIGHT - 190, GENERAL_COLOR, 1.0f, entry->name);
vita2d_draw_texture(headphone_image, SCREEN_WIDTH / 2 - 150, 20);
vita2d_draw_texture(audio_previous_image, SCREEN_WIDTH / 2 - 200, SCREEN_HEIGHT - 170);
vita2d_draw_texture(audio_next_image, SCREEN_WIDTH / 2 - 60, SCREEN_HEIGHT - 170);
if(isPausing()) {
vita2d_draw_texture(audio_play_image, SCREEN_WIDTH / 2 + 80, SCREEN_HEIGHT - 170);
} else {
vita2d_draw_texture(audio_pause_image, SCREEN_WIDTH / 2 + 80, SCREEN_HEIGHT - 170);
}
// End drawing
endDrawing();
sceKernelDelayThread(1000);
}
returnCode = shutdownAudioPlayer();
resetAudioPlayer();
}
return returnCode;
}