forums.ps2dev.org Forum Index forums.ps2dev.org
Homebrew PS2, PSP & PS3 Development Discussions
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

[Solved] Help I can't get audio working properly

 
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Development
View previous topic :: View next topic  
Author Message
Viper8896



Joined: 26 Jan 2006
Posts: 110

PostPosted: Wed Sep 12, 2007 7:40 pm    Post subject: [Solved] Help I can't get audio working properly Reply with quote

Help I'm having a lot of trouble getting decoded ogg vorbis working with pspaudiolib. I just can't figure it out and I can't find any documentation on it. The closest I've got is something very choppy:

main.c
Code:

#include <pspkernel.h>
#include <pspaudiolib.h>
#include <pspdebug.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include <pspiofilemgr.h>
#include <psprtc.h>
#include <tremor/ivorbisfile.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/* Define the module info section */
PSP_MODULE_INFO("vorbis", 0, 1, 1);

/* Define the main thread's attribute value (optional) */
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);

/* to make typing easier */
#define printf pspDebugScreenPrintf

/* 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;
}

OggVorbis_File ovf;
FILE *fp;
int current_section;

void audioCallback(void* buf, unsigned int length, void *userdata)
{
      ov_read(&ovf,buf,4096,&current_section);   
}

int main(void)
{
   pspDebugScreenInit();
   SetupCallbacks();
   fp=fopen("fatms0:/1.ogg\0", "r");
   ov_open(fp,&ovf,NULL,0);
   vorbis_info *vi = ov_info(&ovf,-1);
   int vorb_time = ov_time_total(&ovf,-1);;
   printf("version= %d\nchannels= %d\nsample rate= %ld\nupper bitrate= %ld\nnominal bitrate= %ld\nlower bitrate = %ld\ntotal time=%d", vi->version, vi->channels, vi->rate, vi->bitrate_upper, vi->bitrate_nominal, vi->bitrate_lower, vorb_time/1000);
   printf("\n\nattempting to play...\n\n");
   pspAudioInit();
   pspAudioSetChannelCallback(0, audioCallback, NULL);
   while(1)
   {
      sceDisplayWaitVblankStart();
   }
   return 0;
}


MakeFile
Code:

TARGET = VORBIS-PLAYER
OBJS = main.o

BUILD_PRX=0
PSP_FW_VERSION=150

INCDIR =
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)

LIBDIR =
LDFLAGS =

EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = VORBIS PLAYER

PSPSDK=$(shell psp-config --pspsdk-path)
PSPBIN = $(PSPSDK)/../bin
LIBS += -lpsprtc -lvorbisidec -lvorbis -logg -lpspumd -lpspaudiolib -lpspaudio
include $(PSPSDK)/lib/build.mak


Last edited by Viper8896 on Sat Sep 15, 2007 10:19 am; edited 1 time in total
Back to top
View user's profile Send private message
J.F.



Joined: 22 Feb 2004
Posts: 2906

PostPosted: Thu Sep 13, 2007 4:44 am    Post subject: Reply with quote

It's choppy because you wait until the callback wants audio data to decode the ogg data directly into the buffer. The callback works like this: the audio lib thread plays a buffer of samples, then asks for more; if you take longer than one sample period to respond, you get pops in the audio.

What you NEED to do is something like this - decode a buffer's worth of sample data, then wait for the callback. In the callback, merely copy this data and set a flag saying it's empty. The decode loop sees the flag and decodes the next buffer worth of data long before the callback ever says it needs more.
Back to top
View user's profile Send private message AIM Address
Viper8896



Joined: 26 Jan 2006
Posts: 110

PostPosted: Thu Sep 13, 2007 10:10 pm    Post subject: Reply with quote

thanks for your help but still same prob:

main.c
Code:
#include <pspkernel.h>
#include <pspaudiolib.h>
#include <pspdebug.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include <pspiofilemgr.h>
#include <pspthreadman.h>
#include <psprtc.h>
#include <tremor/ivorbisfile.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/* Define the module info section */
PSP_MODULE_INFO("vorbis", 0, 1, 1);

/* Define the main thread's attribute value (optional) */
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);

/* to make typing easier */
#define printf pspDebugScreenPrintf

/* 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;
}

OggVorbis_File ovf;
FILE *fp;
int current_section;
char *pcmout;
short needMoBuffer = 1;
int buffer_thid;

int fillbuffer()
{
   while(1)
   {
      if(needMoBuffer)
      {
         ov_read(&ovf,pcmout,4096, &current_section);
         needMoBuffer=0;
      }
      else
      {
      sceDisplayWaitVblankStart();
      }
   }
}

void audioCallback(void* buf, unsigned int length, void *userdata)
{
   while(needMoBuffer)//wait just incase the next buffer isnt ready
   {
      sceDisplayWaitVblankStart();
   }
   memcpy(buf,pcmout,4096);
   needMoBuffer=1;
}

int main(void)
{
   pspDebugScreenInit();
   SetupCallbacks();
   pcmout = malloc(4096);
   if(pcmout==NULL)
   {
      printf("memory allocation error");
      while(1){sceDisplayWaitVblankStart();}
   }
   fp=fopen("fatms0:/1.ogg\0", "r");
   ov_open(fp,&ovf,NULL,0);
   vorbis_info *vi = ov_info(&ovf,-1);
   int vorb_time = ov_time_total(&ovf,-1);;
   printf("version= %d\nchannels= %d\nsample rate= %ld\nupper bitrate= %ld\nnominal bitrate= %ld\nlower bitrate = %ld\ntotal time=%d", vi->version, vi->channels, vi->rate, vi->bitrate_upper, vi->bitrate_nominal, vi->bitrate_lower, vorb_time/1000);
   printf("\n\nattempting to play...\n\n");
   buffer_thid = 0;
   /* 0x1800 = Initial stack size. Not min but too low and thread fails - it can't load the buffer. */
   buffer_thid = sceKernelCreateThread("load_more_vorbis", fillbuffer, 0x16, 0x1800, 0, NULL);
   if(buffer_thid>=0) //if thread created ok start it
   {
      sceKernelStartThread(buffer_thid,0,0);
   }
   if(buffer_thid<0)
   {
      printf("thread error");
      while(1){sceDisplayWaitVblankStart();}
   }
   sceDisplayWaitVblankStart();
   sceDisplayWaitVblankStart();
   printf("initiazlizing audio\n");
   pspAudioInit();
   printf("setting audio callback\n\n");
   pspAudioSetChannelCallback(0, audioCallback, NULL);
   while(1)
   {
   sceDisplayWaitVblankStart();
   }
   return 0;
}



Makefile
Code:
TARGET = VORBIS-PLAYER
OBJS = main.o

BUILD_PRX=0
PSP_FW_VERSION=150

INCDIR =
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)

LIBDIR =
LDFLAGS =

EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = VORBIS PLAYER

PSPSDK=$(shell psp-config --pspsdk-path)
PSPBIN = $(PSPSDK)/../bin
LIBS += -lpsprtc -lvorbisidec -lvorbis -logg -lpspumd -lpspaudiolib -lpspaudio
include $(PSPSDK)/lib/build.mak

Back to top
View user's profile Send private message
J.F.



Joined: 22 Feb 2004
Posts: 2906

PostPosted: Fri Sep 14, 2007 3:46 am    Post subject: Reply with quote

Better, but don't wait on vblank. If you miss the guy by a microsecond, you're waiting 1/60th of a second (longer than the audio sample period). Wait on a pair of semaphores - one each direction (full/empty). That way your communication is as fast as the task switching (which on most systems is microseconds at most).
Back to top
View user's profile Send private message AIM Address
Viper8896



Joined: 26 Jan 2006
Posts: 110

PostPosted: Fri Sep 14, 2007 12:30 pm    Post subject: Reply with quote

ive tried this and i cant get it too work.
Code:
#include <pspkernel.h>
#include <pspaudiolib.h>
#include <pspdebug.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include <pspiofilemgr.h>
#include <pspthreadman.h>
#include <psprtc.h>
#include <tremor/ivorbisfile.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/* Define the module info section */
PSP_MODULE_INFO("vorbis", 0, 1, 1);

/* Define the main thread's attribute value (optional) */
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);

/* to make typing easier */
#define printf pspDebugScreenPrintf

/* 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;
}

OggVorbis_File ovf;
FILE *fp;
int current_section;
char *pcmout;
int buffer_thid;
int buffer_semaid;

int fillbuffer()
{
   while(1)
   {
      sceKernelWaitSema(buffer_semaid, 1, 0);
      ov_read(&ovf,pcmout,4096, &current_section);
      sceKernelSignalSema(buffer_semaid, 0);
   }
}

void audioCallback(void* buf, unsigned int length, void *userdata)
{
   sceKernelWaitSema(buffer_semaid, 0, 0);
   memcpy(buf,pcmout,4096);
   sceKernelSignalSema(buffer_semaid, 1);
}

int main(void)
{
   pspDebugScreenInit();
   SetupCallbacks();
   pcmout = malloc(4096);
   if(pcmout==NULL)
   {
      printf("memory allocation error");
      while(1){sceDisplayWaitVblankStart();}
   }
   fp=fopen("fatms0:/1.ogg\0", "r");
   ov_open(fp,&ovf,NULL,0);
   vorbis_info *vi = ov_info(&ovf,-1);
   int vorb_time = ov_time_total(&ovf,-1);;
   printf("version= %d\nchannels= %d\nsample rate= %ld\nupper bitrate= %ld\nnominal bitrate= %ld\nlower bitrate = %ld\ntotal time=%d", vi->version, vi->channels, vi->rate, vi->bitrate_upper, vi->bitrate_nominal, vi->bitrate_lower, vorb_time/1000);
   printf("\n\nattempting to play...\n\n");
   buffer_semaid = sceKernelCreateSema("Buffer_Sema", 0, 1, 1, 0);
   buffer_thid = 0;
   /* 0x1800 = Initial stack size. Not min but too low and thread fails - it can't load the buffer. */
   buffer_thid = sceKernelCreateThread("load_more_vorbis", fillbuffer, 0x32, 0x1800, 0, NULL);
   if(buffer_thid>=0) //if thread created ok start it
   {
      sceKernelStartThread(buffer_thid,0,0);
   }
   if(buffer_thid<0)
   {
      printf("thread error");
      while(1){sceDisplayWaitVblankStart();}
   }
   printf("initiazlizing audio\n");
   pspAudioInit();
   printf("setting audio callback\n\n");
   pspAudioSetChannelCallback(0, audioCallback, NULL);
   while(1)
   {
      sceDisplayWaitVblankStart();      
   }
   return 0;
}

Back to top
View user's profile Send private message
J.F.



Joined: 22 Feb 2004
Posts: 2906

PostPosted: Fri Sep 14, 2007 4:03 pm    Post subject: Reply with quote

You're trying to do it with one semaphore when you need two. Try it like this:

Code:

int fillbuffer()
{
   while(1)
   {
      sceKernelWaitSema(empty_semaid, 1, 0);
      ov_read(&ovf,pcmout,4096, &current_section);
      sceKernelSignalSema(full_semaid, 1);
   }
}

void audioCallback(void* buf, unsigned int length, void *userdata)
{
   sceKernelWaitSema(full_semaid, 1, 0);
   memcpy(buf,pcmout,4096);
   sceKernelSignalSema(empty_semaid, 1);
}


Create the empty semaphore set and the full semaphore clear. You also need to end the fillbuffer while on ov_read running out of data.

I'm not certain if semaphores are cleared by the wait. They may need to be reset after the wait. If so, the above would be like this:

Code:

int fillbuffer()
{
   while(1)
   {
      sceKernelWaitSema(empty_semaid, 1, 0);
      sceKernelSignalSema(empty_semaid, 0);
      ov_read(&ovf,pcmout,4096, &current_section);
      sceKernelSignalSema(full_semaid, 1);
   }
}

void audioCallback(void* buf, unsigned int length, void *userdata)
{
   sceKernelWaitSema(full_semaid, 1, 0);
   sceKernelSignalSema(full_semaid, 0);
   memcpy(buf,pcmout,4096);
   sceKernelSignalSema(empty_semaid, 1);
}
Back to top
View user's profile Send private message AIM Address
sakya



Joined: 28 Apr 2006
Posts: 190

PostPosted: Fri Sep 14, 2007 9:38 pm    Post subject: Reply with quote

Hi! :)

I'm also interested in OGG playback.
I tried to write my little app to decode an OGG but it plays too fast.
I tried first with with pspAudioSetChannelCallback, and then with sceAudioOutputBlocking but the result is the same.
I think the problem is I don't know how to retrieve the number of samples decoded in pcmout.

I decode using a buffer[4096] and reserve the channel with:
Code:
OGG_audio_channel = sceAudioChReserve(OGG_audio_channel, 1152 , PSP_AUDIO_FORMAT_STEREO);


Then, how can I know that 1152 samples are in pcmoutBlock when I execute sceAudioOutputBlocking (1152 is a number I just copied from another source)?
Code:
sceAudioOutputBlocking(OGG_audio_channel, PSP_AUDIO_VOLUME_MAX, pcmoutBlock);


Here's my code:
Code:
#include <pspkernel.h>
#include <pspctrl.h>
#include <pspdebug.h>
#include <pspaudio.h>
#include <pspaudiolib.h>
#include <psppower.h>
#include <pspdisplay.h>
#include <string.h>
#include <stdio.h>

#include "tremor/ivorbiscodec.h"
#include "tremor/ivorbisfile.h"

PSP_MODULE_INFO("libTremor Example", 0, 1, 1);
#define FALSE 0
#define TRUE !FALSE
#define THREAD_PRIORITY 12

/////////////////////////////////////////////////////////////////////////////////////////
//Globals
/////////////////////////////////////////////////////////////////////////////////////////
OggVorbis_File OGG_VorbisFile;
int OGG_eos = 0;
int OGG_audio_channel = 0;
char pcmout[4096];
char pcmoutBlock[4096];
int bitStream;
static FILE *OGG_file = 0;

int bufferEmpty = 0;
int bufferFull = 0;

/////////////////////////////////////////////////////////////////////////////////////////
//Callbacks
/////////////////////////////////////////////////////////////////////////////////////////
/* 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;
}

/////////////////////////////////////////////////////////////////////////////////////////
//Buffer filling
/////////////////////////////////////////////////////////////////////////////////////////
int fillBuffer(SceSize args, void *argp){
    while(1){
        sceKernelWaitSema(bufferEmpty, 1, 0);
        sceKernelSignalSema(bufferEmpty, 0);
      long ret=ov_read(&OGG_VorbisFile,pcmout,sizeof(pcmout),&bitStream);
      if (ret == 0){
         //EOF:
            OGG_eos = 1;
            ov_clear(&OGG_VorbisFile);
            sceKernelSignalSema(bufferFull, 1);
            break;
        }
        sceKernelSignalSema(bufferFull, 1);
    }
    return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
//Audio output
/////////////////////////////////////////////////////////////////////////////////////////
int audioOutput(SceSize args, void *argp){
    while(1){
        sceKernelWaitSema(bufferFull, 1, 0);
        sceKernelSignalSema(bufferFull, 0);
        if (OGG_eos)
            break;
        memcpy(pcmoutBlock,pcmout,4096);
        sceKernelSignalSema(bufferEmpty, 1);
        sceAudioOutputBlocking(OGG_audio_channel, PSP_AUDIO_VOLUME_MAX, pcmoutBlock);
    }
    return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
//Main
/////////////////////////////////////////////////////////////////////////////////////////
int main() {
   scePowerSetClockFrequency(222, 222, 111);

   pspDebugScreenInit();
   SetupCallbacks();

   pspDebugScreenInit();
   pspDebugScreenSetXY(0, 0);
   pspDebugScreenPrintf("libTremor test");
   pspDebugScreenSetXY(0, 25);
   pspDebugScreenPrintf("Press X to exit");

   SceCtrlData pad;

   //Apro il file OGG:
   OGG_file = fopen("ms0:/test.ogg", "r");
   if ((OGG_file) != NULL) {
      ov_open(OGG_file, &OGG_VorbisFile, NULL, 0);
   }else{
      pspDebugScreenSetXY(0, 10);
      pspDebugScreenPrintf("Error opening file\n");
      return -1;
   }

    //Stampo le informazioni sul file:
   vorbis_info *vi = ov_info(&OGG_VorbisFile, -1);
   pspDebugScreenSetXY(0, 2);
   pspDebugScreenPrintf("Channels: %d\n", vi->channels);
   pspDebugScreenPrintf("Hz      : %ld Hz\n", vi->rate);
   pspDebugScreenPrintf("Bitrate : %ld kBit\n", vi->bitrate_nominal/1000);

   int h = 0;
   int m = 0;
   int s = 0;
   char dest[9];
   long secs = (long)ov_time_total(&OGG_VorbisFile, -1)/1000;
   h = secs / 3600;
   m = (secs - h * 3600) / 60;
   s = secs - h * 3600 - m * 60;
   snprintf(dest, sizeof(dest), "%2.2i:%2.2i:%2.2i", h, m, s);
   pspDebugScreenPrintf("Length  : %s\n", dest);

    //Reserve channel:
    OGG_audio_channel = sceAudioChReserve(OGG_audio_channel, 1152 , PSP_AUDIO_FORMAT_STEREO);

    //Start buffer filling thread:
    int bufferThid = sceKernelCreateThread("bufferFilling", fillBuffer, 0x18, 0x1800, 0, NULL);
    if(bufferThid < 0)
        return -1;
   sceKernelStartThread(bufferThid, 0, NULL);
    sceKernelSignalSema(bufferEmpty, 1);

    //Start audio output thread:
    int audioThid = sceKernelCreateThread("audioOutput", audioOutput, 0x18, 0x1800, 0, NULL);
    if(audioThid < 0)
        return -1;
   sceKernelStartThread(audioThid, 0, NULL);

   while(1){
      sceCtrlReadBufferPositive(&pad, 1);
      if(pad.Buttons & PSP_CTRL_CROSS){
         break;
      }
   }

    //Release chennale:
    sceAudioChRelease(OGG_audio_channel);
   sceKernelExitGame();
   return 0;
}


Many thanks :)

Ciaooo
Sakya
Back to top
View user's profile Send private message Visit poster's website
Viper8896



Joined: 26 Jan 2006
Posts: 110

PostPosted: Fri Sep 14, 2007 10:13 pm    Post subject: Reply with quote

sce audio out needs the buffer by reference as the last argument. then it will allways attempt to output the ammount of SAMPLES u told it to with sceAudioChReserve. each sample is 16 bit (2 bytes) and for stereo its double that so 4 bytes per sample. char pcmout[4096] is a 4 k buffer so it can hold 1024 16bit stereo samples. when you use the blocking call rather that the no blocking call it will output all the audio before it moves on in your code. i hope any of this ways helpfull.

and jf are you sure about needing 2 semas. couldnt i just use 1 and put it in a different state. i tried it anyway like that and it was still choppy. and ive tried to make sense of what is going on here http://svn.ps2dev.org/filedetails.php?repname=pspware&path=%2Ftrunk%2FPSPMediaCenter%2Fcodec%2Fogg%2Foggplayer.c&rev=0&sc=0
and i dont seems like it takes 1 channel and doubles it across for stereo instead of using right and left like it was originally encoded for. and finally what is the second argument that the audio call back receives each time used for. it seems that it is allways a constant 1024.
Back to top
View user's profile Send private message
sakya



Joined: 28 Apr 2006
Posts: 190

PostPosted: Fri Sep 14, 2007 11:07 pm    Post subject: Reply with quote

Hi! :)

Ooops, in the previous source I forgot to create the semaphores! ;)

Many thanks for your answer, it's usefull. :)
I think the problem is that ov_read() doesen't return always 4096, because "ov_read() will decode at most one vorbis packet per invocation, so the value returned will generally be less than length."

I updated my code (I just jump if decoded bytes != 4096).
The audio is bad (obviously: I'm not outputting all the data) but sounds better then before).

Here's the code:
Code:
#include <pspkernel.h>
#include <pspctrl.h>
#include <pspdebug.h>
#include <pspaudio.h>
#include <pspaudiolib.h>
#include <psppower.h>
#include <pspdisplay.h>
#include <string.h>
#include <stdio.h>

#include "tremor/ivorbiscodec.h"
#include "tremor/ivorbisfile.h"

PSP_MODULE_INFO("libTremor Example", 0x1000, 1, 1);
PSP_MAIN_THREAD_ATTR(0);

#define OUTPUT_BUFFER 4*1024

/////////////////////////////////////////////////////////////////////////////////////////
//Globals
/////////////////////////////////////////////////////////////////////////////////////////
int runningFlag = 1;
OggVorbis_File OGG_VorbisFile;
int OGG_eos = 0;
int OGG_audio_channel = 0;
char pcmout[OUTPUT_BUFFER]__attribute__ ((aligned(64)));
char pcmoutBlock[OUTPUT_BUFFER]__attribute__ ((aligned(64)));
int bitStream;
static FILE *OGG_file = 0;

int bufferEmpty;
int bufferFull;
long bytesRed = 0;

/////////////////////////////////////////////////////////////////////////////////////////
//Callbacks
/////////////////////////////////////////////////////////////////////////////////////////
/* Exit callback */
int exit_callback(int arg1, int arg2, void *common) {
   //sceKernelExitGame();
    runningFlag = 0;
    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;
}

/////////////////////////////////////////////////////////////////////////////////////////
//Buffer filling
/////////////////////////////////////////////////////////////////////////////////////////
int fillBuffer(SceSize args, void *argp){
    while(runningFlag){
        sceKernelWaitSema(bufferEmpty, 1, 0);
        sceKernelSignalSema(bufferEmpty, 0);
      bytesRed = ov_read(&OGG_VorbisFile,pcmout,sizeof(pcmout),&bitStream);
      if (!bytesRed){
         //EOF:
            OGG_eos = 1;
            ov_clear(&OGG_VorbisFile);
            sceKernelSignalSema(bufferFull, 1);
            break;
        }
        sceKernelSignalSema(bufferFull, 1);
    }
    return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
//Audio output
/////////////////////////////////////////////////////////////////////////////////////////
int audioOutput(SceSize args, void *argp){
    long bytesLeft = OUTPUT_BUFFER;
    int currentBufferPos = 0;

    while(runningFlag){
        sceKernelWaitSema(bufferFull, 1, 0);
        sceKernelSignalSema(bufferFull, 0);
        if (OGG_eos)
            break;

        bytesLeft -= bytesRed;
        if (!bytesLeft){
            memcpy(pcmoutBlock,pcmout,OUTPUT_BUFFER);
            bytesLeft = OUTPUT_BUFFER;
            currentBufferPos = 0;
        }else{
            //TODO: Manage bytesRead != OUTPUT_BUFFER
            bytesLeft = OUTPUT_BUFFER;
            sceKernelSignalSema(bufferEmpty, 1);
            continue;
        }
        sceKernelSignalSema(bufferEmpty, 1);
        sceAudioOutputBlocking(OGG_audio_channel, PSP_AUDIO_VOLUME_MAX, pcmoutBlock);
    }
    return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
//Main
/////////////////////////////////////////////////////////////////////////////////////////
int main() {
   scePowerSetClockFrequency(222, 222, 111);

   pspDebugScreenInit();
   SetupCallbacks();

   pspDebugScreenInit();
   pspDebugScreenSetXY(0, 0);
   pspDebugScreenPrintf("libTremor test");
   pspDebugScreenSetXY(0, 25);
   pspDebugScreenPrintf("Press X to exit");

   SceCtrlData pad;

   //Apro il file OGG:
   OGG_file = fopen("ms0:/test.ogg", "r");
   if ((OGG_file) != NULL) {
      ov_open(OGG_file, &OGG_VorbisFile, NULL, 0);
   }else{
      pspDebugScreenSetXY(0, 10);
      pspDebugScreenPrintf("Error opening file\n");
      return -1;
   }

    //Stampo le informazioni sul file:
   vorbis_info *vi = ov_info(&OGG_VorbisFile, -1);
   pspDebugScreenSetXY(0, 2);
   pspDebugScreenPrintf("Channels   : %d\n", vi->channels);
   pspDebugScreenPrintf("Sample rate: %ld Hz\n", vi->rate);
   pspDebugScreenPrintf("Bitrate    : %ld kBit\n", vi->bitrate_nominal/1000);

   int h = 0;
   int m = 0;
   int s = 0;
   char dest[9];
   long secs = (long)ov_time_total(&OGG_VorbisFile, -1)/1000;
   h = secs / 3600;
   m = (secs - h * 3600) / 60;
   s = secs - h * 3600 - m * 60;
   snprintf(dest, sizeof(dest), "%2.2i:%2.2i:%2.2i", h, m, s);
   pspDebugScreenPrintf("Length     : %s\n", dest);

    //Reserve audio channel:
    OGG_audio_channel = sceAudioChReserve(OGG_audio_channel, OUTPUT_BUFFER/4, PSP_AUDIO_FORMAT_STEREO);

    //Start buffer filling thread:
    int bufferThid = sceKernelCreateThread("bufferFilling", fillBuffer, 0x16, 0x1800, 0, NULL);
    if(bufferThid < 0)
        return -1;
   sceKernelStartThread(bufferThid, 0, NULL);
   
    //Creates semaphores:
    bufferEmpty = sceKernelCreateSema("bufferEmpty", 0, 1, 1, 0);
    bufferFull = sceKernelCreateSema("bufferFull", 0, 0, 1, 0);

    //Start audio output thread:
    int audioThid = sceKernelCreateThread("audioOutput", audioOutput, 0x18, 0x1800, 0, NULL);
    if(audioThid < 0)
        return -1;
   sceKernelStartThread(audioThid, 0, NULL);

   while(runningFlag){
      sceCtrlReadBufferPositive(&pad, 1);
      if(pad.Buttons & PSP_CTRL_CROSS){
         break;
      }
   }

    //Release chennale:
    sceAudioChRelease(OGG_audio_channel);
   sceKernelExitGame();
   return 0;
}


Ciaooo
Sakya
Back to top
View user's profile Send private message Visit poster's website
Viper8896



Joined: 26 Jan 2006
Posts: 110

PostPosted: Fri Sep 14, 2007 11:40 pm    Post subject: Reply with quote

i was just about to say that ovread doesnt allways read the specified amount and that was the problem with the choppyness. the length u pass in is just a maximum. you need to create the code to sort the data out but if u dont 512 bytes (128 16 bit stereo samples) is a good amount with the least erros in sound.
Back to top
View user's profile Send private message
J.F.



Joined: 22 Feb 2004
Posts: 2906

PostPosted: Sat Sep 15, 2007 6:33 am    Post subject: Reply with quote

sakya wrote:
Hi! :)

Ooops, in the previous source I forgot to create the semaphores! ;)

Many thanks for your answer, it's usefull. :)
I think the problem is that ov_read() doesen't return always 4096, because "ov_read() will decode at most one vorbis packet per invocation, so the value returned will generally be less than length."

I updated my code (I just jump if decoded bytes != 4096).
The audio is bad (obviously: I'm not outputting all the data) but sounds better then before).


I noticed you always assumed you got 4096 last time, but I wanted to get the semaphore issue fixed first. :)

Be sure to ask if you have trouble on that part. I'm rather interested in the result since playing ogg files could be used in a number of different apps.
Back to top
View user's profile Send private message AIM Address
Viper8896



Joined: 26 Jan 2006
Posts: 110

PostPosted: Sat Sep 15, 2007 6:58 am    Post subject: Reply with quote

J.F. wrote:
. I'm rather interested in the result since playing ogg files could be used in a number of different apps.


im just trying to learn how to do this and so is he. there isn't anything new about what we are both learning go here for one of many examples http://svn.ps2dev.org/filedetails.php?repname=pspware&path=%2Ftrunk%2FPSPMediaCenter%2Fcodec%2Fogg%2Foggplayer.c&rev=0&sc=0
im just assuming that the above program actually works without errors and never got round to trying it.

also sdl mixer works and my program with that woks perfectly but i just wanted to use vorbis more directly and that also means my program will be free of any GNU as the ogg/vorbis/tremor are all on a BSD license.

and finally while im talking about GNU license issues is it possible to dynamically link the SDL mixer library or any of the SDL library on the PSP.

edit:
for anyone else having the same problem the solution is to simply call
Code:
sceAudioSetChannelDataLen(AudioChannel, bytes_decoded/4);
before every
Code:
sceAudioOutputBlocking
Back to top
View user's profile Send private message
sakya



Joined: 28 Apr 2006
Posts: 190

PostPosted: Sat Sep 15, 2007 7:38 pm    Post subject: Reply with quote

Hi! :)

Viper8896 wrote:
for anyone else having the same problem the solution is to simply call
Code:
sceAudioSetChannelDataLen(AudioChannel, bytes_decoded/4);
before every
Code:
sceAudioOutputBlocking

Oh, many thanks! Never heard of sceAudioSetChannelDataLen...I was writing code to manage the data...now is very simple. :)

Here's the code that correctly plays the file ms0:/test.ogg

Code:
#include <pspkernel.h>
#include <pspctrl.h>
#include <pspdebug.h>
#include <pspaudio.h>
#include <psppower.h>
#include <pspdisplay.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "tremor/ivorbiscodec.h"
#include "tremor/ivorbisfile.h"

PSP_MODULE_INFO("libTremor Example", 0x1000, 1, 1);
PSP_MAIN_THREAD_ATTR(0);

#define OUTPUT_BUFFER 4*1024

/////////////////////////////////////////////////////////////////////////////////////////
//Globals
/////////////////////////////////////////////////////////////////////////////////////////
int runningFlag = 1;
OggVorbis_File OGG_VorbisFile;
int OGG_eos = 0;
int OGG_audio_channel = 0;
char pcmout[OUTPUT_BUFFER];        //for ov_read
char pcmoutBlock[OUTPUT_BUFFER];   //for real output
int bitStream;
static FILE *OGG_file = 0;

int bufferEmpty;
int bufferFull;
long bytesRed = 0;

/////////////////////////////////////////////////////////////////////////////////////////
//Callbacks
/////////////////////////////////////////////////////////////////////////////////////////
/* Exit callback */
int exit_callback(int arg1, int arg2, void *common) {
   //sceKernelExitGame();
    runningFlag = 0;
    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;
}

/////////////////////////////////////////////////////////////////////////////////////////
//Buffer filling
/////////////////////////////////////////////////////////////////////////////////////////
int fillBuffer(SceSize args, void *argp){

    bufferEmpty = sceKernelCreateSema("bufferEmpty", 0, 1, 1, 0);
    while(runningFlag){
        sceKernelWaitSema(bufferEmpty, 1, 0);
      bytesRed = ov_read(&OGG_VorbisFile,pcmout,sizeof(pcmout),&bitStream);
      switch (bytesRed){
      case 0:
         //EOF:
         OGG_eos = 1;
         ov_clear(&OGG_VorbisFile);
         sceKernelSignalSema(bufferFull, 1);
         break;      
      case OV_HOLE:
      case OV_EBADLINK:
         sceKernelSignalSema(bufferEmpty, 1);
         break;
      default:
         sceKernelSignalSema(bufferFull, 1);
         break;
      }
    }
    return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
//Audio output
/////////////////////////////////////////////////////////////////////////////////////////
int audioOutput(SceSize args, void *argp){
   bufferFull = sceKernelCreateSema("bufferFull", 0, 0, 1, 0);
   while(runningFlag){
        sceKernelWaitSema(bufferFull, 1, 0);
        if (OGG_eos)
            break;
      memcpy(pcmoutBlock,pcmout,OUTPUT_BUFFER);
      sceKernelSignalSema(bufferEmpty, 1);
      sceAudioSetChannelDataLen(OGG_audio_channel, bytesRed/4);
        sceAudioOutputBlocking(OGG_audio_channel, PSP_AUDIO_VOLUME_MAX, pcmoutBlock);
    }
    return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
//Main
/////////////////////////////////////////////////////////////////////////////////////////
int main() {
   pspDebugScreenInit();
   SetupCallbacks();

   pspDebugScreenInit();
   pspDebugScreenSetXY(0, 0);
   pspDebugScreenPrintf("libTremor test");
   pspDebugScreenSetXY(0, 25);
   pspDebugScreenPrintf("Press X to exit");

   SceCtrlData pad;

   //Apro il file OGG:
   OGG_file = fopen("ms0:/test.ogg", "r");
   if ((OGG_file) != NULL) {
      ov_open(OGG_file, &OGG_VorbisFile, NULL, 0);
   }else{
      pspDebugScreenSetXY(0, 10);
      pspDebugScreenPrintf("Error opening file\n");
      return -1;
   }

    //Stampo le informazioni sul file:
   vorbis_info *vi = ov_info(&OGG_VorbisFile, -1);
   pspDebugScreenSetXY(0, 2);
   pspDebugScreenPrintf("Channels   : %d\n", vi->channels);
   pspDebugScreenPrintf("Sample rate: %ld Hz\n", vi->rate);
   pspDebugScreenPrintf("Bitrate    : %ld kBit\n", vi->bitrate_nominal/1000);

   int h = 0;
   int m = 0;
   int s = 0;
   char dest[9];
   long secs = (long)ov_time_total(&OGG_VorbisFile, -1)/1000;
   h = secs / 3600;
   m = (secs - h * 3600) / 60;
   s = secs - h * 3600 - m * 60;
   snprintf(dest, sizeof(dest), "%2.2i:%2.2i:%2.2i", h, m, s);
   pspDebugScreenPrintf("Length     : %s\n", dest);

    //Reserve audio channel:
    OGG_audio_channel = sceAudioChReserve(OGG_audio_channel, OUTPUT_BUFFER/4, PSP_AUDIO_FORMAT_STEREO);

    //Start buffer filling thread:
    int bufferThid = sceKernelCreateThread("bufferFilling", fillBuffer, 0x12, 0x10000, PSP_THREAD_ATTR_USER, NULL);
    if(bufferThid < 0)
        return -1;
   sceKernelStartThread(bufferThid, 0, NULL);

    //Start audio output thread:
    int audioThid = sceKernelCreateThread("audioOutput", audioOutput, 0x16, 0x1800, PSP_THREAD_ATTR_USER, NULL);
    if(audioThid < 0)
        return -1;
   sceKernelStartThread(audioThid, 0, NULL);

   while(runningFlag && !OGG_eos){
        //Print timestring:
        secs = (long) ov_time_tell(&OGG_VorbisFile)/1000;
        h = secs / 3600;
        m = (secs - h * 3600) / 60;
        s = secs - h * 3600 - m * 60;
        snprintf(dest, sizeof(dest), "%2.2i:%2.2i:%2.2i", h, m, s);
        pspDebugScreenSetXY(0, 6);
        pspDebugScreenPrintf("Current    : %s\n", dest);

      sceCtrlReadBufferPositive(&pad, 1);
      if(pad.Buttons & PSP_CTRL_CROSS){
         break;
      }
        sceKernelDelayThread(10000);
   }
   
    if (OGG_eos){
        pspDebugScreenSetXY(0, 7);
        pspDebugScreenPrintf("EOF");
        sceKernelDelayThread(400000);
    }

    //Release channel:
    sceAudioChRelease(OGG_audio_channel);
   sceKernelExitGame();
   return 0;
}


Many thanks again. :)
Ciaooo
Sakya


Last edited by sakya on Wed Sep 19, 2007 5:17 pm; edited 1 time in total
Back to top
View user's profile Send private message Visit poster's website
TakutoKaneshiro



Joined: 07 Sep 2007
Posts: 4

PostPosted: Wed Sep 19, 2007 3:41 pm    Post subject: Reply with quote

sakya, are you sure that creating semaphores must be after using them in buffer filling thread? Its dont work for me.

Anyway, this is really helpful to understanding PSP audio thread.
Back to top
View user's profile Send private message
sakya



Joined: 28 Apr 2006
Posts: 190

PostPosted: Wed Sep 19, 2007 5:20 pm    Post subject: Reply with quote

Hi! :)

TakutoKaneshiro wrote:
sakya, are you sure that creating semaphores must be after using them in buffer filling thread? Its dont work for me.

You're right, sorry.
I've updated the code (I hope it's correct I didn't compile it)...strange it was working for me. :)

Ciaooo
Sakya
Back to top
View user's profile Send private message Visit poster's website
Viper8896



Joined: 26 Jan 2006
Posts: 110

PostPosted: Wed Sep 19, 2007 9:41 pm    Post subject: Reply with quote

im glad im not the only one having sema troubles because if you go to this link it http://psp.jim.sh/pspsdk-doc/group__ThreadMan.html#geaf949e2a8ef3a310e1a22efed160b7b it says that the signal is to increment the sema meaning a signal of 1 should also be used to reset it back to 0 as long as you set the max length to 1 in the sema creation: http://psp.jim.sh/pspsdk-doc/group__ThreadMan.html#ge397ac75be4fa05f1825e625089abbe8
Back to top
View user's profile Send private message
sakya



Joined: 28 Apr 2006
Posts: 190

PostPosted: Wed Sep 19, 2007 10:53 pm    Post subject: Reply with quote

Hi! :)
Viper8896 wrote:
im glad im not the only one having sema troubles because if you go to this link it http://psp.jim.sh/pspsdk-doc/group__ThreadMan.html#geaf949e2a8ef3a310e1a22efed160b7b it says that the signal is to increment the sema meaning a signal of 1 should also be used to reset it back to 0 as long as you set the max length to 1 in the sema creation: http://psp.jim.sh/pspsdk-doc/group__ThreadMan.html#ge397ac75be4fa05f1825e625089abbe8

Many thanks for this clarification. :)
I'm asking how could the program work with these errors in semaphores...

Ciaooo
Sakya
Back to top
View user's profile Send private message Visit poster's website
Viper8896



Joined: 26 Jan 2006
Posts: 110

PostPosted: Wed Sep 19, 2007 11:43 pm    Post subject: Reply with quote

sakya wrote:


Code:
....
int fillBuffer(SceSize args, void *argp){

    bufferEmpty = sceKernelCreateSema("bufferEmpty", 0, 1, 1, 0);
    while(runningFlag){
        sceKernelWaitSema(bufferEmpty, 1, 0);
      bytesRed = ov_read(&OGG_VorbisFile,pcmout,sizeof(pcmout),&bitStream);
      switch (bytesRed){
      case 0:
         //EOF:
         OGG_eos = 1;
         ov_clear(&OGG_VorbisFile);
         sceKernelSignalSema(bufferFull, 1);
         break;      
      case OV_HOLE:
      case OV_EBADLINK:
         sceKernelSignalSema(bufferEmpty, 1);
         break;
      default:
         sceKernelSignalSema(bufferFull, 1);
         break;
      }
    }
    return 0;
}




u dont need to create the semas within the thread just make sure that no wait/poll/set/delete calls are made on any semas that have not yet been created.

and for all your other errors and problems you might have: http://xiph.org/vorbis/doc/vorbisfile/threads.html. basically it just keeps saying only one thread can use &OGG_VorbisFile. you are using it in buffer filling and in your main thread because you just got lucky with all the timming. put all that time string code in the buffer filling thread as well. also i would like to point out do use bytesRed/4 just like that because if the buffer filling thread over takes and the next bytesRed might be a lot different than what it should be for the samples you have in pcmoutBlock
Back to top
View user's profile Send private message
J.F.



Joined: 22 Feb 2004
Posts: 2906

PostPosted: Thu Sep 20, 2007 8:06 am    Post subject: Reply with quote

sakya wrote:
Hi! :)

TakutoKaneshiro wrote:
sakya, are you sure that creating semaphores must be after using them in buffer filling thread? Its dont work for me.

You're right, sorry.
I've updated the code (I hope it's correct I didn't compile it)...strange it was working for me. :)

Ciaooo
Sakya


That's still not quite right... you should creat both semaphores before starting either thread since both threads mess with both semaphores.

I'd put the creation of both semaphores either right before or right after the channel reserve command.
Back to top
View user's profile Send private message AIM Address
J.F.



Joined: 22 Feb 2004
Posts: 2906

PostPosted: Tue Sep 25, 2007 11:57 am    Post subject: Reply with quote

Using the bulk of the info here, I made a simple ogg player for 3.xx firmware (runs fine on my slim). It still plays the last buffer of sound if you stop one song and start another, but I wasn't too worried about that. The main things about this player - showing how to make sure your app works on the slim, and the file requestor. The commented out lines in the makefile, the main.c includes, and the buffer fill thread are for using ogg-vorbis instead of tremor. In the file requester, press X to select a file or enter a directory (they're shown in [ ] ), triangle to go back one directory level, and O to cancel.

EDIT: fixed a bug when navigating up or left in the file requester, and implemented double-buffering to get rid of data copying in the audio callback. Added volume control - press LTRIGGER or RTRIGGER during playback to change the volume (needs some key debounce still).

EDIT: Fixed noise in playback. Seems that setting the length each time through the audio fill loop can cause noise. Don't do it! Pass the same amount of data every time and it'll be noise free. If you look at the fillbuffer routine, you'll notice I now ov_read() until I have precisely as many samples as we need each time. This gives you noise-free playback.

makefile
Code:
TARGET = OggPlayer
OBJS = main.o reqfile.o

INCDIR =
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)

BUILD_PRX = 1
PSP_FW_VERSION = 371

LIBDIR =
LIBS = -lvorbisidec -lvorbis -logg -lpspaudiolib -lpspaudio -lpsppower
#LIBS = -lvorbisfile -lvorbis -logg -lpspaudiolib -lpspaudio -lpsppower -lm
LDFLAGS =

EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = Simple Ogg Player
PSP_EBOOT_ICON="icon0.png"
#PSP_EBOOT_PIC1="pic1.png"
#PSP_EBOOT_SND0="snd0.at3"

PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak


main.c
Code:
#include <pspkernel.h>
#include <pspctrl.h>
#include <pspdebug.h>
#include <pspaudio.h>
#include <pspaudiolib.h>
#include <psppower.h>
#include <pspdisplay.h>
#include <string.h>
#include <stdio.h>

#include <tremor/ivorbiscodec.h>
#include <tremor/ivorbisfile.h>
//#include <vorbis/codec.h>
//#include <vorbis/vorbisfile.h>

#define printf pspDebugScreenPrintf

#define VERS    1
#define REVS    0

extern char *RequestFile(char *);

PSP_MODULE_INFO("OggPlayer", 0, VERS, REVS);
PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER);
PSP_HEAP_SIZE_KB(2500);

#define OUTPUT_BUFFER 8192  // must be less than PSP_AUDIO_SAMPLE_MAX*4

/////////////////////////////////////////////////////////////////////////////////////////
//Globals
/////////////////////////////////////////////////////////////////////////////////////////

int runningFlag;
int bufferEmpty;
int bufferFull;

int audioVol = PSP_AUDIO_VOLUME_MAX;

char pcmout1[OUTPUT_BUFFER];
char pcmout2[OUTPUT_BUFFER];
long pcmlen1, pcmlen2;
int bufferFlip = 0;

OggVorbis_File OGG_VorbisFile;
int OGG_eos = 0;
int OGG_audio_channel = 0;
int bitStream;
static FILE *OGG_file = 0;

/////////////////////////////////////////////////////////////////////////////////////////
//Callbacks
/////////////////////////////////////////////////////////////////////////////////////////

