I'm using the mp3 samples from http://www.scriptscribbler.com/psp/
It worked fine when I followed the tutorial, or modified it slightly.
But now I'm implementing it into a project, and I run into problems.
I'm doing the all of the mp3 stuff in a funciton in a header file, instead of in the main() funciton.
It compiles fine, not even a warning...but when the program is executed, more specifically when MP3_Load("insert/file/here.mp3"); finishes, it returns an error...and I can't understand why. It makes no sense that moving the mp3 functions into a header would screw them up...especially when it compiles fine.
Here is the sourcecode; names are deleted to keep this project a secret.
Main.c
Code: Select all
//////////////////////////////////////////////////////////////////////////////////////////
/* game PSP Version 0.1
* by Jason J. Owens
* February 8th, 2006 1:45 PM
*/
//////////////////////////////////////////////////////////////////////////////////////////
// #includes
#include <pspkernel.h>
#include <pspdebug.h>
#include <psptypes.h>
#include <pspdisplay.h>
#include <pspaudio.h>
#include <pspaudiolib.h>
#include "framebuffer.h"
#include "graphics.h"
//#include "Screenshot.h"
#include "mp3player.h"
#include "intro.h"
#include "game.h"
PSP_MODULE_INFO("game PSP Vers 0.1", 0, 0, 1); // Defines module info, psp mode, and program version.
PSP_MAIN_THREAD_ATTR(0); // Defines the main thread's attribute value (optional).
#define printf pspDebugScreenPrintf // Defines printf, just to make typing easier.
int ExitGame = 0; // Gets set to '1' when getting ready to exit the game.
// Exit callback
int exit_callback(int arg1, int arg2, void *common)
{
ExitGame = 1;
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, THREAD_ATTR_USER, 0);
if(thid >= 0)
{
sceKernelStartThread(thid, 0, 0);
}
return thid;
}
// Here's the Main Function.
int main(void)
{
pspDebugScreenInit(); // Initializes the Debug text output screen.
SetupCallbacks(); // Sets up the callback thread and returns its thread id.
//Intro();
if(!Title_Screen()){printf("Error in Title_Screen() function!\n");}
while(!ExitGame){sceDisplayWaitVblankStart();}
sceKernelExitGame(); // Returns PSP to the Main Menu.
return 0; // Just for good practice.
}
//////////////////////////////////////////////////////////////////////////////////////////
Code: Select all
// game.h
// By Jason J. Owens
// February 8, 2006 @ 2:26 PM
//----------------------------
#ifndef __game__
#define __game__
//----------------------
// #defines
#define printf pspDebugScreenPrintf
/////////////////////
// Global variables
//------------------
// Prototypes
int Title_Screen(void);
//------------
// Function definitions
int Title_Screen()
{
pspDebugScreenInit();
// Initialize & load music
printf("Initializing PSP Audio...\n");
pspAudioInit();
printf("Initializing MP3 playback system...\n");
MP3_Init(1);
printf("Loading Title_Screen.mp3 music...\n");
if(!MP3_Load("game_Resources/MUSIC/Title_Screen.mp3"))
{
printf("Title_Screen.mp3 failed to load!\n");
return 1;
}
// Initialize & load graphics
initGraphics();
Image* Title_Screen=loadImage("game_Resources/GFX/Title_Screen.png");
if(!Title_Screen)
{
printf("Title_Screen.png failed to load!\n");
return 1;
}
// Start playback of music and display Title Screen
MP3_Play();
blitAlphaImageToScreen(0,0,480,272,Title_Screen,0,0);
sceDisplayWaitVblankStart();
flipScreen();
return 0;
}
//----------------------
#endif
But you might want to see it anyway
mp3player.c
Code: Select all
// mp3player.c: MP3 Player Implementation in C for Sony PSP
//
////////////////////////////////////////////////////////////////////////////
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspiofilemgr.h>
#include <pspdisplay.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <pspaudiolib.h>
#include "mp3player.h"
#define FALSE 0
#define TRUE !FALSE
#define min(a,b) (((a)<(b))?(a):(b))
#define max(a,b) (((a)>(b))?(a):(b))
#define MadErrorString(x) mad_stream_errorstr(x)
#define INPUT_BUFFER_SIZE (5*8192)
#define OUTPUT_BUFFER_SIZE 2048 /* Must be an integer multiple of 4. */
/* This table represents the subband-domain filter characteristics. It
* is initialized by the ParseArgs() function and is used as
* coefficients against each subband samples when DoFilter is non-nul.
*/
mad_fixed_t Filter[32];
/* DoFilter is non-nul when the Filter table defines a filter bank to
* be applied to the decoded audio subbands.
*/
int DoFilter = 0;
/* Define printf, just to make typing easier */
#define printf pspDebugScreenPrintf
#define NUMCHANNELS 2
u8 *ptr;
long size;
long samplesInOutput = 0;
//////////////////////////////////////////////////////////////////////
// Global local variables
//////////////////////////////////////////////////////////////////////
//libmad lowlevel stuff
// The following variables contain the music data, ie they don't change value until you load a new file
struct mad_stream Stream;
struct mad_frame Frame;
struct mad_synth Synth;
mad_timer_t Timer;
signed short OutputBuffer[OUTPUT_BUFFER_SIZE];
unsigned char InputBuffer[INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD],
*OutputPtr = (unsigned char *) OutputBuffer, *GuardPtr = NULL;
const unsigned char *OutputBufferEnd = (unsigned char *) OutputBuffer + OUTPUT_BUFFER_SIZE * 2;
int Status = 0, i;
unsigned long FrameCount = 0;
// The following variables are maintained and updated by the tracker during playback
static int isPlaying; // Set to true when a mod is being played
//////////////////////////////////////////////////////////////////////
// These are the public functions
//////////////////////////////////////////////////////////////////////
static int myChannel;
static int eos;
/* Define printf, just to make typing easier */
#define printf pspDebugScreenPrintf
/*void MP3setStubs(codecStubs * stubs)
{
stubs->init = MP3_Init;
stubs->load = MP3_Load;
stubs->play = MP3_Play;
stubs->pause = MP3_Pause;
stubs->stop = MP3_Stop;
stubs->end = MP3_End;
stubs->time = MP3_GetTimeString;
stubs->tick = NULL;
stubs->eos = MP3_EndOfStream;
memcpy(stubs->extension, "mp3\0" "\0\0\0\0", 2 * 4);
}*/
static int PrintFrameInfo(struct mad_header *Header)
{
const char *Layer, *Mode, *Emphasis;
/* Convert the layer number to it's printed representation. */
switch (Header->layer) {
case MAD_LAYER_I:
Layer = "I";
break;
case MAD_LAYER_II:
Layer = "II";
break;
case MAD_LAYER_III:
Layer = "III";
break;
default:
Layer = "(unexpected layer value)";
break;
}
/* Convert the audio mode to it's printed representation. */
switch (Header->mode) {
case MAD_MODE_SINGLE_CHANNEL:
Mode = "single channel";
break;
case MAD_MODE_DUAL_CHANNEL:
Mode = "dual channel";
break;
case MAD_MODE_JOINT_STEREO:
Mode = "joint (MS/intensity) stereo";
break;
case MAD_MODE_STEREO:
Mode = "normal LR stereo";
break;
default:
Mode = "(unexpected mode value)";
break;
}
/* Convert the emphasis to it's printed representation. Note that
* the MAD_EMPHASIS_RESERVED enumeration value appeared in libmad
* version 0.15.0b.
*/
switch (Header->emphasis) {
case MAD_EMPHASIS_NONE:
Emphasis = "no";
break;
case MAD_EMPHASIS_50_15_US:
Emphasis = "50/15 us";
break;
case MAD_EMPHASIS_CCITT_J_17:
Emphasis = "CCITT J.17";
break;
#if (MAD_VERSION_MAJOR>=1) || \
((MAD_VERSION_MAJOR==0) && (MAD_VERSION_MINOR>=15))
case MAD_EMPHASIS_RESERVED:
Emphasis = "reserved(!)";
break;
#endif
default:
Emphasis = "(unexpected emphasis value)";
break;
}
//pspDebugScreenSetXY(0, 29);
//printf("%lu kb/s audio MPEG layer %s stream at %dHz\n", Header->bitrate / 1000, Layer, Header->samplerate);
//sceDisplayWaitVblankStart();
return (0);
}
/****************************************************************************
* Applies a frequency-domain filter to audio data in the subband-domain. *
****************************************************************************/
static void ApplyFilter(struct mad_frame *Frame)
{
int Channel, Sample, Samples, SubBand;
/* There is two application loops, each optimized for the number
* of audio channels to process. The first alternative is for
* two-channel frames, the second is for mono-audio.
*/
Samples = MAD_NSBSAMPLES(&Frame->header);
if (Frame->header.mode != MAD_MODE_SINGLE_CHANNEL)
for (Channel = 0; Channel < 2; Channel++)
for (Sample = 0; Sample < Samples; Sample++)
for (SubBand = 0; SubBand < 32; SubBand++)
Frame->sbsample[Channel][Sample][SubBand] =
mad_f_mul(Frame->sbsample[Channel][Sample][SubBand], Filter[SubBand]);
else
for (Sample = 0; Sample < Samples; Sample++)
for (SubBand = 0; SubBand < 32; SubBand++)
Frame->sbsample[0][Sample][SubBand] = mad_f_mul(Frame->sbsample[0][Sample][SubBand], Filter[SubBand]);
}
/****************************************************************************
* Converts a sample from libmad's fixed point number format to a signed *
* short (16 bits). *
****************************************************************************/
static signed short MadFixedToSshort(mad_fixed_t Fixed)
{
/* A fixed point number is formed of the following bit pattern:
*
* SWWWFFFFFFFFFFFFFFFFFFFFFFFFFFFF
* MSB LSB
* S ==> Sign (0 is positive, 1 is negative)
* W ==> Whole part bits
* F ==> Fractional part bits
*
* This pattern contains MAD_F_FRACBITS fractional bits, one
* should alway use this macro when working on the bits of a fixed
* point number. It is not guaranteed to be constant over the
* different platforms supported by libmad.
*
* The signed short value is formed, after clipping, by the least
* significant whole part bit, followed by the 15 most significant
* fractional part bits. Warning: this is a quick and dirty way to
* compute the 16-bit number, madplay includes much better
* algorithms.
*/
/* Clipping */
if (Fixed >= MAD_F_ONE)
return (SHRT_MAX);
if (Fixed <= -MAD_F_ONE)
return (-SHRT_MAX);
/* Conversion. */
Fixed = Fixed >> (MAD_F_FRACBITS - 15);
return ((signed short) Fixed);
}
static void MP3Callback(void *_buf2, unsigned int numSamples, void *pdata)
{
short *_buf = (short *)_buf2;
unsigned long samplesOut = 0;
// u8 justStarted = 1;
if (isPlaying == TRUE) { // Playing , so mix up a buffer
if (samplesInOutput > 0) {
//printf("%d samples in buffer\n", samplesInOutput);
if (samplesInOutput > numSamples) {
memcpy((char *) _buf, (char *) OutputBuffer, numSamples * 2 * 2);
samplesOut = numSamples;
samplesInOutput -= numSamples;
} else {
memcpy((char *) _buf, (char *) OutputBuffer, samplesInOutput * 2 * 2);
samplesOut = samplesInOutput;
samplesInOutput = 0;
}
}
while (samplesOut < numSamples) {
if (Stream.buffer == NULL || Stream.error == MAD_ERROR_BUFLEN) {
//size_t ReadSize, Remaining;
//unsigned char *ReadStart;
/* {2} libmad may not consume all bytes of the input
* buffer. If the last frame in the buffer is not wholly
* contained by it, then that frame's start is pointed by
* the next_frame member of the Stream structure. This
* common situation occurs when mad_frame_decode() fails,
* sets the stream error code to MAD_ERROR_BUFLEN, and
* sets the next_frame pointer to a non NULL value. (See
* also the comment marked {4} bellow.)
*
* When this occurs, the remaining unused bytes must be
* put back at the beginning of the buffer and taken in
* account before refilling the buffer. This means that
* the input buffer must be large enough to hold a whole
* frame at the highest observable bit-rate (currently 448
* kb/s). XXX=XXX Is 2016 bytes the size of the largest
* frame? (448000*(1152/32000))/8
*/
/*if(Stream.next_frame!=NULL)
{
Remaining=Stream.bufend-Stream.next_frame;
memmove(InputBuffer,Stream.next_frame,Remaining);
ReadStart=InputBuffer+Remaining;
ReadSize=INPUT_BUFFER_SIZE-Remaining;
}
else
ReadSize=INPUT_BUFFER_SIZE,
ReadStart=InputBuffer,
Remaining=0;
*/
/* Fill-in the buffer. If an error occurs print a message
* and leave the decoding loop. If the end of stream is
* reached we also leave the loop but the return status is
* left untouched.
*/
//ReadSize=BstdRead(ReadStart,1,ReadSize,BstdFile);
//printf("readsize: %d\n", ReadSize);
//sceDisplayWaitVblankStart();
/*if(ReadSize<=0)
{
//printf("read error on bit-stream (%s - %d)\n", error_to_string(errno), errno);
// Status=1;
if(BstdFile->eof) {
printf("end of input stream\n");
sceDisplayWaitVblankStart();
}
//break;
printf("Readsize was <=0 in player callback\n");
sceDisplayWaitVblankStart();
} */
/* {3} When decoding the last frame of a file, it must be
* followed by MAD_BUFFER_GUARD zero bytes if one wants to
* decode that last frame. When the end of file is
* detected we append that quantity of bytes at the end of
* the available data. Note that the buffer can't overflow
* as the guard size was allocated but not used the the
* buffer management code. (See also the comment marked
* {1}.)
*
* In a message to the mad-dev mailing list on May 29th,
* 2001, Rob Leslie explains the guard zone as follows:
*
* "The reason for MAD_BUFFER_GUARD has to do with the
* way decoding is performed. In Layer III, Huffman
* decoding may inadvertently read a few bytes beyond
* the end of the buffer in the case of certain invalid
* input. This is not detected until after the fact. To
* prevent this from causing problems, and also to
* ensure the next frame's main_data_begin pointer is
* always accessible, MAD requires MAD_BUFFER_GUARD
* (currently 8) bytes to be present in the buffer past
* the end of the current frame in order to decode the
* frame."
*/
/*if(BstdFileEofP(BstdFile))
{
GuardPtr=ReadStart+ReadSize;
memset(GuardPtr,0,MAD_BUFFER_GUARD);
ReadSize+=MAD_BUFFER_GUARD;
} */
/* Pipe the new buffer content to libmad's stream decoder
* facility.
*/
mad_stream_buffer(&Stream, ptr, size);
Stream.error = 0;
}
/* Decode the next MPEG frame. The streams is read from the
* buffer, its constituents are break down and stored the the
* Frame structure, ready for examination/alteration or PCM
* synthesis. Decoding options are carried in the Frame
* structure from the Stream structure.
*
* Error handling: mad_frame_decode() returns a non zero value
* when an error occurs. The error condition can be checked in
* the error member of the Stream structure. A mad error is
* recoverable or fatal, the error status is checked with the
* MAD_RECOVERABLE macro.
*
* {4} When a fatal error is encountered all decoding
* activities shall be stopped, except when a MAD_ERROR_BUFLEN
* is signaled. This condition means that the
* mad_frame_decode() function needs more input to complete
* its work. One shall refill the buffer and repeat the
* mad_frame_decode() call. Some bytes may be left unused at
* the end of the buffer if those bytes forms an incomplete
* frame. Before refilling, the remaining bytes must be moved
* to the beginning of the buffer and used for input for the
* next mad_frame_decode() invocation. (See the comments
* marked {2} earlier for more details.)
*
* Recoverable errors are caused by malformed bit-streams, in
* this case one can call again mad_frame_decode() in order to
* skip the faulty part and re-sync to the next frame.
*/
if (mad_frame_decode(&Frame, &Stream)) {
if (MAD_RECOVERABLE(Stream.error)) {
/* Do not print a message if the error is a loss of
* synchronization and this loss is due to the end of
* stream guard bytes. (See the comments marked {3}
* supra for more informations about guard bytes.)
*/
if (Stream.error != MAD_ERROR_LOSTSYNC || Stream.this_frame != GuardPtr) {
//printf("recoverable frame level error (%s)\n", MadErrorString(&Stream));
//sceDisplayWaitVblankStart();
}
return; //continue;
} else if (Stream.error == MAD_ERROR_BUFLEN) {
eos = 1;
return; //continue;
} else {
printf("unrecoverable MP3 frame level error (%s).\n", MadErrorString(&Stream));
sceDisplayWaitVblankStart();
Status = 1;
MP3_Stop(); //break;
}
}
/* The characteristics of the stream's first frame is printed
* on stderr. The first frame is representative of the entire
* stream.
*/
if (FrameCount == 0)
if (PrintFrameInfo(&Frame.header)) {
Status = 1;
//break;
}
/* Accounting. The computed frame duration is in the frame
* header structure. It is expressed as a fixed point number
* whole data type is mad_timer_t. It is different from the
* samples fixed point format and unlike it, it can't directly
* be added or subtracted. The timer module provides several
* functions to operate on such numbers. Be careful there, as
* some functions of libmad's timer module receive some of
* their mad_timer_t arguments by value!
*/
FrameCount++;
mad_timer_add(&Timer, Frame.header.duration);
/* Between the frame decoding and samples synthesis we can
* perform some operations on the audio data. We do this only
* if some processing was required. Detailed explanations are
* given in the ApplyFilter() function.
*/
if (DoFilter)
ApplyFilter(&Frame);
/* Once decoded the frame is synthesized to PCM samples. No errors
* are reported by mad_synth_frame();
*/
mad_synth_frame(&Synth, &Frame);
/* Synthesized samples must be converted from libmad's fixed
* point number to the consumer format. Here we use unsigned
* 16 bit big endian integers on two channels. Integer samples
* are temporarily stored in a buffer that is flushed when
* full.
*/
for (i = 0; i < Synth.pcm.length; i++) {
signed short Sample;
//printf("%d < %d\n", samplesOut, numSamples);
if (samplesOut < numSamples) {
//printf("I really get here\n");
/* Left channel */
Sample = MadFixedToSshort(Synth.pcm.samples[0][i]);
// *(OutputPtr++)=Sample>>8;
// *(OutputPtr++)=Sample&0xff;
_buf[samplesOut * 2] = Sample;
/* Right channel. If the decoded stream is monophonic then
* the right output channel is the same as the left one.
*/
if (MAD_NCHANNELS(&Frame.header) == 2)
Sample = MadFixedToSshort(Synth.pcm.samples[1][i]);
// *(OutputPtr++)=Sample>>8;
// *(OutputPtr++)=Sample&0xff;
//_buf[samplesOut*2]=0;//Sample;
_buf[samplesOut * 2 + 1] = Sample;
samplesOut++;
} else {
//printf("%d < %d of %d\n", samplesOut, numSamples, Synth.pcm.length);
Sample = MadFixedToSshort(Synth.pcm.samples[0][i]);
OutputBuffer[samplesInOutput * 2] = Sample;
//OutputBuffer[samplesInOutput*4+1]=0;//Sample>>8;
//OutputBuffer[samplesInOutput*4+2]=0;//Sample&0xff;
if (MAD_NCHANNELS(&Frame.header) == 2)
Sample = MadFixedToSshort(Synth.pcm.samples[1][i]);
OutputBuffer[samplesInOutput * 2 + 1] = Sample;
//OutputBuffer[samplesInOutput*4+3]=0;//Sample>>8;
//OutputBuffer[samplesInOutput*4+4]=0;//Sample&0xff;
samplesInOutput++;
}
}
}
} else { // Not Playing , so clear buffer
{
int count;
for (count = 0; count < numSamples * 2; count++)
*(_buf + count) = 0;
}
}
}
void MP3_Init(int channel)
{
myChannel = channel;
isPlaying = FALSE;
pspAudioSetChannelCallback(myChannel, MP3Callback,0);
/* First the structures used by libmad must be initialized. */
mad_stream_init(&Stream);
mad_frame_init(&Frame);
mad_synth_init(&Synth);
mad_timer_reset(&Timer);
//ModPlay_Load("",data);
}
void MP3_FreeTune()
{
/* The input file was completely read; the memory allocated by our
* reading module must be reclaimed.
*/
if (ptr)
free(ptr);
//sceIoClose(BstdFile->fd);
//BstdFileDestroy(BstdFile);
/* Mad is no longer used, the structures that were initialized must
* now be cleared.
*/
mad_synth_finish(&Synth);
mad_frame_finish(&Frame);
mad_stream_finish(&Stream);
/* If the output buffer is not empty and no error occurred during
* the last write, then flush it.
*/
/*if(OutputPtr!=OutputBuffer && Status!=2)
{
size_t BufferSize=OutputPtr-OutputBuffer;
if(fwrite(OutputBuffer,1,BufferSize,OutputFp)!=BufferSize)
{
fprintf(stderr,"%s: PCM write error (%s).\n",
ProgName,strerror(errno));
Status=2;
}
} */
/* Accounting report if no error occurred. */
if (!Status) {
char Buffer[80];
/* The duration timer is converted to a human readable string
* with the versatile, but still constrained mad_timer_string()
* function, in a fashion not unlike strftime(). The main
* difference is that the timer is broken into several
* values according some of it's arguments. The units and
* fracunits arguments specify the intended conversion to be
* executed.
*
* The conversion unit (MAD_UNIT_MINUTES in our example) also
* specify the order and kind of conversion specifications
* that can be used in the format string.
*
* It is best to examine libmad's timer.c source-code for details
* of the available units, fraction of units, their meanings,
* the format arguments, etc.
*/
mad_timer_string(Timer, Buffer, "%lu:%02lu.%03u", MAD_UNITS_MINUTES, MAD_UNITS_MILLISECONDS, 0);
//printf("%lu frames decoded (%s).\n", FrameCount, Buffer);
//sceDisplayWaitVblankStart();
sceKernelDelayThread(500000);
}
}
void MP3_End()
{
MP3_Stop();
pspAudioSetChannelCallback(myChannel, 0,0);
MP3_FreeTune();
}
//////////////////////////////////////////////////////////////////////
// Functions - Local and not public
//////////////////////////////////////////////////////////////////////
// This is the initialiser and module loader
// This is a general call, which loads the module from the
// given address into the modplayer
//
// It basically loads into an internal format, so once this function
// has returned the buffer at 'data' will not be needed again.
int MP3_Load(char *filename)
{
int fd;
eos = 0;
//psp_stats pstat;
//sceIoGetstat(filename, &pstat);
if ((fd = sceIoOpen(filename, PSP_O_RDONLY, 0777)) > 0) {
// opened file, so get size now
size = sceIoLseek(fd, 0, PSP_SEEK_END);
sceIoLseek(fd, 0, PSP_SEEK_SET);
ptr = (unsigned char *) malloc(size + 8);
memset(ptr, 0, size + 8);
if (ptr != 0) { // Read file in
sceIoRead(fd, ptr, size);
} else {
printf("Error allocing\n");
sceIoClose(fd);
return 1;
}
// Close file
sceIoClose(fd);
} else {
return 1;
}
return 0;
}
// This function initialises for playing, and starts
int MP3_Play()
{
// See if I'm already playing
if (isPlaying)
return FALSE;
isPlaying = TRUE;
return TRUE;
}
void MP3_Pause()
{
isPlaying = !isPlaying;
}
int MP3_Stop()
{
//stop playing
isPlaying = FALSE;
//clear buffer
memset(OutputBuffer, 0, OUTPUT_BUFFER_SIZE);
OutputPtr = (unsigned char *) OutputBuffer;
//seek to beginning of file
//sceIoLseek(BstdFile->fd, 0, SEEK_SET);
return TRUE;
}
void MP3_GetTimeString(char *dest)
{
mad_timer_string(Timer, dest, "%02lu:%02u:%02u", MAD_UNITS_HOURS, MAD_UNITS_MILLISECONDS, 0);
}
int MP3_EndOfStream()
{
if (eos == 1)
{
eos=0;
return 1;
}
return 0;
}
makefile
Code: Select all
TARGET = game_PSP
OBJS = graphics.o framebuffer.o mp3player.o main.o
INCDIR =
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)
LIBDIR =
LDFLAGS =
LIBS = -lpspgu -lpng -lz -lm -lmad -lpspaudiolib -lpspaudio -lpsppower
EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = game PSP
PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak