Hi, I got two questions about libogg / libvorbis:
1.) Where can I find a documentation about the functions, constants, etc these 2 libs provide?
2.) Is there any simple sample code playing ogg-vorbis files on the PSP? I know about some programs which play ogg files, but I haven't found any source code so far... :/
Another thing is that I experienced problems while trying to compile libogg (checked out from via svn last night). It kept stating I hadn't installed automake though I actually hae installed automake 1.10 (according to the README.PSP, version 1.6 or above is required). I finally compiled it by changing autogen.sh so that it wouldn't check for autogen and aclocale anymore...
<edit>
I'm NOT using Cygwin but native Linux (Frugalware 0.7-pre2).
</edit>
thx in advance
Pascal
libogg / libvorbis
The official web site has docs I've made a simple player that plays a .ogg vorbis file including pausing, showing the current time in seconds of the track and skipping forward and back but the code is written using SDL not native vorbis but here it is anyway.
This should compile if you have installed the psp toolchain along with the psp ports of libogg libvorbis and libsdl.
Code: Select all
/*
* OGG PLAYER
*
*
*/
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspdisplay.h>
#include <psppower.h>
#include <pspctrl.h>
#include <pspiofilemgr.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pspumd.h>
#include <SDL.h>
#include <SDL_main.h>
#include <SDL_thread.h>
#include <SDL_mixer.h>
#include <psprtc.h>
#include <vorbis/vorbisfile.h>
/* to make typing easier */
#define printf pspDebugScreenPrintf
/* known file types */
#define file1 8557
#define file2 8630
#define file3 8703
#define dire1 4461
#define dire2 4607
#define specialfile 511
/* Screen resolution */
#define W 480
#define H 272
int menu = 0; //menu options
int i = 0; //used for loops
SceCtrlData pad; //stores which button has been pressed
int x = 22; //character that changes while music is playing: /-\|
char nowplaying[256] = ""; //actuall string of currently playing music file
int playthid; //used for playing thread
/* Exit callback */
int exit_callback(int arg1, int arg2, void *common)
{
sceKernelExitGame();
return 0;
}
/* Callback thread */
int CallbackThread(SceSize args, void *argp)
{
int cbid;
cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
sceKernelRegisterExitCallback(cbid);
sceKernelSleepThreadCB();
return 0;
}
/* Sets up the callback thread and returns its thread id */
int SetupCallbacks(void)
{
int thid = 0;
thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
if(thid >= 0)
{
sceKernelStartThread(thid, 0, 0);
}
return thid;
}
int goupdirectory(char location[256])
{
for(i=0; i<=256; i++)
{
if(location[i] == '\0') //find the first null character
{
i --; //go back one character
location[i] = '\0'; //take of the ending '/'
/* keep removing charcters until a '/' is found */
while(location[i] != '/')
{
/* if a ':' is found its gone too far eg: 'ms0:/' */
if(location[i] == ':')
{
i++;
location[i] = '/'; //add on a '/'
i++;
location[i] = '\0'; // add on the null character
return 1;
}
location[i] = '\0';
i--;
}
return 0;
}
}
return 1;
}
int checkextension(char filename[256])
{
for(i=0; i<=255; i++)
{
if(filename[i] == '\0') //find the first null character
{
i--;
if((filename[i] == 71) || (filename[i] == 103)) //should end in g or G
{
i--;
if((filename[i] == 71) || (filename[i] == 103)) /*and another g
or G before that */
{
i--;
if((filename[i] == 79) || (filename[i] == 111)) /*and o or O before that */
{
i--;
if(filename[i] == 46) //and finally a dot
{
return 1; //return 1 for .ogg (will add further extensions such as mp3 in future)
}
else
{
break; //no dot before ogg so its of the wrong type
}
}
else
{
break; //no o before gg so its of the wrong type
}
}
else
{
break; //ends in a g but there isnt another g before it
}
}
else
{
break; //doesnt end in g so wrong file type
}
}
}
return 0; //not a playable file
}
int playing() //thread to run when being played
{
int k;
while(1)
{
if(Mix_PlayingMusic() && !Mix_PausedMusic()) //if its playing and not paused
{
switch(x) //this is the spinning character to show music is being played
{
case 22:
x=29;
break;
case 23:
x=30;
break;
case 29:
x=23;
break;
case 30:
x=22;
break;
}
}
if(Mix_PlayingMusic() && Mix_PausedMusic()) //if it is playing but paused
{
}
if(!Mix_PlayingMusic()) //if not playing
{
sceKernelExitDeleteThread(0);
}
/* this loop slows the thread down for controlling the speed of the spinning character */
for(k=0;k<10;k++)
{
sceDisplayWaitVblankStart();
}
}
}
int starttheogg()
{
/* the next 4 variables are used for opening the audio device */
int audio_rate = 22050;
Uint16 audio_format = AUDIO_S16; /* 16-bit stereo */
int audio_channels = 2;
int audio_buffers = 4096;
/* Mix_Music actually holds the music information. */
Mix_Music *music = NULL;
/* manually open the ogg vorbis file to see how long it is as not available with SDL_mixer */
OggVorbis_File ovf;
/* file pointer must be used with ovfile pointer see above^ */
FILE *fp;
/* total length of music file(seconds) */
int vorbis_time;
/* ticks per second */
u32 tickspersecond;
/* start time(ticks) */
u64 time1;
/* end time(ticks) */
u64 time2;
/* position in current playing music file(in ticks so must divide by tickspersecond) */
u64 musicposition;
/* position to seek to */
double seekto;
/* get how many ticks per second for working out position in play stream */
tickspersecond = sceRtcGetTickResolution();
/* open the file for getting its total time as no function available in SDL_mixer */
fp=fopen(nowplaying, "r");
ov_open(fp,&ovf,NULL,0);
vorbis_time = ov_time_total(&ovf,-1);
ov_clear(&ovf);
fclose(fp);
/* create the thread that goes when playing (int playing()) see above for the code^.
* first initializing playthid to 0. then calling the create thread function.
* giving it the name playing_thread. 0x32 priority. 0xFA0 initial stack size.
* 0 attributes. NULL options
*/
playthid = 0;
playthid = sceKernelCreateThread("playing_thread", playing, 0x32, 0xFA0, 0, NULL);
/* start at 0 and work towards end */
musicposition = 0;
pspDebugScreenSetXY(0,8);
printf("%-256s", nowplaying);
pspDebugScreenSetXY(0,26);
printf("<START> %c %-25s\n", 61, "Pause/Resume");
printf("%c %c %-25s\n", -8, 61, "Stop");
printf("L %c %-25s\n", 61, "Rewind to start");
printf("%c %c %-25s\n", -48, 61, "Seek Back");
printf("%c %c %-25s", -49, 61, "Seek Forward");
/* Open the audio device not the music file */
if(Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers))
{
return 1;
}
if(music != NULL) //make sure no music is already loaded
{
Mix_HaltMusic();
Mix_FreeMusic(music);
music = NULL;
}
music = Mix_LoadMUS(nowplaying);
if(music == NULL) //music should not be null anymore but if it still null, it failed to load
{
return 2;
}
if(Mix_PlayMusic(music, 0) == -1) //play music returns 0 on success or -1 on error
{
return 3;
}
if(playthid>=0) //if thread created ok start it
{
sceKernelStartThread(playthid,0,0);
}
if(playthid<0)
{
pspDebugScreenClear();
printf("thread error");
while(1) //just sit here in infinite loop so i can see where the error is
{
}
}
sceRtcGetCurrentTick(&time1);
while(Mix_PlayingMusic())
{
if(Mix_PausedMusic())
{
pspDebugScreenSetXY(25,15);
printf("music paused \n");
}
else
{
pspDebugScreenSetXY(25,15);
printf("music playing\n");
}
for(i=0;i<10;i++)
{
sceDisplayWaitVblankStart();
}
while(1)
{
if(!Mix_PlayingMusic())
{
break;
}
pspDebugScreenSetXY(40,15);
if(Mix_PausedMusic())
{
sceRtcGetCurrentTick(&time1);
}
sceRtcGetCurrentTick(&time2);
musicposition = musicposition + (time2-time1);
printf("%c %d/%d ",x,(int)(musicposition/tickspersecond),vorbis_time);
sceRtcGetCurrentTick(&time1);
sceCtrlReadBufferPositive(&pad, 1);
if(pad.Buttons & PSP_CTRL_LEFT)
{
musicposition = musicposition - (tickspersecond*4);//seek backward 4 seconds
if(((int)musicposition)<0)
{
musicposition=0;
sceRtcGetCurrentTick(&time1);
seekto = 0;
}
else
{
seekto = (musicposition/tickspersecond);
}
Mix_SetMusicPosition(seekto);
x = 30;
break;
}
if(pad.Buttons & PSP_CTRL_RIGHT)
{
musicposition = musicposition + (tickspersecond*4);//seek forward 4 seconds
if((musicposition/tickspersecond)>vorbis_time)
{
Mix_HaltMusic();
Mix_FreeMusic(music);
music = NULL;
break;
}
seekto = (musicposition/tickspersecond);
Mix_SetMusicPosition(seekto);
break;
}
if(pad.Buttons & PSP_CTRL_START)
{
if(Mix_PausedMusic())
{
Mix_ResumeMusic();
sceRtcGetCurrentTick(&time1);
}
else
{
Mix_PauseMusic();
}
break;
}
if(pad.Buttons & PSP_CTRL_CIRCLE)
{
Mix_HaltMusic();
Mix_FreeMusic(music);
music = NULL;
break;
}
if(pad.Buttons & PSP_CTRL_LTRIGGER)
{
x = 30;
musicposition = 0;
Mix_RewindMusic();
sceRtcGetCurrentTick(&time1);
break;
}
}
}
pspDebugScreenSetXY(25,15);
printf("%-50c", ' ');
return 0;
}
int main(void)
{
int drive = 0; //used to change drive
int pos = 0; //file position in current directory
int dfd; //directory file descriptor
int morede = 1; //more directory entrys to read
char location[256] = "fatms0:/"; //location to open
int filecount = 0; //count how many files in current directory
int driveneedstochange = 0; //used to check if drive needs to change
SceIoDirent dir; //store name and info
memset(&dir, 0, sizeof dir); //set memory (must do this to make it work)
SetupCallbacks(); //setup callbacks
pspDebugScreenInit(); //initialize screen
SDL_Init(SDL_INIT_AUDIO); //must be called to use sdl audio
printf("OGG VORBIS PLAYER\nCoded by Viper");
/* Start main loop */
while(1)
{
if(driveneedstochange == 1)
{
switch (drive)
{
case 0:
strcpy(location, "fatms0:/");
break;
case 1:
pspDebugScreenSetXY(0,8);
printf("%-256s", "Checking UMD...");
i = sceUmdCheckMedium(0);
if(i != 0)
{
sceUmdActivate(1, "disc0:");
strcpy(location, "disc0:/");
sceUmdWaitDriveStat(UMD_WAITFORINIT);
break;
}
else
{
drive = 2;
}
case 2:
strcpy(location, "flash0:/");
break;
case 3:
strcpy(location, "flash1:/");
break;
}
driveneedstochange = 0;
}
pspDebugScreenSetXY(0,8);
if((dfd = sceIoDopen(location)) >= 0)
{
morede = 1;
filecount = 0;
while(morede > 0) //while there is more directory entries to read
{
morede = sceIoDread(dfd, &dir);
filecount ++;
}
filecount --;
sceIoDclose(dfd);
dfd = sceIoDopen(location); //reopening it now that ive got the count
printf("%-256s", location);
pspDebugScreenSetXY(0,10);
for(i = pos; i >= 0; i--)
{
morede = sceIoDread(dfd, &dir);
if(morede < 0) //error
{
printf("Error reading %-242s", location); /* it managed to open the directory but couldnt
read it contents (unlikely going to happen)*/
while(1)
{
sceDisplayWaitVblankStart();
}
}
}
printf("%d/%-5d", (pos+1), filecount);
pspDebugScreenSetXY(15,10);
switch(dir.d_stat.st_mode)
{
case dire1:
case dire2:
printf("directory");
break;
case file1:
case file2:
case file3:
printf("file ");
break;
case specialfile:
printf("special ");
break;
default:
printf("?:%-7d", dir.d_stat.st_mode);
break;
}
pspDebugScreenSetXY(25,10);
printf("%-256s", dir.d_name);
sceIoDclose(dfd);
}
else
{
printf("%-256s", "Error opening drive");
}
pspDebugScreenSetXY(0,26);
printf("%c %c %-25s\n", -40, 61, "Change drive"); //triangle equals change drive
printf("%c %c %-25s\n", -8 , 61, "Go up to parent directory"); //circle equals go up a dir
printf("%c %c %-25s\n", 28, 61, "Play / enter directory"); //cross equalls play / enter directory
printf("%c/%c %c %-25s\n", -50, -51, 61, "Choose file / directory"); //up/down equals choose file / director
printf("%-40c", ' ');
for(i = 0; i < 10; i++) //so user cant make accidently press the buttons to quickly
{
sceDisplayWaitVblankStart();
}
/* Start input check loop */
while(1)
{
sceCtrlReadBufferPositive(&pad, 1);
if(pad.Buttons & PSP_CTRL_UP)
{
if(pos > 0)
{
pos--;
break;
}
else
{
break;
}
}
if(pad.Buttons & PSP_CTRL_DOWN)
{
if(pos < (filecount-1))
{
pos++;
break;
}
else
{
break;
}
}
if(pad.Buttons & PSP_CTRL_TRIANGLE)
{
pos = 0;
driveneedstochange = 1;
switch (drive)
{
case 0:
drive = 1;
break;
case 1:
drive = 2;
break;
case 2:
drive = 3;
break;
case 3:
drive = 0;
break;
}
break;
}
if(pad.Buttons & PSP_CTRL_CIRCLE) //when on UMD ther is no .. to go up a dir so we need this
{
if(goupdirectory(location) == 0)
{
pos = 0;
}
break;
}
if(pad.Buttons & PSP_CTRL_CROSS) //if ogg play it or if dir change to that dir
{
switch(dir.d_stat.st_mode)
{
case dire1: //this is to check if its a directory
case dire2:
/* do nothing bcos a single dot means the same working dir */
if((dir.d_name[0] == '.') && (dir.d_name[1] == '\0'))
{
break;
}
/* if we get 2 dots however that means go up a directory */
if((dir.d_name[0] == '.') && (dir.d_name[1] == '.') && (dir.d_name[2] == '\0'))
{
if(goupdirectory(location) == 0)
{
pos = 0;
}
break;
}
else //must be regular directory
{
pos = 0; //start at the first file
strcat(location, dir.d_name); //add the directory name to the current location
for(i=0; i<255; i++)
{
if(location[i] == '\0') //find the first null character
{
location[i] = '/'; //and make it a forward slash
i++;
location[i] = '\0'; //add the final null character
break;
}
}
}
break;
case file1: //check if file
case file2:
case file3:
//now check if it ends in .ogg
if(checkextension(dir.d_name) == 1)
{
strcpy(nowplaying, location);
strcat(nowplaying, dir.d_name);
starttheogg();
break;
}
break;
default:
break; //dont know how to handle this
}
break;
}
} //end input check loop
morede = 1;
} //end main loop
return 0;
} //end main function