mirror of
https://github.com/joel16/VitaShell.git
synced 2024-11-26 21:30:45 +00:00
480 lines
13 KiB
C
480 lines
13 KiB
C
/*
|
|
VitaShell
|
|
Copyright (C) 2015-2018, TheFloW
|
|
|
|
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 "audioplayer.h"
|
|
#include "file.h"
|
|
#include "theme.h"
|
|
#include "language.h"
|
|
#include "utils.h"
|
|
#include "audio/lrcparse.h"
|
|
|
|
#include "audio/player.h"
|
|
|
|
static struct fileInfo *fileinfo = NULL;
|
|
static vita2d_texture *tex = NULL;
|
|
|
|
|
|
/**
|
|
* Calculate the x-axis position if draw text in center
|
|
* @param[in] sx start x-axis
|
|
* @param[in] ex end x-axis
|
|
* @param[in] text
|
|
* @return x-axis position
|
|
*/
|
|
float getCenteroffset(float sx,float ex,char* string){
|
|
if(!string||(string[0] == '\0'))
|
|
return sx;
|
|
|
|
float drawWidthSpace = ex - sx;
|
|
uint16_t stringWidth = pgf_text_width(string);
|
|
return stringWidth > drawWidthSpace ? sx : sx + (drawWidthSpace - stringWidth) / 2 ;
|
|
}
|
|
|
|
/**
|
|
* Try to load lrc file from audio path
|
|
* @param[in] path audio path
|
|
* @param[out] totalms set 0
|
|
* @param[out] lyricsIndex set 0
|
|
* @return Lyrics pointer , NULL is fail
|
|
*/
|
|
Lyrics* loadLyricsFile(const char *path, uint64_t *totalms, uint32_t *lyricsIndex){
|
|
size_t pathlength = strlen(path);
|
|
*totalms = *lyricsIndex = 0;
|
|
|
|
while(pathlength > 0){
|
|
if(path[pathlength] == '.'){
|
|
break;
|
|
}
|
|
pathlength--;
|
|
}
|
|
|
|
if(pathlength < 0)
|
|
return NULL;
|
|
|
|
char lrcPath[pathlength + 5 * sizeof(char)];
|
|
memccpy(lrcPath,path,sizeof(char),pathlength);//copy path string except filename extension
|
|
strcpy(lrcPath+pathlength,".lrc");
|
|
|
|
return lrcParseLoadWithFile(lrcPath);
|
|
}
|
|
|
|
/**
|
|
* Draw the lyrics from the designated area
|
|
* @param[in] lyrics Lyrics pointer
|
|
* @param[in] cur_time_string Playing time string
|
|
* @param[out] totalms Playing time (millisecond)
|
|
* @param[out] lyricsIndex Index of lyrics
|
|
* @param[in] lrcSpaceX Designated area starting point x
|
|
* @param[in] lrcSpaceX Designated area starting point y
|
|
*/
|
|
void drawLyrics(Lyrics* lyrics, const char *cur_time_string, uint64_t* totalms, uint32_t* lyricsIndex, float lrcSpaceX, float lrcSpaceY){
|
|
if(!lyrics)
|
|
return;
|
|
|
|
char hourString[3];
|
|
char minuteString[3];
|
|
char secondString[3];
|
|
|
|
strncpy(hourString,cur_time_string,sizeof(hourString)-1);
|
|
strncpy(minuteString,cur_time_string + 3,sizeof(minuteString)-1);
|
|
strncpy(secondString,cur_time_string + 6,sizeof(secondString)-1);
|
|
|
|
*totalms = (((atoi(hourString) * 60) + atoi(minuteString)) * 60 + atoi(secondString)) * 1000;
|
|
|
|
uint32_t m_index = *lyricsIndex >= 1 ? (*lyricsIndex - 1) : *lyricsIndex;
|
|
float right_max_x = SCREEN_WIDTH - SHELL_MARGIN_X;
|
|
//draw current lyrics
|
|
pgf_draw_textf(getCenteroffset(lrcSpaceX,right_max_x,lyrics->lrclines[m_index].word),lrcSpaceY, AUDIO_INFO_ASSIGN, "%s",lyrics->lrclines[m_index].word);
|
|
|
|
int i;
|
|
for (i = 1;i < 7; i++){//draw 6 line lyrics for preview
|
|
int n_index = m_index + i;
|
|
if(n_index + 1 > lyrics->lyricscount)
|
|
break;
|
|
pgf_draw_textf(getCenteroffset(lrcSpaceX,right_max_x,lyrics->lrclines[n_index].word),lrcSpaceY + FONT_Y_SPACE * i, AUDIO_INFO, "%s",lyrics->lrclines[n_index].word);
|
|
}
|
|
|
|
if (*totalms >= (lyrics->lrclines[*lyricsIndex].totalms) ){
|
|
*lyricsIndex = *lyricsIndex + 1;
|
|
} else if (( *lyricsIndex >= 1 ) & ( *totalms < (lyrics->lrclines[*lyricsIndex - 1].totalms ))) {
|
|
*lyricsIndex = *lyricsIndex - 1;
|
|
}
|
|
}
|
|
|
|
void shortenString(char *out, const char *in, int width) {
|
|
strcpy(out, in);
|
|
|
|
int i;
|
|
for (i = strlen(out)-1; i > 0; i--) {
|
|
if (pgf_text_width(out) < width)
|
|
break;
|
|
|
|
out[i] = '\0';
|
|
}
|
|
}
|
|
|
|
vita2d_texture *getAlternativeCoverImage(const char *file) {
|
|
char path[MAX_PATH_LENGTH];
|
|
|
|
char *p = strrchr(file, '/');
|
|
if (p) {
|
|
*p = '\0';
|
|
|
|
snprintf(path, MAX_PATH_LENGTH, "%s/cover.jpg", file);
|
|
if (checkFileExist(path)) {
|
|
*p = '/';
|
|
return vita2d_load_JPEG_file(path);
|
|
}
|
|
|
|
snprintf(path, MAX_PATH_LENGTH, "%s/folder.jpg", file);
|
|
if (checkFileExist(path)) {
|
|
*p = '/';
|
|
return vita2d_load_JPEG_file(path);
|
|
}
|
|
|
|
*p = '/';
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void getAudioInfo(const char *file) {
|
|
char *buffer = NULL;
|
|
|
|
fileinfo = getInfoFunct();
|
|
|
|
if (tex) {
|
|
vita2d_wait_rendering_done();
|
|
vita2d_free_texture(tex);
|
|
tex = NULL;
|
|
}
|
|
|
|
switch (fileinfo->encapsulatedPictureType) {
|
|
case JPEG_IMAGE:
|
|
case PNG_IMAGE:
|
|
{
|
|
SceUID fd = sceIoOpen(file, SCE_O_RDONLY, 0);
|
|
if (fd >= 0) {
|
|
char *buffer = malloc(fileinfo->encapsulatedPictureLength);
|
|
if (buffer) {
|
|
sceIoLseek32(fd, fileinfo->encapsulatedPictureOffset, SCE_SEEK_SET);
|
|
sceIoRead(fd, buffer, fileinfo->encapsulatedPictureLength);
|
|
sceIoClose(fd);
|
|
|
|
if (fileinfo->encapsulatedPictureType == JPEG_IMAGE)
|
|
tex = vita2d_load_JPEG_buffer(buffer, fileinfo->encapsulatedPictureLength);
|
|
|
|
if (fileinfo->encapsulatedPictureType == PNG_IMAGE)
|
|
tex = vita2d_load_PNG_buffer(buffer);
|
|
|
|
if (tex)
|
|
vita2d_texture_set_filters(tex, SCE_GXM_TEXTURE_FILTER_LINEAR, SCE_GXM_TEXTURE_FILTER_LINEAR);
|
|
|
|
free(buffer);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!tex)
|
|
tex = getAlternativeCoverImage(file);
|
|
}
|
|
|
|
int audioPlayer(const char *file, int type, FileList *list, FileListEntry *entry, int *base_pos, int *rel_pos) {
|
|
static int speed_list[] = { -7, -3, -1, 0, 1, 3, 7 };
|
|
#define N_SPEED (sizeof(speed_list) / sizeof(int))
|
|
|
|
sceAppMgrAcquireBgmPort();
|
|
|
|
powerLock();
|
|
|
|
setAudioFunctions(type);
|
|
|
|
initFunct(0);
|
|
loadFunct((char *)file);
|
|
playFunct();
|
|
|
|
getAudioInfo(file);
|
|
|
|
uint64_t totalms = 0;
|
|
uint32_t lyricsIndex = 0;
|
|
Lyrics* lyrics = loadLyricsFile(file,&totalms,&lyricsIndex);
|
|
|
|
int scroll_count = 0;
|
|
float scroll_x = 0.0f;
|
|
|
|
while (1) {
|
|
char cur_time_string[12];
|
|
getTimeStringFunct(cur_time_string);
|
|
|
|
readPad();
|
|
|
|
// Cancel
|
|
if (pressed_pad[PAD_CANCEL]) {
|
|
break;
|
|
}
|
|
|
|
// Display off
|
|
if (pressed_pad[PAD_TRIANGLE]) {
|
|
scePowerRequestDisplayOff();
|
|
}
|
|
|
|
// Toggle play/pause
|
|
if (pressed_pad[PAD_ENTER]) {
|
|
if (isPlayingFunct() && getPlayingSpeedFunct() == 0) {
|
|
pauseFunct();
|
|
} else {
|
|
setPlayingSpeedFunct(0);
|
|
playFunct();
|
|
}
|
|
}
|
|
|
|
if (pressed_pad[PAD_LEFT] || pressed_pad[PAD_RIGHT]) {
|
|
int speed = getPlayingSpeedFunct();
|
|
|
|
if (pressed_pad[PAD_LEFT]) {
|
|
int i;
|
|
for (i = 0; i < N_SPEED; i++) {
|
|
if (speed_list[i] == speed) {
|
|
if (i > 0)
|
|
speed = speed_list[i-1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pressed_pad[PAD_RIGHT]) {
|
|
int i;
|
|
for (i = 0; i < N_SPEED; i++) {
|
|
if (speed_list[i] == speed) {
|
|
if (i < N_SPEED - 1)
|
|
speed = speed_list[i + 1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
setPlayingSpeedFunct(speed);
|
|
|
|
playFunct();
|
|
}
|
|
|
|
// Previous/next song.
|
|
if (getPercentageFunct() == 100.0f || endOfStreamFunct() ||
|
|
pressed_pad[PAD_LTRIGGER] || pressed_pad[PAD_RTRIGGER]) {
|
|
int previous = pressed_pad[PAD_LTRIGGER];
|
|
if (previous && strcmp(cur_time_string, "00:00:00") != 0) {
|
|
lrcParseClose(lyrics);
|
|
endFunct();
|
|
initFunct(0);
|
|
loadFunct((char *)file);
|
|
playFunct();
|
|
|
|
getAudioInfo(file);
|
|
|
|
lyrics = loadLyricsFile(file,&totalms,&lyricsIndex);
|
|
|
|
} else {
|
|
int available = 0;
|
|
|
|
int old_base_pos = *base_pos;
|
|
int old_rel_pos = *rel_pos;
|
|
FileListEntry *old_entry = entry;
|
|
|
|
if (getPercentageFunct() == 100.0f && !endOfStreamFunct())
|
|
previous = 1;
|
|
|
|
if (endOfStreamFunct())
|
|
previous = 0;
|
|
|
|
while (previous ? entry->previous : entry->next) {
|
|
entry = previous ? entry->previous : entry->next;
|
|
|
|
if (previous) {
|
|
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 || type == FILE_TYPE_OGG) {
|
|
file = path;
|
|
|
|
lrcParseClose(lyrics);
|
|
endFunct();
|
|
|
|
setAudioFunctions(type);
|
|
|
|
initFunct(0);
|
|
loadFunct((char *)file);
|
|
playFunct();
|
|
|
|
getAudioInfo(file);
|
|
|
|
lyrics = loadLyricsFile(file,&totalms,&lyricsIndex);
|
|
|
|
available = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!available) {
|
|
*base_pos = old_base_pos;
|
|
*rel_pos = old_rel_pos;
|
|
entry = old_entry;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start drawing
|
|
startDrawing(bg_audio_image);
|
|
|
|
// Draw shell info
|
|
drawShellInfo(file);
|
|
|
|
float cover_size = MAX_ENTRIES * FONT_Y_SPACE;
|
|
|
|
// Cover
|
|
if (tex) {
|
|
vita2d_draw_texture_scale(tex, SHELL_MARGIN_X, START_Y, cover_size / vita2d_texture_get_width(tex), cover_size / vita2d_texture_get_height(tex));
|
|
} else {
|
|
vita2d_draw_texture(cover_image, SHELL_MARGIN_X, START_Y);
|
|
}
|
|
|
|
// Info
|
|
float x = 2.0f * SHELL_MARGIN_X + cover_size;
|
|
|
|
pgf_draw_text(x, START_Y + (0 * FONT_Y_SPACE), AUDIO_INFO_ASSIGN, language_container[TITLE]);
|
|
pgf_draw_text(x, START_Y + (1 * FONT_Y_SPACE), AUDIO_INFO_ASSIGN, language_container[ALBUM]);
|
|
pgf_draw_text(x, START_Y + (2 * FONT_Y_SPACE), AUDIO_INFO_ASSIGN, language_container[ARTIST]);
|
|
pgf_draw_text(x, START_Y + (3 * FONT_Y_SPACE), AUDIO_INFO_ASSIGN, language_container[GENRE]);
|
|
pgf_draw_text(x, START_Y + (4 * FONT_Y_SPACE), AUDIO_INFO_ASSIGN, language_container[YEAR]);
|
|
|
|
x += 120.0f;
|
|
|
|
vita2d_enable_clipping();
|
|
vita2d_set_clip_rectangle(x + 1.0f, START_Y, x + 1.0f + 390.0f, START_Y + (5 * FONT_Y_SPACE));
|
|
|
|
float title_x = x;
|
|
uint32_t color = AUDIO_INFO;
|
|
|
|
int width = (int)pgf_text_width(fileinfo->title);
|
|
if (width >= 390.0f) {
|
|
if (scroll_count < 60) {
|
|
scroll_x = title_x;
|
|
} else if (scroll_count < width + 90) {
|
|
scroll_x--;
|
|
} else if (scroll_count < width + 120) {
|
|
color = (color & 0x00FFFFFF) | ((((color >> 24) * (scroll_count - width - 90)) / 30) << 24); // fade-in in 0.5s
|
|
scroll_x = title_x;
|
|
} else {
|
|
scroll_count = 0;
|
|
}
|
|
|
|
scroll_count++;
|
|
|
|
title_x = scroll_x;
|
|
}
|
|
|
|
pgf_draw_text(title_x, START_Y + (0 * FONT_Y_SPACE), color, fileinfo->title[0] == '\0' ? "-" : fileinfo->title);
|
|
pgf_draw_text(x, START_Y + (1 * FONT_Y_SPACE), AUDIO_INFO, fileinfo->album[0] == '\0' ? "-" : fileinfo->album);
|
|
pgf_draw_text(x, START_Y + (2 * FONT_Y_SPACE), AUDIO_INFO, fileinfo->artist[0] == '\0' ? "-" : fileinfo->artist);
|
|
pgf_draw_text(x, START_Y + (3 * FONT_Y_SPACE), AUDIO_INFO, fileinfo->genre[0] == '\0' ? "-" : fileinfo->genre);
|
|
pgf_draw_text(x, START_Y + (4 * FONT_Y_SPACE), AUDIO_INFO, fileinfo->year[0] == '\0' ? "-" : fileinfo->year);
|
|
|
|
vita2d_disable_clipping();
|
|
|
|
x -= 120.0f;
|
|
|
|
drawLyrics(lyrics, cur_time_string, &totalms, &lyricsIndex, x, START_Y + (6 * FONT_Y_SPACE));
|
|
|
|
float y = SCREEN_HEIGHT - 6.0f * SHELL_MARGIN_Y;
|
|
|
|
// Icon
|
|
vita2d_texture *icon = NULL;
|
|
|
|
if (getPlayingSpeedFunct() != 0) {
|
|
if (getPlayingSpeedFunct() < 0) {
|
|
icon = fastrewind_image;
|
|
} else {
|
|
icon = fastforward_image;
|
|
}
|
|
|
|
pgf_draw_textf(x + 45.0f, y, AUDIO_SPEED, "%dx", abs(getPlayingSpeedFunct() + (getPlayingSpeedFunct() < 0 ? -1 : 1)));
|
|
} else {
|
|
if (isPlayingFunct()) {
|
|
icon = pause_image;
|
|
} else {
|
|
icon = play_image;
|
|
}
|
|
}
|
|
|
|
vita2d_draw_texture(icon, x, y + 3.0f);
|
|
|
|
// Time
|
|
char string[32];
|
|
sprintf(string, "%s / %s", cur_time_string, fileinfo->strLength);
|
|
float time_x = ALIGN_RIGHT(SCREEN_WIDTH-SHELL_MARGIN_X, pgf_text_width(string));
|
|
|
|
int w = pgf_draw_text(time_x, y, AUDIO_TIME_CURRENT, cur_time_string);
|
|
pgf_draw_text(time_x + (float)w, y, AUDIO_TIME_SLASH, " /");
|
|
pgf_draw_text(ALIGN_RIGHT(SCREEN_WIDTH-SHELL_MARGIN_X, pgf_text_width(fileinfo->strLength)), y, AUDIO_TIME_TOTAL, fileinfo->strLength);
|
|
|
|
float length = SCREEN_WIDTH - 3.0f * SHELL_MARGIN_X - cover_size;
|
|
vita2d_draw_rectangle(x, (y) + FONT_Y_SPACE + 10.0f, length, 8, AUDIO_TIME_BAR_BG);
|
|
vita2d_draw_rectangle(x, (y) + FONT_Y_SPACE + 10.0f, getPercentageFunct() * length / 100.0f, 8, AUDIO_TIME_BAR);
|
|
|
|
// End drawing
|
|
endDrawing();
|
|
}
|
|
|
|
if (tex) {
|
|
vita2d_wait_rendering_done();
|
|
vita2d_free_texture(tex);
|
|
tex = NULL;
|
|
}
|
|
|
|
lrcParseClose(lyrics);
|
|
endFunct();
|
|
|
|
powerUnlock();
|
|
|
|
sceAppMgrReleaseBgmPort();
|
|
|
|
return 0;
|
|
}
|