/* 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, PSP_THREAD_ATTR_USER, 0);
   if(thid >= 0) {
      sceKernelStartThread(thid, 0, 0);
   }

   return thid;
}

/////////////////////////////////////////////////////////////////////////////////////////
//Buffer filling
/////////////////////////////////////////////////////////////////////////////////////////
int fillBuffer(SceSize args, void *argp)
{
   int bytes, bytesRed;
   int fillbuf;

   while(runningFlag){
      sceKernelWaitSema(bufferEmpty, 1, 0);
      if (OGG_eos || !runningFlag)
         break;
      bytesRed = 0;
      fillbuf = bufferFlip ? (int)pcmout2 : (int)pcmout1;
      while (bytesRed < OUTPUT_BUFFER && runningFlag && !OGG_eos)
      {
         bytes = ov_read(&OGG_VorbisFile,(char *)(fillbuf+bytesRed),OUTPUT_BUFFER-bytesRed,&bitStream);
         //bytes = ov_read(&OGG_VorbisFile,(char *)(fillbuf+bytesRed),OUTPUT_BUFFER-bytesRed,0,2,1,&bitStream);
         if (bytes == 0)
         {
            //EOF:
            OGG_eos = 1;
            ov_clear(&OGG_VorbisFile);
         }
         else if (bytes > 0)
         {
            bytesRed += bytes;
         }
      }
      if (bufferFlip)
         pcmlen2 = bytesRed;
      else
         pcmlen1 = bytesRed;
      sceKernelSignalSema(bufferFull, 1);
   }
   sceKernelExitDeleteThread(0);
   return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
//Audio output
/////////////////////////////////////////////////////////////////////////////////////////
int audioOutput(SceSize args, void *argp)
{
   int playlen;
   char *playbuf;

   while(runningFlag)
   {
      sceKernelWaitSema(bufferFull, 1, 0);
      if (OGG_eos || !runningFlag)
         break;
      //playlen = bufferFlip ? pcmlen1 : pcmlen2;
      playbuf = bufferFlip ? pcmout1 : pcmout2;
      bufferFlip ^= 1;
      sceKernelSignalSema(bufferEmpty, 1);
      //sceAudioSetChannelDataLen(OGG_audio_channel, playlen/4);
      sceAudioOutputBlocking(OGG_audio_channel, audioVol, playbuf);
   }
   sceKernelExitDeleteThread(0);
   return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
//Main
/////////////////////////////////////////////////////////////////////////////////////////
int main() {
   char *filename;
   SceCtrlData pad;
   char dest[9];
   long secs;
   int h;
   int m;
   int s;
   vorbis_info *vi;

   pspDebugScreenInit();
   SetupCallbacks();

   //Creates semaphores:
   bufferEmpty = sceKernelCreateSema("bufferEmpty", 0, 1, 1, 0);
   bufferFull = sceKernelCreateSema("bufferFull", 0, 0, 1, 0);

   pspDebugScreenInit();
   pspDebugScreenSetBackColor(0xA0602000);
   pspDebugScreenSetTextColor(0xffffff00);

   audioVol = 0x4000; // half max volume

mloop:
   filename = RequestFile("ms0:/MUSIC/");

   pspDebugScreenClear();
   pspDebugScreenSetXY(25, 1);
   printf("Simple Ogg Player");
   pspDebugScreenSetXY(25, 3);
   printf("CPU: %i BUS: %i", scePowerGetCpuClockFrequency(), scePowerGetBusClockFrequency());
   pspDebugScreenSetXY(21, 4);
   printf("Press X to stop playback");

   printf("\n\n Playing %s\n\n", filename);
   sceKernelDelayThread(2*1000*1000);

   //Apro il file OGG:
   OGG_file = fopen(filename, "r");
   if ((OGG_file) != NULL)
      ov_open(OGG_file, &OGG_VorbisFile, NULL, 0);
   else
   {
      printf(" Error opening file\n");
      sceKernelDelayThread(2*1000*1000);
      goto mloop;
   }

   //Stampo le informazioni sul file:
   vi = ov_info(&OGG_VorbisFile, -1);
   printf(" Channels   : %d\n", vi->channels);
   printf(" Sample rate: %ld Hz\n", vi->rate);
   printf(" Bitrate    : %ld kBit\n", vi->bitrate_nominal/1000);

   h = 0;
   m = 0;
   s = 0;
   secs = (long)ov_time_total(&OGG_VorbisFile, -1)/1000;
   h = secs / 3600;
   m = (secs - h * 3600) / 60;
   s = secs - h * 3600 - m * 60;
   snprintf(dest, sizeof(dest), "%2.2i:%2.2i:%2.2i", h, m, s);
   printf(" Length   : %s\n", dest);

   sceKernelDelayThread(2*1000*1000);

   memset(pcmout1, 0, OUTPUT_BUFFER);
   memset(pcmout2, 0, OUTPUT_BUFFER);
   pcmlen1 = 0;
   pcmlen2 = 0;

   OGG_eos = 0;
   runningFlag = 1;
   sceKernelSignalSema(bufferEmpty, 1);
   sceKernelSignalSema(bufferFull, 0);

   //Reserve audio channel:
   OGG_audio_channel = sceAudioChReserve(OGG_audio_channel, OUTPUT_BUFFER/4, PSP_AUDIO_FORMAT_STEREO);

   //Start buffer filling thread:
   int bufferThid = sceKernelCreateThread("bufferFilling", fillBuffer, 0x12, 0x1800, PSP_THREAD_ATTR_USER, NULL);
   if(bufferThid < 0)
      sceKernelExitGame();
   sceKernelStartThread(bufferThid, 0, NULL);

   //Start audio output thread:
   int audioThid = sceKernelCreateThread("audioOutput", audioOutput, 0x16, 0x1800, PSP_THREAD_ATTR_USER, NULL);
   if(audioThid < 0)
      sceKernelExitGame();
   sceKernelStartThread(audioThid, 0, NULL);

   while(runningFlag && !OGG_eos)
   {
      //Print timestring:
      secs = (long) ov_time_tell(&OGG_VorbisFile)/1000;
      h = secs / 3600;
      m = (secs - h * 3600) / 60;
      s = secs - h * 3600 - m * 60;
      snprintf(dest, sizeof(dest), "%2.2i:%2.2i:%2.2i", h, m, s);
      pspDebugScreenSetXY(1, 16);
      printf("Current    : %s\n", dest);

      sceCtrlReadBufferPositive(&pad, 1);
      if(pad.Buttons & PSP_CTRL_CROSS)
         break; // stop playback

      if (pad.Buttons & PSP_CTRL_LTRIGGER)
         if (audioVol > 0)
            audioVol -= 0x0800; // 16 steps on the audio

      if (pad.Buttons & PSP_CTRL_RTRIGGER)
         if (audioVol < 0x8000)
            audioVol += 0x0800; // 16 steps on the audio

      sceKernelDelayThread(100*1000);
   }

   if (OGG_eos == 1)
      printf("\n\n End of file\n");
   else
      printf("\n\n Playback stopped\n");

   runningFlag = 0; // kill the threads
   sceKernelSignalSema(bufferEmpty, 1);
   sceKernelSignalSema(bufferFull, 1);
   sceKernelDelayThread(1*1000*1000);

   //Release channel:
   sceAudioChRelease(OGG_audio_channel);

   goto mloop;

   return 0; // never reaches here - just supresses compiler warning
}


reqfile.c
Code:
#include <pspkernel.h>
#include <pspctrl.h>
#include <pspdebug.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>


#define printf pspDebugScreenPrintf


#define MAXFILES 1000
#define PAGESIZE 32


static struct fileentries {
   char filename[FILENAME_MAX];
   char path[FILENAME_MAX];
   int flags;
} cdfiles[MAXFILES];

static int maxfiles;


/****************************************************************************
 * get_buttons
 *
 ****************************************************************************/

static unsigned int get_buttons()
{
   SceCtrlData pad;

   sceCtrlReadBufferPositive(&pad, 1);
   return pad.Buttons;
}

/****************************************************************************
 * ParseDirectory
 *
 * Parse the directory, returning the number of files found
 ****************************************************************************/

int parse_dir (char *path)
{
   DIR *dir;
   DIR *test_dir;
   struct dirent *dirent = 0;
   struct stat fstat;
   char file_name[FILENAME_MAX];
   FILE *file;

   maxfiles = 0;
   /* open directory */
   if ( ( dir = opendir( path ) ) == 0 )
      return 0;

   while ( ( dirent = readdir( dir ) ) != 0 )
   {
      if ( dirent->d_name[0] == '.' ) continue;
      /* get stats */
      sprintf( file_name, "%s/%s", path, dirent->d_name );
      if ( stat( file_name, &fstat ) == -1 ) continue;
      /* check directory */
      if ( S_ISDIR( fstat.st_mode ) )
      {
         if ( ( test_dir = opendir( file_name ) ) == 0  ) continue;
         closedir( test_dir );
         memset (&cdfiles[maxfiles], 0, sizeof (struct fileentries));
         strncpy(cdfiles[maxfiles].path, path, FILENAME_MAX);
         cdfiles[maxfiles].path[FILENAME_MAX-1] = 0;
         strncpy(cdfiles[maxfiles].filename, dirent->d_name, FILENAME_MAX);
         cdfiles[maxfiles].filename[FILENAME_MAX-1] = 0;
         cdfiles[maxfiles].flags = 1;
         maxfiles++;
      }
      else
      /* check regular file */
      if ( S_ISREG( fstat.st_mode ) )
      {
         /* test it */
         if ( ( file = fopen( file_name, "r" ) ) == 0 ) continue;
         fclose( file );
         memset (&cdfiles[maxfiles], 0, sizeof (struct fileentries));
         strncpy(cdfiles[maxfiles].path, path, FILENAME_MAX);
         cdfiles[maxfiles].path[FILENAME_MAX-1] = 0;
         strncpy(cdfiles[maxfiles].filename, dirent->d_name, FILENAME_MAX);
         cdfiles[maxfiles].filename[FILENAME_MAX-1] = 0;
         maxfiles++;
      }

      if (maxfiles == MAXFILES)
         break;
   }
   /* close dir */
   closedir( dir );

   return maxfiles;
}

