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

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
Viper8896
Posts: 110
Joined: Thu Jan 26, 2006 6:20 pm

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

Post by Viper8896 »

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: Select all

#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&#40;"vorbis", 0, 1, 1&#41;;

/* Define the main thread's attribute value &#40;optional&#41; */
PSP_MAIN_THREAD_ATTR&#40;THREAD_ATTR_USER | THREAD_ATTR_VFPU&#41;;

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

/* Exit callback */
int exit_callback&#40;int arg1, int arg2, void *common&#41;
&#123;
	sceKernelExitGame&#40;&#41;;

	return 0;
&#125;

/* Callback thread */
int CallbackThread&#40;SceSize args, void *argp&#41;
&#123;
	int cbid;

	cbid = sceKernelCreateCallback&#40;"Exit Callback", exit_callback, NULL&#41;;
	sceKernelRegisterExitCallback&#40;cbid&#41;;

	sceKernelSleepThreadCB&#40;&#41;;

	return 0;
&#125;

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks&#40;void&#41;
&#123;
	int thid = 0;

	thid = sceKernelCreateThread&#40;"update_thread", CallbackThread, 0x11, 0xFA0, 0, 0&#41;;
	if&#40;thid >= 0&#41;
	&#123;
		sceKernelStartThread&#40;thid, 0, 0&#41;;
	&#125;

	return thid;
&#125;

OggVorbis_File ovf;
FILE *fp;
int current_section;

void audioCallback&#40;void* buf, unsigned int length, void *userdata&#41;
&#123;
		ov_read&#40;&ovf,buf,4096,&current_section&#41;;	
&#125;

int main&#40;void&#41;
&#123;
	pspDebugScreenInit&#40;&#41;;
	SetupCallbacks&#40;&#41;;
	fp=fopen&#40;"fatms0&#58;/1.ogg\0", "r"&#41;;
	ov_open&#40;fp,&ovf,NULL,0&#41;;
	vorbis_info *vi = ov_info&#40;&ovf,-1&#41;;
	int vorb_time = ov_time_total&#40;&ovf,-1&#41;;;
	printf&#40;"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&#41;;
	printf&#40;"\n\nattempting to play...\n\n"&#41;;
	pspAudioInit&#40;&#41;;
	pspAudioSetChannelCallback&#40;0, audioCallback, NULL&#41;;
	while&#40;1&#41;
	&#123;
		sceDisplayWaitVblankStart&#40;&#41;;
	&#125;
	return 0;
&#125;
MakeFile

Code: Select all

TARGET = VORBIS-PLAYER
OBJS = main.o

BUILD_PRX=0
PSP_FW_VERSION=150

INCDIR = 
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $&#40;CFLAGS&#41; -fno-exceptions -fno-rtti
ASFLAGS = $&#40;CFLAGS&#41;

LIBDIR =
LDFLAGS =

EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = VORBIS PLAYER

PSPSDK=$&#40;shell psp-config --pspsdk-path&#41;
PSPBIN = $&#40;PSPSDK&#41;/../bin
LIBS += -lpsprtc -lvorbisidec -lvorbis -logg -lpspumd -lpspaudiolib -lpspaudio
include $&#40;PSPSDK&#41;/lib/build.mak
Last edited by Viper8896 on Sat Sep 15, 2007 10:19 am, edited 1 time in total.
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

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.
Viper8896
Posts: 110
Joined: Thu Jan 26, 2006 6:20 pm

Post by Viper8896 »

thanks for your help but still same prob:

main.c

Code: Select all

#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&#40;"vorbis", 0, 1, 1&#41;;

/* Define the main thread's attribute value &#40;optional&#41; */
PSP_MAIN_THREAD_ATTR&#40;THREAD_ATTR_USER | THREAD_ATTR_VFPU&#41;;

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

/* Exit callback */
int exit_callback&#40;int arg1, int arg2, void *common&#41;
&#123;
   sceKernelExitGame&#40;&#41;;

   return 0;
&#125;

/* Callback thread */
int CallbackThread&#40;SceSize args, void *argp&#41;
&#123;
   int cbid;

   cbid = sceKernelCreateCallback&#40;"Exit Callback", exit_callback, NULL&#41;;
   sceKernelRegisterExitCallback&#40;cbid&#41;;

   sceKernelSleepThreadCB&#40;&#41;;

   return 0;
&#125;

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks&#40;void&#41;
&#123;
   int thid = 0;

   thid = sceKernelCreateThread&#40;"update_thread", CallbackThread, 0x11, 0xFA0, 0, 0&#41;;
   if&#40;thid >= 0&#41;
   &#123;
      sceKernelStartThread&#40;thid, 0, 0&#41;;
   &#125;

   return thid;
&#125;

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

int fillbuffer&#40;&#41;
&#123;
	while&#40;1&#41;
	&#123;
		if&#40;needMoBuffer&#41;
		&#123;
			ov_read&#40;&ovf,pcmout,4096, &current_section&#41;;
			needMoBuffer=0;
		&#125;
		else
		&#123;
		sceDisplayWaitVblankStart&#40;&#41;;
		&#125;
	&#125;
&#125;

void audioCallback&#40;void* buf, unsigned int length, void *userdata&#41;
&#123;
	while&#40;needMoBuffer&#41;//wait just incase the next buffer isnt ready
	&#123;
		sceDisplayWaitVblankStart&#40;&#41;;
	&#125;
	memcpy&#40;buf,pcmout,4096&#41;;
	needMoBuffer=1;
&#125;

int main&#40;void&#41;
&#123;
	pspDebugScreenInit&#40;&#41;;
	SetupCallbacks&#40;&#41;;
	pcmout = malloc&#40;4096&#41;;
	if&#40;pcmout==NULL&#41;
	&#123;
		printf&#40;"memory allocation error"&#41;;
		while&#40;1&#41;&#123;sceDisplayWaitVblankStart&#40;&#41;;&#125;
	&#125;
	fp=fopen&#40;"fatms0&#58;/1.ogg\0", "r"&#41;;
	ov_open&#40;fp,&ovf,NULL,0&#41;;
	vorbis_info *vi = ov_info&#40;&ovf,-1&#41;;
	int vorb_time = ov_time_total&#40;&ovf,-1&#41;;;
	printf&#40;"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&#41;;
	printf&#40;"\n\nattempting to play...\n\n"&#41;;
	buffer_thid = 0;
	/* 0x1800 = Initial stack size. Not min but too low and thread fails - it can't load the buffer. */
	buffer_thid = sceKernelCreateThread&#40;"load_more_vorbis", fillbuffer, 0x16, 0x1800, 0, NULL&#41;;
	if&#40;buffer_thid>=0&#41; //if thread created ok start it
	&#123;
		sceKernelStartThread&#40;buffer_thid,0,0&#41;;
	&#125;
	if&#40;buffer_thid<0&#41;
	&#123;
		printf&#40;"thread error"&#41;;
		while&#40;1&#41;&#123;sceDisplayWaitVblankStart&#40;&#41;;&#125;
	&#125;
	sceDisplayWaitVblankStart&#40;&#41;;
	sceDisplayWaitVblankStart&#40;&#41;;
	printf&#40;"initiazlizing audio\n"&#41;;
	pspAudioInit&#40;&#41;;
	printf&#40;"setting audio callback\n\n"&#41;;
	pspAudioSetChannelCallback&#40;0, audioCallback, NULL&#41;;
	while&#40;1&#41;
	&#123;
	sceDisplayWaitVblankStart&#40;&#41;;
	&#125;
	return 0;
&#125;

Makefile

Code: Select all

TARGET = VORBIS-PLAYER
OBJS = main.o

BUILD_PRX=0
PSP_FW_VERSION=150

INCDIR =
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $&#40;CFLAGS&#41; -fno-exceptions -fno-rtti
ASFLAGS = $&#40;CFLAGS&#41;

LIBDIR =
LDFLAGS =

EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = VORBIS PLAYER

PSPSDK=$&#40;shell psp-config --pspsdk-path&#41;
PSPBIN = $&#40;PSPSDK&#41;/../bin
LIBS += -lpsprtc -lvorbisidec -lvorbis -logg -lpspumd -lpspaudiolib -lpspaudio
include $&#40;PSPSDK&#41;/lib/build.mak

J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

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).
Viper8896
Posts: 110
Joined: Thu Jan 26, 2006 6:20 pm

Post by Viper8896 »

ive tried this and i cant get it too work.

Code: Select all

#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&#40;"vorbis", 0, 1, 1&#41;;

/* Define the main thread's attribute value &#40;optional&#41; */
PSP_MAIN_THREAD_ATTR&#40;THREAD_ATTR_USER | THREAD_ATTR_VFPU&#41;;

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

/* Exit callback */
int exit_callback&#40;int arg1, int arg2, void *common&#41;
&#123;
   sceKernelExitGame&#40;&#41;;

   return 0;
&#125;

/* Callback thread */
int CallbackThread&#40;SceSize args, void *argp&#41;
&#123;
   int cbid;

   cbid = sceKernelCreateCallback&#40;"Exit Callback", exit_callback, NULL&#41;;
   sceKernelRegisterExitCallback&#40;cbid&#41;;

   sceKernelSleepThreadCB&#40;&#41;;

   return 0;
&#125;

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks&#40;void&#41;
&#123;
   int thid = 0;

   thid = sceKernelCreateThread&#40;"update_thread", CallbackThread, 0x11, 0xFA0, 0, 0&#41;;
   if&#40;thid >= 0&#41;
   &#123;
      sceKernelStartThread&#40;thid, 0, 0&#41;;
   &#125;

   return thid;
&#125;

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

int fillbuffer&#40;&#41;
&#123;
	while&#40;1&#41;
	&#123;
		sceKernelWaitSema&#40;buffer_semaid, 1, 0&#41;;
		ov_read&#40;&ovf,pcmout,4096, &current_section&#41;;
		sceKernelSignalSema&#40;buffer_semaid, 0&#41;;
	&#125;
&#125;

void audioCallback&#40;void* buf, unsigned int length, void *userdata&#41;
&#123;
	sceKernelWaitSema&#40;buffer_semaid, 0, 0&#41;;
	memcpy&#40;buf,pcmout,4096&#41;;
	sceKernelSignalSema&#40;buffer_semaid, 1&#41;;
&#125;

int main&#40;void&#41;
&#123;
	pspDebugScreenInit&#40;&#41;;
	SetupCallbacks&#40;&#41;;
	pcmout = malloc&#40;4096&#41;;
	if&#40;pcmout==NULL&#41;
	&#123;
		printf&#40;"memory allocation error"&#41;;
		while&#40;1&#41;&#123;sceDisplayWaitVblankStart&#40;&#41;;&#125;
	&#125;
	fp=fopen&#40;"fatms0&#58;/1.ogg\0", "r"&#41;;
	ov_open&#40;fp,&ovf,NULL,0&#41;;
	vorbis_info *vi = ov_info&#40;&ovf,-1&#41;;
	int vorb_time = ov_time_total&#40;&ovf,-1&#41;;;
	printf&#40;"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&#41;;
	printf&#40;"\n\nattempting to play...\n\n"&#41;;
	buffer_semaid = sceKernelCreateSema&#40;"Buffer_Sema", 0, 1, 1, 0&#41;;
	buffer_thid = 0;
	/* 0x1800 = Initial stack size. Not min but too low and thread fails - it can't load the buffer. */
	buffer_thid = sceKernelCreateThread&#40;"load_more_vorbis", fillbuffer, 0x32, 0x1800, 0, NULL&#41;;
	if&#40;buffer_thid>=0&#41; //if thread created ok start it
	&#123;
		sceKernelStartThread&#40;buffer_thid,0,0&#41;;
	&#125;
	if&#40;buffer_thid<0&#41;
	&#123;
		printf&#40;"thread error"&#41;;
		while&#40;1&#41;&#123;sceDisplayWaitVblankStart&#40;&#41;;&#125;
	&#125;
	printf&#40;"initiazlizing audio\n"&#41;;
	pspAudioInit&#40;&#41;;
	printf&#40;"setting audio callback\n\n"&#41;;
	pspAudioSetChannelCallback&#40;0, audioCallback, NULL&#41;;
	while&#40;1&#41;
	&#123;
		sceDisplayWaitVblankStart&#40;&#41;;		
	&#125;
	return 0;
&#125;

J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

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

Code: Select all

int fillbuffer&#40;&#41;
&#123;
   while&#40;1&#41;
   &#123;
      sceKernelWaitSema&#40;empty_semaid, 1, 0&#41;;
      ov_read&#40;&ovf,pcmout,4096, &current_section&#41;;
      sceKernelSignalSema&#40;full_semaid, 1&#41;;
   &#125;
&#125;

void audioCallback&#40;void* buf, unsigned int length, void *userdata&#41;
&#123;
   sceKernelWaitSema&#40;full_semaid, 1, 0&#41;;
   memcpy&#40;buf,pcmout,4096&#41;;
   sceKernelSignalSema&#40;empty_semaid, 1&#41;;
&#125;
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: Select all

int fillbuffer&#40;&#41;
&#123;
   while&#40;1&#41;
   &#123;
      sceKernelWaitSema&#40;empty_semaid, 1, 0&#41;;
      sceKernelSignalSema&#40;empty_semaid, 0&#41;;
      ov_read&#40;&ovf,pcmout,4096, &current_section&#41;;
      sceKernelSignalSema&#40;full_semaid, 1&#41;;
   &#125;
&#125;

void audioCallback&#40;void* buf, unsigned int length, void *userdata&#41;
&#123;
   sceKernelWaitSema&#40;full_semaid, 1, 0&#41;;
   sceKernelSignalSema&#40;full_semaid, 0&#41;;
   memcpy&#40;buf,pcmout,4096&#41;;
   sceKernelSignalSema&#40;empty_semaid, 1&#41;;
&#125;
sakya
Posts: 190
Joined: Fri Apr 28, 2006 5:48 pm
Contact:

Post by sakya »

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: Select all

OGG_audio_channel = sceAudioChReserve&#40;OGG_audio_channel, 1152 , PSP_AUDIO_FORMAT_STEREO&#41;;
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: Select all

sceAudioOutputBlocking&#40;OGG_audio_channel, PSP_AUDIO_VOLUME_MAX, pcmoutBlock&#41;;
Here's my code:

Code: Select all

#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&#40;"libTremor Example", 0, 1, 1&#41;;
#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&#91;4096&#93;;
char pcmoutBlock&#91;4096&#93;;
int bitStream;
static FILE *OGG_file = 0;

int bufferEmpty = 0;
int bufferFull = 0;

/////////////////////////////////////////////////////////////////////////////////////////
//Callbacks
/////////////////////////////////////////////////////////////////////////////////////////
/* Exit callback */
int exit_callback&#40;int arg1, int arg2, void *common&#41; &#123;
	sceKernelExitGame&#40;&#41;;
	return 0;
&#125;

/* Callback thread */
int CallbackThread&#40;SceSize args, void *argp&#41; &#123;
	int cbid;

	cbid = sceKernelCreateCallback&#40;"Exit Callback", exit_callback, NULL&#41;;
	sceKernelRegisterExitCallback&#40;cbid&#41;;
	sceKernelSleepThreadCB&#40;&#41;;
	return 0;
&#125;

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks&#40;void&#41; &#123;
	int thid = 0;

	thid = sceKernelCreateThread&#40;"update_thread", CallbackThread, 0x11, 0xFA0, 0, 0&#41;;
	if&#40;thid >= 0&#41; &#123;
		sceKernelStartThread&#40;thid, 0, 0&#41;;
	&#125;

	return thid;
&#125;

/////////////////////////////////////////////////////////////////////////////////////////
//Buffer filling
/////////////////////////////////////////////////////////////////////////////////////////
int fillBuffer&#40;SceSize args, void *argp&#41;&#123;
    while&#40;1&#41;&#123;
        sceKernelWaitSema&#40;bufferEmpty, 1, 0&#41;; 
        sceKernelSignalSema&#40;bufferEmpty, 0&#41;; 
		long ret=ov_read&#40;&OGG_VorbisFile,pcmout,sizeof&#40;pcmout&#41;,&bitStream&#41;;
		if &#40;ret == 0&#41;&#123;
			//EOF&#58;
            OGG_eos = 1;
            ov_clear&#40;&OGG_VorbisFile&#41;;
            sceKernelSignalSema&#40;bufferFull, 1&#41;;
            break;
        &#125;
        sceKernelSignalSema&#40;bufferFull, 1&#41;;
    &#125;
    return 0;
&#125;

/////////////////////////////////////////////////////////////////////////////////////////
//Audio output
/////////////////////////////////////////////////////////////////////////////////////////
int audioOutput&#40;SceSize args, void *argp&#41;&#123;
    while&#40;1&#41;&#123;
        sceKernelWaitSema&#40;bufferFull, 1, 0&#41;;
        sceKernelSignalSema&#40;bufferFull, 0&#41;;
        if &#40;OGG_eos&#41;
            break;
        memcpy&#40;pcmoutBlock,pcmout,4096&#41;; 
        sceKernelSignalSema&#40;bufferEmpty, 1&#41;;
        sceAudioOutputBlocking&#40;OGG_audio_channel, PSP_AUDIO_VOLUME_MAX, pcmoutBlock&#41;;
    &#125;
    return 0;
&#125;

/////////////////////////////////////////////////////////////////////////////////////////
//Main
/////////////////////////////////////////////////////////////////////////////////////////
int main&#40;&#41; &#123;
	scePowerSetClockFrequency&#40;222, 222, 111&#41;;

	pspDebugScreenInit&#40;&#41;;
	SetupCallbacks&#40;&#41;;

	pspDebugScreenInit&#40;&#41;;
	pspDebugScreenSetXY&#40;0, 0&#41;;
	pspDebugScreenPrintf&#40;"libTremor test"&#41;;
	pspDebugScreenSetXY&#40;0, 25&#41;;
	pspDebugScreenPrintf&#40;"Press X to exit"&#41;;

	SceCtrlData pad;

	//Apro il file OGG&#58;
	OGG_file = fopen&#40;"ms0&#58;/test.ogg", "r"&#41;;
	if &#40;&#40;OGG_file&#41; != NULL&#41; &#123;
		ov_open&#40;OGG_file, &OGG_VorbisFile, NULL, 0&#41;;
	&#125;else&#123;
		pspDebugScreenSetXY&#40;0, 10&#41;;
		pspDebugScreenPrintf&#40;"Error opening file\n"&#41;;
		return -1;
	&#125;

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

	int h = 0;
	int m = 0;
	int s = 0;
	char dest&#91;9&#93;;
	long secs = &#40;long&#41;ov_time_total&#40;&OGG_VorbisFile, -1&#41;/1000;
	h = secs / 3600;
	m = &#40;secs - h * 3600&#41; / 60;
	s = secs - h * 3600 - m * 60;
	snprintf&#40;dest, sizeof&#40;dest&#41;, "%2.2i&#58;%2.2i&#58;%2.2i", h, m, s&#41;;
	pspDebugScreenPrintf&#40;"Length  &#58; %s\n", dest&#41;;

    //Reserve channel&#58;
    OGG_audio_channel = sceAudioChReserve&#40;OGG_audio_channel, 1152 , PSP_AUDIO_FORMAT_STEREO&#41;;

    //Start buffer filling thread&#58;
    int bufferThid = sceKernelCreateThread&#40;"bufferFilling", fillBuffer, 0x18, 0x1800, 0, NULL&#41;;
    if&#40;bufferThid < 0&#41;
        return -1;
	sceKernelStartThread&#40;bufferThid, 0, NULL&#41;;
    sceKernelSignalSema&#40;bufferEmpty, 1&#41;;

    //Start audio output thread&#58;
    int audioThid = sceKernelCreateThread&#40;"audioOutput", audioOutput, 0x18, 0x1800, 0, NULL&#41;;
    if&#40;audioThid < 0&#41;
        return -1;
	sceKernelStartThread&#40;audioThid, 0, NULL&#41;;

	while&#40;1&#41;&#123;
		sceCtrlReadBufferPositive&#40;&pad, 1&#41;;
		if&#40;pad.Buttons & PSP_CTRL_CROSS&#41;&#123;
			break;
		&#125;
	&#125;

    //Release chennale&#58;
    sceAudioChRelease&#40;OGG_audio_channel&#41;;
	sceKernelExitGame&#40;&#41;;
	return 0;
&#125;
Many thanks :)

Ciaooo
Sakya
Viper8896
Posts: 110
Joined: Thu Jan 26, 2006 6:20 pm

Post by Viper8896 »

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?r ... 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.
sakya
Posts: 190
Joined: Fri Apr 28, 2006 5:48 pm
Contact:

Post by sakya »

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: Select all

#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&#40;"libTremor Example", 0x1000, 1, 1&#41;;
PSP_MAIN_THREAD_ATTR&#40;0&#41;;

#define OUTPUT_BUFFER 4*1024

/////////////////////////////////////////////////////////////////////////////////////////
//Globals
/////////////////////////////////////////////////////////////////////////////////////////
int runningFlag = 1;
OggVorbis_File OGG_VorbisFile;
int OGG_eos = 0;
int OGG_audio_channel = 0;
char pcmout&#91;OUTPUT_BUFFER&#93;__attribute__ &#40;&#40;aligned&#40;64&#41;&#41;&#41;;
char pcmoutBlock&#91;OUTPUT_BUFFER&#93;__attribute__ &#40;&#40;aligned&#40;64&#41;&#41;&#41;;
int bitStream;
static FILE *OGG_file = 0;

int bufferEmpty;
int bufferFull;
long bytesRed = 0;

/////////////////////////////////////////////////////////////////////////////////////////
//Callbacks
/////////////////////////////////////////////////////////////////////////////////////////
/* Exit callback */
int exit_callback&#40;int arg1, int arg2, void *common&#41; &#123;
	//sceKernelExitGame&#40;&#41;;
    runningFlag = 0;
    return 0;
&#125;

/* Callback thread */
int CallbackThread&#40;SceSize args, void *argp&#41; &#123;
	int cbid;

	cbid = sceKernelCreateCallback&#40;"Exit Callback", exit_callback, NULL&#41;;
	sceKernelRegisterExitCallback&#40;cbid&#41;;
	sceKernelSleepThreadCB&#40;&#41;;
	return 0;
&#125;

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks&#40;void&#41; &#123;
	int thid = 0;

	thid = sceKernelCreateThread&#40;"update_thread", CallbackThread, 0x11, 0xFA0, 0, 0&#41;;
	if&#40;thid >= 0&#41; &#123;
		sceKernelStartThread&#40;thid, 0, 0&#41;;
	&#125;

	return thid;
&#125;

/////////////////////////////////////////////////////////////////////////////////////////
//Buffer filling
/////////////////////////////////////////////////////////////////////////////////////////
int fillBuffer&#40;SceSize args, void *argp&#41;&#123;
    while&#40;runningFlag&#41;&#123;
        sceKernelWaitSema&#40;bufferEmpty, 1, 0&#41;; 
        sceKernelSignalSema&#40;bufferEmpty, 0&#41;; 
		bytesRed = ov_read&#40;&OGG_VorbisFile,pcmout,sizeof&#40;pcmout&#41;,&bitStream&#41;;
		if &#40;!bytesRed&#41;&#123;
			//EOF&#58;
            OGG_eos = 1;
            ov_clear&#40;&OGG_VorbisFile&#41;;
            sceKernelSignalSema&#40;bufferFull, 1&#41;;
            break;
        &#125;
        sceKernelSignalSema&#40;bufferFull, 1&#41;;
    &#125;
    return 0;
&#125;

/////////////////////////////////////////////////////////////////////////////////////////
//Audio output
/////////////////////////////////////////////////////////////////////////////////////////
int audioOutput&#40;SceSize args, void *argp&#41;&#123;
    long bytesLeft = OUTPUT_BUFFER;
    int currentBufferPos = 0;

    while&#40;runningFlag&#41;&#123;
        sceKernelWaitSema&#40;bufferFull, 1, 0&#41;;
        sceKernelSignalSema&#40;bufferFull, 0&#41;;
        if &#40;OGG_eos&#41;
            break;

        bytesLeft -= bytesRed;
        if &#40;!bytesLeft&#41;&#123;
            memcpy&#40;pcmoutBlock,pcmout,OUTPUT_BUFFER&#41;;
            bytesLeft = OUTPUT_BUFFER;
            currentBufferPos = 0;
        &#125;else&#123;
            //TODO&#58; Manage bytesRead != OUTPUT_BUFFER
            bytesLeft = OUTPUT_BUFFER;
            sceKernelSignalSema&#40;bufferEmpty, 1&#41;;
            continue;
        &#125;
        sceKernelSignalSema&#40;bufferEmpty, 1&#41;;
        sceAudioOutputBlocking&#40;OGG_audio_channel, PSP_AUDIO_VOLUME_MAX, pcmoutBlock&#41;;
    &#125;
    return 0;
&#125;

/////////////////////////////////////////////////////////////////////////////////////////
//Main
/////////////////////////////////////////////////////////////////////////////////////////
int main&#40;&#41; &#123;
	scePowerSetClockFrequency&#40;222, 222, 111&#41;;

	pspDebugScreenInit&#40;&#41;;
	SetupCallbacks&#40;&#41;;

	pspDebugScreenInit&#40;&#41;;
	pspDebugScreenSetXY&#40;0, 0&#41;;
	pspDebugScreenPrintf&#40;"libTremor test"&#41;;
	pspDebugScreenSetXY&#40;0, 25&#41;;
	pspDebugScreenPrintf&#40;"Press X to exit"&#41;;

	SceCtrlData pad;

	//Apro il file OGG&#58;
	OGG_file = fopen&#40;"ms0&#58;/test.ogg", "r"&#41;;
	if &#40;&#40;OGG_file&#41; != NULL&#41; &#123;
		ov_open&#40;OGG_file, &OGG_VorbisFile, NULL, 0&#41;;
	&#125;else&#123;
		pspDebugScreenSetXY&#40;0, 10&#41;;
		pspDebugScreenPrintf&#40;"Error opening file\n"&#41;;
		return -1;
	&#125;

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

	int h = 0;
	int m = 0;
	int s = 0;
	char dest&#91;9&#93;;
	long secs = &#40;long&#41;ov_time_total&#40;&OGG_VorbisFile, -1&#41;/1000;
	h = secs / 3600;
	m = &#40;secs - h * 3600&#41; / 60;
	s = secs - h * 3600 - m * 60;
	snprintf&#40;dest, sizeof&#40;dest&#41;, "%2.2i&#58;%2.2i&#58;%2.2i", h, m, s&#41;;
	pspDebugScreenPrintf&#40;"Length     &#58; %s\n", dest&#41;;

    //Reserve audio channel&#58;
    OGG_audio_channel = sceAudioChReserve&#40;OGG_audio_channel, OUTPUT_BUFFER/4, PSP_AUDIO_FORMAT_STEREO&#41;;

    //Start buffer filling thread&#58;
    int bufferThid = sceKernelCreateThread&#40;"bufferFilling", fillBuffer, 0x16, 0x1800, 0, NULL&#41;;
    if&#40;bufferThid < 0&#41;
        return -1;
	sceKernelStartThread&#40;bufferThid, 0, NULL&#41;;
    
    //Creates semaphores&#58;
    bufferEmpty = sceKernelCreateSema&#40;"bufferEmpty", 0, 1, 1, 0&#41;;
    bufferFull = sceKernelCreateSema&#40;"bufferFull", 0, 0, 1, 0&#41;;

    //Start audio output thread&#58;
    int audioThid = sceKernelCreateThread&#40;"audioOutput", audioOutput, 0x18, 0x1800, 0, NULL&#41;;
    if&#40;audioThid < 0&#41;
        return -1;
	sceKernelStartThread&#40;audioThid, 0, NULL&#41;;

	while&#40;runningFlag&#41;&#123;
		sceCtrlReadBufferPositive&#40;&pad, 1&#41;;
		if&#40;pad.Buttons & PSP_CTRL_CROSS&#41;&#123;
			break;
		&#125;
	&#125;

    //Release chennale&#58;
    sceAudioChRelease&#40;OGG_audio_channel&#41;;
	sceKernelExitGame&#40;&#41;;
	return 0;
&#125;
Ciaooo
Sakya
Viper8896
Posts: 110
Joined: Thu Jan 26, 2006 6:20 pm

Post by Viper8896 »

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.
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

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.
Viper8896
Posts: 110
Joined: Thu Jan 26, 2006 6:20 pm

Post by Viper8896 »

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?r ... 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: Select all

sceAudioSetChannelDataLen&#40;AudioChannel, bytes_decoded/4&#41;;
before every

Code: Select all

sceAudioOutputBlocking
sakya
Posts: 190
Joined: Fri Apr 28, 2006 5:48 pm
Contact:

Post by sakya »

Hi! :)
Viper8896 wrote:for anyone else having the same problem the solution is to simply call

Code: Select all

sceAudioSetChannelDataLen&#40;AudioChannel, bytes_decoded/4&#41;;
before every

Code: Select all

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: Select all

#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&#40;"libTremor Example", 0x1000, 1, 1&#41;;
PSP_MAIN_THREAD_ATTR&#40;0&#41;;

#define OUTPUT_BUFFER 4*1024

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

int bufferEmpty;
int bufferFull;
long bytesRed = 0;

/////////////////////////////////////////////////////////////////////////////////////////
//Callbacks
/////////////////////////////////////////////////////////////////////////////////////////
/* Exit callback */
int exit_callback&#40;int arg1, int arg2, void *common&#41; &#123;
	//sceKernelExitGame&#40;&#41;;
    runningFlag = 0;
    return 0;
&#125;

/* Callback thread */
int CallbackThread&#40;SceSize args, void *argp&#41; &#123;
	int cbid;

	cbid = sceKernelCreateCallback&#40;"Exit Callback", exit_callback, NULL&#41;;
	sceKernelRegisterExitCallback&#40;cbid&#41;;
	sceKernelSleepThreadCB&#40;&#41;;
	return 0;
&#125;

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks&#40;void&#41; &#123;
	int thid = 0;

	thid = sceKernelCreateThread&#40;"update_thread", CallbackThread, 0x11, 0xFA0, 0, 0&#41;;
	if&#40;thid >= 0&#41; &#123;
		sceKernelStartThread&#40;thid, 0, 0&#41;;
	&#125;

	return thid;
&#125;

/////////////////////////////////////////////////////////////////////////////////////////
//Buffer filling
/////////////////////////////////////////////////////////////////////////////////////////
int fillBuffer&#40;SceSize args, void *argp&#41;&#123;

    bufferEmpty = sceKernelCreateSema&#40;"bufferEmpty", 0, 1, 1, 0&#41;;
    while&#40;runningFlag&#41;&#123;
        sceKernelWaitSema&#40;bufferEmpty, 1, 0&#41;; 
		bytesRed = ov_read&#40;&OGG_VorbisFile,pcmout,sizeof&#40;pcmout&#41;,&bitStream&#41;;
		switch &#40;bytesRed&#41;&#123;
		case 0&#58;
			//EOF&#58;
			OGG_eos = 1;
			ov_clear&#40;&OGG_VorbisFile&#41;;
			sceKernelSignalSema&#40;bufferFull, 1&#41;;
			break;		
		case OV_HOLE&#58;
		case OV_EBADLINK&#58;
			sceKernelSignalSema&#40;bufferEmpty, 1&#41;;
			break;
		default&#58;
			sceKernelSignalSema&#40;bufferFull, 1&#41;;
			break;
		&#125;
    &#125;
    return 0;
&#125;

/////////////////////////////////////////////////////////////////////////////////////////
//Audio output
/////////////////////////////////////////////////////////////////////////////////////////
int audioOutput&#40;SceSize args, void *argp&#41;&#123;
	bufferFull = sceKernelCreateSema&#40;"bufferFull", 0, 0, 1, 0&#41;;
	while&#40;runningFlag&#41;&#123;
        sceKernelWaitSema&#40;bufferFull, 1, 0&#41;;
        if &#40;OGG_eos&#41;
            break;
		memcpy&#40;pcmoutBlock,pcmout,OUTPUT_BUFFER&#41;;
		sceKernelSignalSema&#40;bufferEmpty, 1&#41;;
		sceAudioSetChannelDataLen&#40;OGG_audio_channel, bytesRed/4&#41;;
        sceAudioOutputBlocking&#40;OGG_audio_channel, PSP_AUDIO_VOLUME_MAX, pcmoutBlock&#41;;
    &#125;
    return 0;
&#125;

/////////////////////////////////////////////////////////////////////////////////////////
//Main
/////////////////////////////////////////////////////////////////////////////////////////
int main&#40;&#41; &#123;
	pspDebugScreenInit&#40;&#41;;
	SetupCallbacks&#40;&#41;;

	pspDebugScreenInit&#40;&#41;;
	pspDebugScreenSetXY&#40;0, 0&#41;;
	pspDebugScreenPrintf&#40;"libTremor test"&#41;;
	pspDebugScreenSetXY&#40;0, 25&#41;;
	pspDebugScreenPrintf&#40;"Press X to exit"&#41;;

	SceCtrlData pad;

	//Apro il file OGG&#58;
	OGG_file = fopen&#40;"ms0&#58;/test.ogg", "r"&#41;;
	if &#40;&#40;OGG_file&#41; != NULL&#41; &#123;
		ov_open&#40;OGG_file, &OGG_VorbisFile, NULL, 0&#41;;
	&#125;else&#123;
		pspDebugScreenSetXY&#40;0, 10&#41;;
		pspDebugScreenPrintf&#40;"Error opening file\n"&#41;;
		return -1;
	&#125;

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

	int h = 0;
	int m = 0;
	int s = 0;
	char dest&#91;9&#93;;
	long secs = &#40;long&#41;ov_time_total&#40;&OGG_VorbisFile, -1&#41;/1000;
	h = secs / 3600;
	m = &#40;secs - h * 3600&#41; / 60;
	s = secs - h * 3600 - m * 60;
	snprintf&#40;dest, sizeof&#40;dest&#41;, "%2.2i&#58;%2.2i&#58;%2.2i", h, m, s&#41;;
	pspDebugScreenPrintf&#40;"Length     &#58; %s\n", dest&#41;;

    //Reserve audio channel&#58;
    OGG_audio_channel = sceAudioChReserve&#40;OGG_audio_channel, OUTPUT_BUFFER/4, PSP_AUDIO_FORMAT_STEREO&#41;;

    //Start buffer filling thread&#58;
    int bufferThid = sceKernelCreateThread&#40;"bufferFilling", fillBuffer, 0x12, 0x10000, PSP_THREAD_ATTR_USER, NULL&#41;;
    if&#40;bufferThid < 0&#41;
        return -1;
	sceKernelStartThread&#40;bufferThid, 0, NULL&#41;;

    //Start audio output thread&#58;
    int audioThid = sceKernelCreateThread&#40;"audioOutput", audioOutput, 0x16, 0x1800, PSP_THREAD_ATTR_USER, NULL&#41;;
    if&#40;audioThid < 0&#41;
        return -1;
	sceKernelStartThread&#40;audioThid, 0, NULL&#41;;

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

		sceCtrlReadBufferPositive&#40;&pad, 1&#41;;
		if&#40;pad.Buttons & PSP_CTRL_CROSS&#41;&#123;
			break;
		&#125;
        sceKernelDelayThread&#40;10000&#41;;
	&#125;
    
    if &#40;OGG_eos&#41;&#123;
        pspDebugScreenSetXY&#40;0, 7&#41;;
        pspDebugScreenPrintf&#40;"EOF"&#41;;
        sceKernelDelayThread&#40;400000&#41;;
    &#125;

    //Release channel&#58;
    sceAudioChRelease&#40;OGG_audio_channel&#41;;
	sceKernelExitGame&#40;&#41;;
	return 0;
&#125;
Many thanks again. :)
Ciaooo
Sakya
Last edited by sakya on Wed Sep 19, 2007 5:17 pm, edited 1 time in total.
TakutoKaneshiro
Posts: 4
Joined: Fri Sep 07, 2007 7:00 pm

Post by TakutoKaneshiro »

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.
sakya
Posts: 190
Joined: Fri Apr 28, 2006 5:48 pm
Contact:

Post by sakya »

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
Viper8896
Posts: 110
Joined: Thu Jan 26, 2006 6:20 pm

Post by Viper8896 »

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__Thr ... efed160b7b 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__Thr ... 25089abbe8
sakya
Posts: 190
Joined: Fri Apr 28, 2006 5:48 pm
Contact:

Post by sakya »

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__Thr ... efed160b7b 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__Thr ... 25089abbe8
Many thanks for this clarification. :)
I'm asking how could the program work with these errors in semaphores...

Ciaooo
Sakya
Viper8896
Posts: 110
Joined: Thu Jan 26, 2006 6:20 pm

Post by Viper8896 »

sakya wrote:

Code: Select all

....
int fillBuffer&#40;SceSize args, void *argp&#41;&#123;

    bufferEmpty = sceKernelCreateSema&#40;"bufferEmpty", 0, 1, 1, 0&#41;;
    while&#40;runningFlag&#41;&#123;
        sceKernelWaitSema&#40;bufferEmpty, 1, 0&#41;; 
		bytesRed = ov_read&#40;&OGG_VorbisFile,pcmout,sizeof&#40;pcmout&#41;,&bitStream&#41;;
		switch &#40;bytesRed&#41;&#123;
		case 0&#58;
			//EOF&#58;
			OGG_eos = 1;
			ov_clear&#40;&OGG_VorbisFile&#41;;
			sceKernelSignalSema&#40;bufferFull, 1&#41;;
			break;		
		case OV_HOLE&#58;
		case OV_EBADLINK&#58;
			sceKernelSignalSema&#40;bufferEmpty, 1&#41;;
			break;
		default&#58;
			sceKernelSignalSema&#40;bufferFull, 1&#41;;
			break;
		&#125;
    &#125;
    return 0;
&#125;

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
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

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.
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

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: Select all

TARGET = OggPlayer
OBJS = main.o reqfile.o

INCDIR =
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $&#40;CFLAGS&#41; -fno-exceptions -fno-rtti
ASFLAGS = $&#40;CFLAGS&#41;

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=$&#40;shell psp-config --pspsdk-path&#41;
include $&#40;PSPSDK&#41;/lib/build.mak
main.c

Code: Select all

#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&#40;char *&#41;;

PSP_MODULE_INFO&#40;"OggPlayer", 0, VERS, REVS&#41;;
PSP_MAIN_THREAD_ATTR&#40;PSP_THREAD_ATTR_USER&#41;;
PSP_HEAP_SIZE_KB&#40;2500&#41;;

#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&#91;OUTPUT_BUFFER&#93;;
char pcmout2&#91;OUTPUT_BUFFER&#93;;
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&#40;int arg1, int arg2, void *common&#41; &#123;
	sceKernelExitGame&#40;&#41;;
	return 0;
&#125;

/* Callback thread */
int CallbackThread&#40;SceSize args, void *argp&#41; &#123;
	int cbid;

	cbid = sceKernelCreateCallback&#40;"Exit Callback", exit_callback, NULL&#41;;
	sceKernelRegisterExitCallback&#40;cbid&#41;;
	sceKernelSleepThreadCB&#40;&#41;;
	return 0;
&#125;

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks&#40;void&#41; &#123;
	int thid = 0;

	thid = sceKernelCreateThread&#40;"update_thread", CallbackThread, 0x11, 0xFA0, PSP_THREAD_ATTR_USER, 0&#41;;
	if&#40;thid >= 0&#41; &#123;
		sceKernelStartThread&#40;thid, 0, 0&#41;;
	&#125;

	return thid;
&#125;

/////////////////////////////////////////////////////////////////////////////////////////
//Buffer filling
/////////////////////////////////////////////////////////////////////////////////////////
int fillBuffer&#40;SceSize args, void *argp&#41;
&#123;
	int bytes, bytesRed;
	int fillbuf;

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

/////////////////////////////////////////////////////////////////////////////////////////
//Audio output
/////////////////////////////////////////////////////////////////////////////////////////
int audioOutput&#40;SceSize args, void *argp&#41;
&#123;
	int playlen;
	char *playbuf;

	while&#40;runningFlag&#41;
	&#123;
		sceKernelWaitSema&#40;bufferFull, 1, 0&#41;;
		if &#40;OGG_eos || !runningFlag&#41;
			break;
		//playlen = bufferFlip ? pcmlen1 &#58; pcmlen2;
		playbuf = bufferFlip ? pcmout1 &#58; pcmout2;
		bufferFlip ^= 1;
		sceKernelSignalSema&#40;bufferEmpty, 1&#41;;
		//sceAudioSetChannelDataLen&#40;OGG_audio_channel, playlen/4&#41;;
		sceAudioOutputBlocking&#40;OGG_audio_channel, audioVol, playbuf&#41;;
	&#125;
	sceKernelExitDeleteThread&#40;0&#41;;
	return 0;
&#125;

/////////////////////////////////////////////////////////////////////////////////////////
//Main
/////////////////////////////////////////////////////////////////////////////////////////
int main&#40;&#41; &#123;
	char *filename;
	SceCtrlData pad;
	char dest&#91;9&#93;;
	long secs;
	int h;
	int m;
	int s;
	vorbis_info *vi;

	pspDebugScreenInit&#40;&#41;;
	SetupCallbacks&#40;&#41;;

	//Creates semaphores&#58;
	bufferEmpty = sceKernelCreateSema&#40;"bufferEmpty", 0, 1, 1, 0&#41;;
	bufferFull = sceKernelCreateSema&#40;"bufferFull", 0, 0, 1, 0&#41;;

	pspDebugScreenInit&#40;&#41;;
	pspDebugScreenSetBackColor&#40;0xA0602000&#41;;
	pspDebugScreenSetTextColor&#40;0xffffff00&#41;;

	audioVol = 0x4000; // half max volume

mloop&#58;
	filename = RequestFile&#40;"ms0&#58;/MUSIC/"&#41;;

	pspDebugScreenClear&#40;&#41;;
	pspDebugScreenSetXY&#40;25, 1&#41;;
	printf&#40;"Simple Ogg Player"&#41;;
	pspDebugScreenSetXY&#40;25, 3&#41;;
	printf&#40;"CPU&#58; %i BUS&#58; %i", scePowerGetCpuClockFrequency&#40;&#41;, scePowerGetBusClockFrequency&#40;&#41;&#41;;
	pspDebugScreenSetXY&#40;21, 4&#41;;
	printf&#40;"Press X to stop playback"&#41;;

	printf&#40;"\n\n Playing %s\n\n", filename&#41;;
	sceKernelDelayThread&#40;2*1000*1000&#41;;

	//Apro il file OGG&#58;
	OGG_file = fopen&#40;filename, "r"&#41;;
	if &#40;&#40;OGG_file&#41; != NULL&#41;
		ov_open&#40;OGG_file, &OGG_VorbisFile, NULL, 0&#41;;
	else
	&#123;
		printf&#40;" Error opening file\n"&#41;;
		sceKernelDelayThread&#40;2*1000*1000&#41;;
		goto mloop;
	&#125;

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

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

	sceKernelDelayThread&#40;2*1000*1000&#41;;

	memset&#40;pcmout1, 0, OUTPUT_BUFFER&#41;;
	memset&#40;pcmout2, 0, OUTPUT_BUFFER&#41;;
	pcmlen1 = 0;
	pcmlen2 = 0;

	OGG_eos = 0;
	runningFlag = 1;
	sceKernelSignalSema&#40;bufferEmpty, 1&#41;;
	sceKernelSignalSema&#40;bufferFull, 0&#41;;

	//Reserve audio channel&#58;
	OGG_audio_channel = sceAudioChReserve&#40;OGG_audio_channel, OUTPUT_BUFFER/4, PSP_AUDIO_FORMAT_STEREO&#41;;

	//Start buffer filling thread&#58;
	int bufferThid = sceKernelCreateThread&#40;"bufferFilling", fillBuffer, 0x12, 0x1800, PSP_THREAD_ATTR_USER, NULL&#41;;
	if&#40;bufferThid < 0&#41;
		sceKernelExitGame&#40;&#41;;
	sceKernelStartThread&#40;bufferThid, 0, NULL&#41;;

	//Start audio output thread&#58;
	int audioThid = sceKernelCreateThread&#40;"audioOutput", audioOutput, 0x16, 0x1800, PSP_THREAD_ATTR_USER, NULL&#41;;
	if&#40;audioThid < 0&#41;
		sceKernelExitGame&#40;&#41;;
	sceKernelStartThread&#40;audioThid, 0, NULL&#41;;

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

		sceCtrlReadBufferPositive&#40;&pad, 1&#41;;
		if&#40;pad.Buttons & PSP_CTRL_CROSS&#41;
			break; // stop playback

		if &#40;pad.Buttons & PSP_CTRL_LTRIGGER&#41;
			if &#40;audioVol > 0&#41;
				audioVol -= 0x0800; // 16 steps on the audio

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

		sceKernelDelayThread&#40;100*1000&#41;;
	&#125;

	if &#40;OGG_eos == 1&#41;
		printf&#40;"\n\n End of file\n"&#41;;
	else
		printf&#40;"\n\n Playback stopped\n"&#41;;

	runningFlag = 0; // kill the threads
	sceKernelSignalSema&#40;bufferEmpty, 1&#41;;
	sceKernelSignalSema&#40;bufferFull, 1&#41;;
	sceKernelDelayThread&#40;1*1000*1000&#41;;

	//Release channel&#58;
	sceAudioChRelease&#40;OGG_audio_channel&#41;;

	goto mloop;

	return 0; // never reaches here - just supresses compiler warning
&#125;
reqfile.c

Code: Select all

#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 &#123;
	char filename&#91;FILENAME_MAX&#93;;
	char path&#91;FILENAME_MAX&#93;;
	int flags;
&#125; cdfiles&#91;MAXFILES&#93;;

static int maxfiles;


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