/****************************************************************************
 * ShowFiles
 *
 * Support function for FileSelector
 ****************************************************************************/

void ShowFiles( int offset, int selection )
{
   int i,j;
   char text[69];

   pspDebugScreenClear();

   j = 0;
   for ( i = offset; i < ( offset + PAGESIZE ) && i < maxfiles ; i++ )
   {
      if ( cdfiles[i].flags )
      {
         strcpy(text,"[");
         strncat(text, cdfiles[i].filename,66);
         strcat(text,"]");
      }
      else
         strncpy(text, cdfiles[i].filename, 68);

      text[68]=0;

      pspDebugScreenSetTextColor(j == ( selection - offset ) ? 0xbbbbbb00 : 0xffffff00);
      pspDebugScreenSetXY((68 - strlen(text)) / 2, i - offset + 1);
      printf("%s", text);

      j++;
   }
   pspDebugScreenSetTextColor(0xffffff00);
}

/****************************************************************************
 * FileSelector
 *
 * Press X to select, O to cancel, and Triangle to go back a level
 ****************************************************************************/

int FileSelector()
{
   int offset = 0;
   int selection = 0;
   int havefile = 0;
   int redraw = 1;
   unsigned int p = get_buttons();

   while ( havefile == 0 && !(p & PSP_CTRL_CIRCLE) )
   {
      if ( redraw )
         ShowFiles( offset, selection );
      redraw = 0;

      while (!(p = get_buttons()))
         sceKernelDelayThread(10000);
      while (p == get_buttons())
         sceKernelDelayThread(10000);

      if ( p & PSP_CTRL_DOWN )
      {
         selection++;
         if ( selection == maxfiles )
            selection = offset = 0;   // wrap around to top

         if ( ( selection - offset ) == PAGESIZE )
            offset += PAGESIZE; // next "page" of entries

         redraw = 1;
      }

      if ( p & PSP_CTRL_UP )
      {
         selection--;
         if ( selection < 0 )
         {
            selection = maxfiles - 1;
            offset = maxfiles > PAGESIZE ? selection - PAGESIZE + 1 : 0; // wrap around to bottom
         }

         if ( selection < offset )
         {
            offset -= PAGESIZE; // previous "page" of entries
            if ( offset < 0 )
               offset = 0;
         }

         redraw = 1;
      }

      if ( p & PSP_CTRL_RIGHT )
      {
         selection += PAGESIZE;
         if ( selection >= maxfiles )
            selection = offset = 0;   // wrap around to top

         if ( ( selection - offset ) >= PAGESIZE )
            offset += PAGESIZE; // next "page" of entries

         redraw = 1;
      }

      if ( p & PSP_CTRL_LEFT )
      {
         selection -= PAGESIZE;
         if ( selection < 0 )
         {
            selection = maxfiles - 1;
            offset = maxfiles > PAGESIZE ? selection - PAGESIZE + 1 : 0; // wrap around to bottom
         }

         if ( selection < offset )
         {
            offset -= PAGESIZE; // previous "page" of entries
            if ( offset < 0 )
               offset = 0;
         }

         redraw = 1;
      }

      if ( p & PSP_CTRL_CROSS )
      {
         if ( cdfiles[selection].flags )   /*** This is directory ***/
         {
            char fname[FILENAME_MAX+FILENAME_MAX];

            strncpy(fname, cdfiles[selection].path, FILENAME_MAX);
            fname[FILENAME_MAX-1] = 0;
            strncat(fname, cdfiles[selection].filename, FILENAME_MAX);
            fname[FILENAME_MAX+FILENAME_MAX-2] = 0;
            strcat(fname, "/");
            offset = selection = 0;
            parse_dir(fname);
         }
         else
            return selection;

         redraw = 1;
      }

      if ( p & PSP_CTRL_TRIANGLE )
      {
         char fname[FILENAME_MAX];
         int pathpos = strlen(cdfiles[1].path) - 2;

         while (pathpos > 5)
         {
            if (cdfiles[1].path[pathpos] == '/') break;
            pathpos--;
         }
         if (pathpos < 5) pathpos = 5; /** handle root case */
         strncpy(fname, cdfiles[1].path, pathpos+1);
         fname[pathpos+1] = 0;
         offset = selection = 0;
         parse_dir(fname);

         redraw = 1;
      }
   }

   return -1; // no file selected
}

/****************************************************************************
 * RequestFile
 *
 * return pointer to filename selected
 ****************************************************************************/

char *RequestFile (char *initialPath)
{
   int selection;
   static char fname[FILENAME_MAX+FILENAME_MAX];

   if (!parse_dir(initialPath))
      return 0;

   selection = FileSelector ();
   if (selection < 0)
      return 0;

   strncpy (fname, cdfiles[selection].path, FILENAME_MAX);
   fname[FILENAME_MAX-1] = 0;
   strncat (fname, cdfiles[selection].filename, FILENAME_MAX);
   fname[FILENAME_MAX+FILENAME_MAX-1] = 0;

   return fname;
}
Back to top
View user's profile Send private message AIM Address
J.F.



Joined: 22 Feb 2004
Posts: 2906

PostPosted: Thu Sep 27, 2007 8:12 pm    Post subject: Reply with quote

Bumped because I got the playback fixed for noise-free playing. Don't play different numbers of samples each time through the loop! Setting the audio channel length creates noise. Oddly, it's not random noise. The noise is different for different lengths being set, so you get different noise for every song, but it's the exact same every time you play a particular song. Strange, huh?

So now the source in my post above this one plays the same number of samples every time through the loop. Look at the fillbuffer routine to see how to get a precise number of samples from ogg/tremor.
Back to top
View user's profile Send private message AIM Address
Viper8896



Joined: 26 Jan 2006
Posts: 110

PostPosted: Thu Sep 27, 2007 9:07 pm    Post subject: Reply with quote

the way i got it to work was to not use sceAudioSetChannelDataLen but just make sure the fill buffer thread actually fills it up. i wasted a lot of time trying to figure out the correct timing so each thread wouldn't wait on the other and now it out puts perfectly. i decided to use 1 event flag, i prefer them over semas.
Back to top
View user's profile Send private message
J.F.



Joined: 22 Feb 2004
Posts: 2906

PostPosted: Thu Sep 27, 2007 10:41 pm    Post subject: Reply with quote

Viper8896 wrote:
the way i got it to work was to not use sceAudioSetChannelDataLen but just make sure the fill buffer thread actually fills it up. i wasted a lot of time trying to figure out the correct timing so each thread wouldn't wait on the other and now it out puts perfectly. i decided to use 1 event flag, i prefer them over semas.


Yeah, that's what I'm doing now - making sure the fillbuffer fills the buffer completely (unless at the end of the file). The two semaphores works fine for me, and it's a very simple way to make two threads wait for each other. I wouldn't mind seeing your event code though. :)
Back to top
View user's profile Send private message AIM Address
Viper8896



Joined: 26 Jan 2006
Posts: 110

PostPosted: Fri Sep 28, 2007 8:53 pm    Post subject: Reply with quote

J.F. wrote:
...I wouldn't mind seeing your event code though. :)


here it is. the "kernel_stuff" is a prx it loads to set the correct frequency(44100/48000).

Code:
/*
 * Vorbis PLAYER
 */

/* includes */
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include <pspiofilemgr.h>
#include <tremor/ivorbisfile.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <pspaudio.h>
#include <kubridge.h>

/* module setup */
PSP_MODULE_INFO("vorbis-ng", 0, 0, 0);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
PSP_HEAP_SIZE_KB(1024*4);

/* globals */
int fd;
OggVorbis_File ovf;
int current_section;
ov_callbacks ogg_callbacks;
int AudioChannel;
ov_callbacks ogg_callbacks;
char EOS;
char VorbLoaded = 0;
int Output_thid;
int Fill_thid;
char Play;
int bufferFlag;
vorbis_info *vi;
char shouldExit;
char ogg_file[256];
char ChannelCount;

long TimeNow;
long TimeTotal;

/* definitions */
#define printf pspDebugScreenPrintf
#define amountOfBytes 1024*4
#define SampleSize 2
#define SamplesPerOutput ((amountOfBytes/SampleSize)/ChannelCount)

typedef struct
{
   long size;
   char pcmout[amountOfBytes];
}buffer;

buffer *buffer0;
buffer *buffer1;

/* function prototypes */
int VorbClear();
int VorbLoad();
int set_audio_freq(int devkitVersion, int frequency);

/* Exit callback */
int exit_callback(int arg1, int arg2, void *common)
{
   VorbClear();
   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;
}

/* vorbis call backs */
size_t ogg_callback_read(void *ptr, size_t size, size_t nmemb, void *datasource)
{
    return sceIoRead(*(int *) datasource, ptr, size * nmemb);
}
int ogg_callback_seek(void *datasource, ogg_int64_t offset, int whence)
{
    return sceIoLseek32(*(int *) datasource, (unsigned int) offset, whence);
}
long ogg_callback_tell(void *datasource)
{
    return sceIoLseek32(*(int *) datasource, 0, SEEK_CUR);
}
int ogg_callback_close(void *datasource)
{
    return sceIoClose(*(int *) datasource);
}

/* swap input buffer with output buffer */
int swapBuffers()
{
   buffer *tempPtr;
   tempPtr = buffer0;
   buffer0 = buffer1;
   buffer1 = tempPtr;
   return 0;
}

/* thread to fill buffer */
int FillBuffer(SceSize args, void *argp)
{
   long bytes_decoded;
   do
   {
      sceKernelWaitEventFlag(bufferFlag,1,PSP_EVENT_WAITCLEAR,0,0);
      if(shouldExit)break;
      swapBuffers();
      sceKernelSetEventFlag(bufferFlag,2);
      buffer0->size=0;
      TimeNow = ov_time_tell(&ovf);
      do
      {
         bytes_decoded =   ov_read(&ovf,buffer0->pcmout+buffer0->size,(amountOfBytes-buffer0->size),&current_section);
         if(bytes_decoded<=0)break;
         buffer0->size += bytes_decoded;
      }while(buffer0->size<amountOfBytes);
   }while(buffer0->size>0);
   sceKernelWaitEventFlag(bufferFlag,1,PSP_EVENT_WAITCLEAR,0,0);
   swapBuffers();
   sceKernelSetEventFlag(bufferFlag,2);
   return 0;
}

/* thread to output buffer */
int OutputBuffer(SceSize args, void *argp)
{
   //int i;
      
   while(buffer1->size>0)
   {
      //for(i=0;i<buffer1->size;i=(i+((SamplesPerOutput*SampleSize)*ChannelCount)))
      //{
         //sceAudioSetChannelDataLen(AudioChannel, buffer1->size/4);
         sceAudioOutputBlocking(AudioChannel, 0x4000, buffer1->pcmout);
      //}
      sceKernelSetEventFlag(bufferFlag,1);
      sceKernelWaitEventFlag(bufferFlag,2,PSP_EVENT_WAITCLEAR,0,0);
      if(shouldExit)break;
      while(!Play)sceKernelDelayThread(100000);
   }
   Play=0;
   EOS=1;
   return 0;
}

int VorbPlay()
{
   if(!VorbLoaded)
   {
      return -1;
   }
   if(EOS) //if ended re-open
   {
      VorbClear();
      VorbLoad(ogg_file);
   }
   Play=1;
   return 0;
}

int VorbStop()
{
   if(!VorbLoaded)
   {
      return -1;
   }
   Play=0;
   return 0;
}

int VorbLoad(char file[256])
{
   if(VorbLoaded)
   {
      return -1;
   }
   strcpy(ogg_file,file);
   EOS=0;
   Play=0;
   shouldExit=0;
   ogg_callbacks.read_func = ogg_callback_read;
   ogg_callbacks.seek_func = ogg_callback_seek;
   ogg_callbacks.close_func = ogg_callback_close;
   ogg_callbacks.tell_func = ogg_callback_tell;
   buffer0 = malloc(sizeof(buffer));
   buffer1 = malloc(sizeof(buffer));
   fd=sceIoOpen(ogg_file, PSP_O_RDONLY, 0777);
   ov_open_callbacks(&fd,&ovf,NULL,0, ogg_callbacks);
   vi = ov_info(&ovf, -1);
   ChannelCount = vi->channels;

   /* get this sample rate bull to work please */
   SceUID mod = kuKernelLoadModule("kernel_stuff.prx", 0, NULL);
   printf("value returned by LoadModule: %d ",mod);
   if(mod>0)
   {
      int ret = set_audio_freq(sceKernelDevkitVersion(), vi->rate);
      printf("set_audio_freq: %d\n",ret);
   }
   /* end sample rate code */

   TimeTotal = ov_time_total(&ovf, -1);
   buffer0->size = ov_read(&ovf,buffer0->pcmout,amountOfBytes,&current_section);
   buffer1->size=1;
   AudioChannel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, SamplesPerOutput, ChannelCount==2?PSP_AUDIO_FORMAT_STEREO:PSP_AUDIO_FORMAT_MONO);
   bufferFlag = sceKernelCreateEventFlag("bufferFlag",PSP_EVENT_WAITMULTIPLE,1,0);
   Fill_thid = sceKernelCreateThread("FillBuffer", FillBuffer, 0x18, 0x800, 0, NULL);
   Output_thid = sceKernelCreateThread("OutputBuffer", OutputBuffer, 0x16, 0x800, 0, NULL);
   sceKernelStartThread(Fill_thid, 0, NULL);
   sceKernelWaitEventFlag(bufferFlag,2,0,0,0);
   sceKernelStartThread(Output_thid, 0, NULL);
   VorbLoaded=1;
   return 0;
}

int VorbClear()
{
   if(!VorbLoaded)
   {
      return -1;
   }
   VorbStop();
   shouldExit=1;
   sceKernelSetEventFlag(bufferFlag,3); //take both threads out of waiting so they can pick up exit signal
   sceKernelDelayThread(1000000); //wait for a second to make sure that the threads have finished
   sceKernelDeleteThread(Fill_thid);
   sceKernelDeleteThread(Output_thid);
   ov_clear(&ovf);
   sceKernelDeleteEventFlag(bufferFlag);
   sceAudioChRelease(AudioChannel);
   free(buffer0);
   free(buffer1);
   VorbLoaded=0;
   return 0;
}

long VorbTime(int total)
{
   if(total)
   return TimeTotal;
   return TimeNow;
}

int VorbPlaying()
{
   return Play;
}

int VorbFinished()
{
   return EOS;
}

int updateTimeString()
{
   while(1)
   {
      //Print timestring:
      long secs = VorbTime(0)/1000;
      int h = secs / 3600;
      int m = (secs - h * 3600) / 60;
      int s = secs - h * 3600 - m * 60;
      char time_string[9];
      snprintf(time_string, sizeof(time_string), "%2.2i:%2.2i:%2.2i", h, m, s);
      pspDebugScreenSetXY(0, 8);
      pspDebugScreenPrintf("Current    : %s\n", time_string);
      sceKernelDelayThread(100000);
   }
   return 0;
}

int main()
{
   pspDebugScreenInit();
   SetupCallbacks();

   SceCtrlData pad;

   printf("loading\n\n");

   VorbLoad("fatms:/1.ogg");

   printf("starting. press X at any time to exit or start to pause/resume\n\n");

   VorbPlay();

   
   pspDebugScreenPrintf("Channels   : %d\n", vi->channels);
   pspDebugScreenPrintf("Sample rate: %ld Hz\n", vi->rate);
   pspDebugScreenPrintf("Bitrate    : %ld kBit\n", vi->bitrate_nominal/1000);
   

   long secs = VorbTime(1)/1000;
   int h = secs / 3600;
   int m = (secs - h * 3600) / 60;
   int s = secs - h * 3600 - m * 60;
   char time_string[9];
   snprintf(time_string, sizeof(time_string), "%2.2i:%2.2i:%2.2i", h, m, s);
   pspDebugScreenPrintf("Length     : %s\n", time_string);


   int time_thid = sceKernelCreateThread("TimeThread", updateTimeString, 0x30, 0x1800, 0, NULL);
   sceKernelStartThread(time_thid, 0, NULL);

   while(1)
   {
      sceCtrlReadBufferPositive(&pad, 1);
      if(pad.Buttons & PSP_CTRL_CROSS)break;
      if(pad.Buttons & PSP_CTRL_START)
      {
         if(VorbPlaying())VorbStop();
         else VorbPlay();
         sceKernelDelayThread(1000000);         
      }
   }

   pspDebugScreenSetXY(0, 10);
   printf("finished");

   VorbClear();

   sceKernelDelayThread(1000000);

   sceKernelExitGame();   

   return 0;

} //end main function


Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Development All times are GMT + 10 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group