static unsigned int get_buttons&#40;&#41;
&#123;
	SceCtrlData pad;

	sceCtrlReadBufferPositive&#40;&pad, 1&#41;;
	return pad.Buttons;
&#125;

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

int parse_dir &#40;char *path&#41;
&#123;
	DIR *dir;
	DIR *test_dir;
	struct dirent *dirent = 0;
	struct stat fstat;
	char file_name&#91;FILENAME_MAX&#93;;
	FILE *file;

	maxfiles = 0;
	/* open directory */
	if &#40; &#40; dir = opendir&#40; path &#41; &#41; == 0 &#41;
		return 0;

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

		if &#40;maxfiles == MAXFILES&#41;
			break;
	&#125;
	/* close dir */
	closedir&#40; dir &#41;;

	return maxfiles;
&#125;

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

void ShowFiles&#40; int offset, int selection &#41;
&#123;
	int i,j;
	char text&#91;69&#93;;

	pspDebugScreenClear&#40;&#41;;

	j = 0;
	for &#40; i = offset; i < &#40; offset + PAGESIZE &#41; && i < maxfiles ; i++ &#41;
	&#123;
		if &#40; cdfiles&#91;i&#93;.flags &#41;
		&#123;
			strcpy&#40;text,"&#91;"&#41;;
			strncat&#40;text, cdfiles&#91;i&#93;.filename,66&#41;;
			strcat&#40;text,"&#93;"&#41;;
		&#125;
		else
			strncpy&#40;text, cdfiles&#91;i&#93;.filename, 68&#41;;

		text&#91;68&#93;=0;

		pspDebugScreenSetTextColor&#40;j == &#40; selection - offset &#41; ? 0xbbbbbb00 &#58; 0xffffff00&#41;;
		pspDebugScreenSetXY&#40;&#40;68 - strlen&#40;text&#41;&#41; / 2, i - offset + 1&#41;;
		printf&#40;"%s", text&#41;;

		j++;
	&#125;
	pspDebugScreenSetTextColor&#40;0xffffff00&#41;;
&#125;

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

int FileSelector&#40;&#41;
&#123;
	int offset = 0;
	int selection = 0;
	int havefile = 0;
	int redraw = 1;
	unsigned int p = get_buttons&#40;&#41;;

	while &#40; havefile == 0 && !&#40;p & PSP_CTRL_CIRCLE&#41; &#41;
	&#123;
		if &#40; redraw &#41;
			ShowFiles&#40; offset, selection &#41;;
		redraw = 0;

		while &#40;!&#40;p = get_buttons&#40;&#41;&#41;&#41;
			sceKernelDelayThread&#40;10000&#41;;
		while &#40;p == get_buttons&#40;&#41;&#41;
			sceKernelDelayThread&#40;10000&#41;;

		if &#40; p & PSP_CTRL_DOWN &#41;
		&#123;
			selection++;
			if &#40; selection == maxfiles &#41;
				selection = offset = 0;	// wrap around to top

			if &#40; &#40; selection - offset &#41; == PAGESIZE &#41;
				offset += PAGESIZE; // next "page" of entries

			redraw = 1;
		&#125;

		if &#40; p & PSP_CTRL_UP &#41;
		&#123;
			selection--;
			if &#40; selection < 0 &#41;
			&#123;
				selection = maxfiles - 1;
				offset = maxfiles > PAGESIZE ? selection - PAGESIZE + 1 &#58; 0; // wrap around to bottom
			&#125;

			if &#40; selection < offset &#41;
			&#123;
				offset -= PAGESIZE; // previous "page" of entries
				if &#40; offset < 0 &#41;
					offset = 0;
			&#125;

			redraw = 1;
		&#125;

		if &#40; p & PSP_CTRL_RIGHT &#41;
		&#123;
			selection += PAGESIZE;
			if &#40; selection >= maxfiles &#41;
				selection = offset = 0;	// wrap around to top

			if &#40; &#40; selection - offset &#41; >= PAGESIZE &#41;
				offset += PAGESIZE; // next "page" of entries

			redraw = 1;
		&#125;

		if &#40; p & PSP_CTRL_LEFT &#41;
		&#123;
			selection -= PAGESIZE;
			if &#40; selection < 0 &#41;
			&#123;
				selection = maxfiles - 1;
				offset = maxfiles > PAGESIZE ? selection - PAGESIZE + 1 &#58; 0; // wrap around to bottom
			&#125;

			if &#40; selection < offset &#41;
			&#123;
				offset -= PAGESIZE; // previous "page" of entries
				if &#40; offset < 0 &#41;
					offset = 0;
			&#125;

			redraw = 1;
		&#125;

		if &#40; p & PSP_CTRL_CROSS &#41;
		&#123;
			if &#40; cdfiles&#91;selection&#93;.flags &#41;	/*** This is directory ***/
			&#123;
				char fname&#91;FILENAME_MAX+FILENAME_MAX&#93;;

				strncpy&#40;fname, cdfiles&#91;selection&#93;.path, FILENAME_MAX&#41;;
				fname&#91;FILENAME_MAX-1&#93; = 0;
				strncat&#40;fname, cdfiles&#91;selection&#93;.filename, FILENAME_MAX&#41;;
				fname&#91;FILENAME_MAX+FILENAME_MAX-2&#93; = 0;
				strcat&#40;fname, "/"&#41;;
				offset = selection = 0;
				parse_dir&#40;fname&#41;;
			&#125;
			else
				return selection;

			redraw = 1;
		&#125;

		if &#40; p & PSP_CTRL_TRIANGLE &#41;
		&#123;
			char fname&#91;FILENAME_MAX&#93;;
			int pathpos = strlen&#40;cdfiles&#91;1&#93;.path&#41; - 2;

			while &#40;pathpos > 5&#41;
			&#123;
				if &#40;cdfiles&#91;1&#93;.path&#91;pathpos&#93; == '/'&#41; break;
				pathpos--;
			&#125;
			if &#40;pathpos < 5&#41; pathpos = 5; /** handle root case */
			strncpy&#40;fname, cdfiles&#91;1&#93;.path, pathpos+1&#41;;
			fname&#91;pathpos+1&#93; = 0;
			offset = selection = 0;
			parse_dir&#40;fname&#41;;

			redraw = 1;
		&#125;
	&#125;

	return -1; // no file selected
&#125;

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

char *RequestFile &#40;char *initialPath&#41;
&#123;
	int selection;
	static char fname&#91;FILENAME_MAX+FILENAME_MAX&#93;;

	if &#40;!parse_dir&#40;initialPath&#41;&#41;
		return 0;

	selection = FileSelector &#40;&#41;;
	if &#40;selection < 0&#41;
		return 0;

	strncpy &#40;fname, cdfiles&#91;selection&#93;.path, FILENAME_MAX&#41;;
	fname&#91;FILENAME_MAX-1&#93; = 0;
	strncat &#40;fname, cdfiles&#91;selection&#93;.filename, FILENAME_MAX&#41;;
	fname&#91;FILENAME_MAX+FILENAME_MAX-1&#93; = 0;

	return fname;
&#125;
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

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.
Viper8896
Posts: 110
Joined: Thu Jan 26, 2006 6:20 pm

Post by Viper8896 »

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.
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

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. :)
Viper8896
Posts: 110
Joined: Thu Jan 26, 2006 6:20 pm

Post by Viper8896 »

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: Select all

/*
 * 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&#40;"vorbis-ng", 0, 0, 0&#41;;
PSP_MAIN_THREAD_ATTR&#40;THREAD_ATTR_USER | THREAD_ATTR_VFPU&#41;;
PSP_HEAP_SIZE_KB&#40;1024*4&#41;;

/* 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&#91;256&#93;;
char ChannelCount;

long TimeNow;
long TimeTotal;

/* definitions */
#define printf pspDebugScreenPrintf
#define amountOfBytes 1024*4
#define SampleSize 2
#define SamplesPerOutput &#40;&#40;amountOfBytes/SampleSize&#41;/ChannelCount&#41;

typedef struct
&#123;
	long size;
	char pcmout&#91;amountOfBytes&#93;;
&#125;buffer;

buffer *buffer0;
buffer *buffer1;

/* function prototypes */
int VorbClear&#40;&#41;;
int VorbLoad&#40;&#41;;
int set_audio_freq&#40;int devkitVersion, int frequency&#41;;

/* Exit callback */
int exit_callback&#40;int arg1, int arg2, void *common&#41;
&#123;
	VorbClear&#40;&#41;;
	sceKernelExitGame&#40;&#41;;

	return 0;
&#125;

/* Callback thread */
int CallbackThread&#40;SceSize args, void *argp&#41;
&#123;
	int cbid;

	cbid = sceKernelCreateCallback&#40;"Exit Callback", exit_callback, NULL&#41;;
	sceKernelRegisterExitCallback&#40;cbid&#41;;

	sceKernelSleepThreadCB&#40;&#41;;

	return 0;
&#125;

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks&#40;void&#41;
&#123;
	int thid = 0;

	thid = sceKernelCreateThread&#40;"update_thread", CallbackThread, 0x11, 0xFA0, 0, 0&#41;;
	if&#40;thid >= 0&#41;
	&#123;
		sceKernelStartThread&#40;thid, 0, 0&#41;;
	&#125;

	return thid;
&#125;

/* vorbis call backs */
size_t ogg_callback_read&#40;void *ptr, size_t size, size_t nmemb, void *datasource&#41;
&#123;
    return sceIoRead&#40;*&#40;int *&#41; datasource, ptr, size * nmemb&#41;;
&#125;
int ogg_callback_seek&#40;void *datasource, ogg_int64_t offset, int whence&#41;
&#123;
    return sceIoLseek32&#40;*&#40;int *&#41; datasource, &#40;unsigned int&#41; offset, whence&#41;;
&#125;
long ogg_callback_tell&#40;void *datasource&#41;
&#123;
    return sceIoLseek32&#40;*&#40;int *&#41; datasource, 0, SEEK_CUR&#41;;
&#125;
int ogg_callback_close&#40;void *datasource&#41;
&#123;
    return sceIoClose&#40;*&#40;int *&#41; datasource&#41;;
&#125;

/* swap input buffer with output buffer */
int swapBuffers&#40;&#41;
&#123;
	buffer *tempPtr;
	tempPtr = buffer0;
	buffer0 = buffer1;
	buffer1 = tempPtr;
	return 0;
&#125;

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

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

int VorbPlay&#40;&#41;
&#123;
	if&#40;!VorbLoaded&#41;
	&#123;
		return -1;
	&#125;
	if&#40;EOS&#41; //if ended re-open
	&#123;
		VorbClear&#40;&#41;;
		VorbLoad&#40;ogg_file&#41;;
	&#125;
	Play=1;
	return 0;
&#125;

int VorbStop&#40;&#41;
&#123;
	if&#40;!VorbLoaded&#41;
	&#123;
		return -1;
	&#125;
	Play=0;
	return 0;
&#125;

int VorbLoad&#40;char file&#91;256&#93;&#41;
&#123;
	if&#40;VorbLoaded&#41;
	&#123;
		return -1;
	&#125;
	strcpy&#40;ogg_file,file&#41;;
	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&#40;sizeof&#40;buffer&#41;&#41;;
	buffer1 = malloc&#40;sizeof&#40;buffer&#41;&#41;;
	fd=sceIoOpen&#40;ogg_file, PSP_O_RDONLY, 0777&#41;;
	ov_open_callbacks&#40;&fd,&ovf,NULL,0, ogg_callbacks&#41;;
	vi = ov_info&#40;&ovf, -1&#41;;
	ChannelCount = vi->channels;

	/* get this sample rate bull to work please */
	SceUID mod = kuKernelLoadModule&#40;"kernel_stuff.prx", 0, NULL&#41;;
	printf&#40;"value returned by LoadModule&#58; %d ",mod&#41;;
	if&#40;mod>0&#41;
	&#123;
		int ret = set_audio_freq&#40;sceKernelDevkitVersion&#40;&#41;, vi->rate&#41;;
		printf&#40;"set_audio_freq&#58; %d\n",ret&#41;;
	&#125;
	/* end sample rate code */

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

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

long VorbTime&#40;int total&#41;
&#123;
	if&#40;total&#41;
	return TimeTotal;
	return TimeNow;
&#125;

int VorbPlaying&#40;&#41;
&#123;
	return Play;
&#125;

int VorbFinished&#40;&#41;
&#123;
	return EOS;
&#125;

int updateTimeString&#40;&#41;
&#123;
	while&#40;1&#41;
	&#123;
		//Print timestring&#58;
		long secs = VorbTime&#40;0&#41;/1000;
		int h = secs / 3600;
		int m = &#40;secs - h * 3600&#41; / 60;
		int s = secs - h * 3600 - m * 60;
		char time_string&#91;9&#93;;
		snprintf&#40;time_string, sizeof&#40;time_string&#41;, "%2.2i&#58;%2.2i&#58;%2.2i", h, m, s&#41;;
		pspDebugScreenSetXY&#40;0, 8&#41;;
		pspDebugScreenPrintf&#40;"Current    &#58; %s\n", time_string&#41;;
		sceKernelDelayThread&#40;100000&#41;;
	&#125;
	return 0;
&#125;

int main&#40;&#41;
&#123;
	pspDebugScreenInit&#40;&#41;;
	SetupCallbacks&#40;&#41;;

	SceCtrlData pad;

	printf&#40;"loading\n\n"&#41;;

	VorbLoad&#40;"fatms&#58;/1.ogg"&#41;;

	printf&#40;"starting. press X at any time to exit or start to pause/resume\n\n"&#41;;

	VorbPlay&#40;&#41;;

	
	pspDebugScreenPrintf&#40;"Channels   &#58; %d\n", vi->channels&#41;;
	pspDebugScreenPrintf&#40;"Sample rate&#58; %ld Hz\n", vi->rate&#41;;
	pspDebugScreenPrintf&#40;"Bitrate    &#58; %ld kBit\n", vi->bitrate_nominal/1000&#41;;
	

	long secs = VorbTime&#40;1&#41;/1000;
	int h = secs / 3600;
	int m = &#40;secs - h * 3600&#41; / 60;
	int s = secs - h * 3600 - m * 60;
	char time_string&#91;9&#93;;
	snprintf&#40;time_string, sizeof&#40;time_string&#41;, "%2.2i&#58;%2.2i&#58;%2.2i", h, m, s&#41;;
	pspDebugScreenPrintf&#40;"Length     &#58; %s\n", time_string&#41;;


	int time_thid = sceKernelCreateThread&#40;"TimeThread", updateTimeString, 0x30, 0x1800, 0, NULL&#41;;
	sceKernelStartThread&#40;time_thid, 0, NULL&#41;;

	while&#40;1&#41;
	&#123;
		sceCtrlReadBufferPositive&#40;&pad, 1&#41;;
		if&#40;pad.Buttons & PSP_CTRL_CROSS&#41;break;
		if&#40;pad.Buttons & PSP_CTRL_START&#41;
		&#123;
			if&#40;VorbPlaying&#40;&#41;&#41;VorbStop&#40;&#41;;
			else VorbPlay&#40;&#41;;
			sceKernelDelayThread&#40;1000000&#41;;			
		&#125;
	&#125;

	pspDebugScreenSetXY&#40;0, 10&#41;;
	printf&#40;"finished"&#41;;

	VorbClear&#40;&#41;;

	sceKernelDelayThread&#40;1000000&#41;;

	sceKernelExitGame&#40;&#41;;	

	return 0;

&#125; //end main function


Post Reply