 |
forums.ps2dev.org Homebrew PS2, PSP & PS3 Development Discussions
|
| View previous topic :: View next topic |
| Author |
Message |
cools
Joined: 04 Mar 2006 Posts: 46
|
Posted: Fri Oct 05, 2007 11:30 am Post subject: Hardware MP3 Player |
|
|
Well I have created a hardware mp3 player, for general use, but it has some flaws. Now I am not the greatest c/c++ coder in the world and I wont pretend to be, but I do what I can to get by. Currently the twon main problems I am having are: after you stop a song, you can't start another one (program will freeze) and it has issues playing alongside a running app...
The code is as follows:
| Code: | #include <pspkernel.h>
#include <stdio.h>
#include <stdlib.h>
#include <pspkernel.h>
#include <pspsdk.h>
#include <pspaudiocodec.h>
#include <pspaudio.h>
#include <string.h>
#include <malloc.h>
#include <pspmpeg.h>
#include "hmp3.h"
static int bitrates[] = {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 };
static int bitrates_v2[] = {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 };
static int samplerates[4][3] =
{
{11025, 12000, 8000,},//mpeg 2.5
{0, 0, 0,}, //reserved
{22050, 24000, 16000,},//mpeg 2
{44100, 48000, 32000}//mpeg 1
};
unsigned long mp3_codec_buffer[65] __attribute__((aligned(64)));
short mp3_mix_buffer[1152 * 2] __attribute__((aligned(64)));
short mp3_output_buffer[4][1152 * 2] __attribute__((aligned(64)));
int mp3_output_index = 0;
SceUID mp3_handle;
u8* mp3_data_buffer;
u16 mp3_data_align;
u32 mp3_sample_per_frame;
u16 mp3_channel_mode;
u32 mp3_data_start;
u32 mp3_data_size;
u8 mp3_getEDRAM;
u32 mp3_channels;
u32 mp3_samplerate;
int frame_size;
int size;
int eos = 0;
int achannel_set;
int samplesdecoded;
int audio_channel;
int module_started = 0;
int mp3_thread;
int ispaused = 0;
static int LoadStartModule(char *path)
{
u32 loadResult;
u32 startResult;
int status;
loadResult = sceKernelLoadModule(path, 0, NULL);
if (loadResult & 0x80000000)
return -1;
else
startResult =
sceKernelStartModule(loadResult, 0, NULL, &status, NULL);
if (loadResult != startResult)
return -2;
return 0;
}
void Mp3_init()
{
LoadStartModule("flash0:/kd/me_for_vsh.prx");
LoadStartModule("flash0:/kd/videocodec.prx");
LoadStartModule("flash0:/kd/audiocodec.prx");
LoadStartModule("flash0:/kd/mpegbase.prx");
LoadStartModule("flash0:/kd/mpeg_vsh.prx");
sceMpegInit();
}
void Mp3_stop()
{
if(mp3_handle >= 0){
sceIoClose(mp3_handle);
}
if(mp3_data_buffer){
free(mp3_data_buffer);
}
if ( mp3_getEDRAM ) {
sceAudiocodecReleaseEDRAM(mp3_codec_buffer);
}
//sceKernelSuspendThread (mp3_thread); ? Is there something special I need to do here?
}
void Mp3_load(char *filename)
{
mp3_handle = sceIoOpen(filename, PSP_O_RDONLY, 0777);
if ( ! mp3_handle ){
Mp3_stop();
}
mp3_channels = 2;
size = sceIoLseek32(mp3_handle, 0, PSP_SEEK_END);
sceIoLseek32(mp3_handle, 0, PSP_SEEK_SET);
mp3_data_start = SeekNextFrame(mp3_handle);
if (mp3_data_start < 0){
Mp3_stop();
}
size -= mp3_data_start;
memset(mp3_codec_buffer, 0, sizeof(mp3_codec_buffer));
if ( sceAudiocodecCheckNeedMem(mp3_codec_buffer, 0x1002) < 0 ) {
Mp3_stop();
}
if ( sceAudiocodecGetEDRAM(mp3_codec_buffer, 0x1002) < 0 ) {
Mp3_stop();
}
mp3_getEDRAM = 1;
if ( sceAudiocodecInit(mp3_codec_buffer, 0x1002) < 0 ) {
Mp3_stop();
}
eos = 0;
}
int Mp3thread(SceSize args, void *argp)
{
while( !eos ) {
memset(mp3_mix_buffer, 0, mp3_sample_per_frame*2*2);
unsigned char mp3_header_buf[4];
if ( sceIoRead( mp3_handle, mp3_header_buf, 4 ) != 4 ) {
eos = 1;
continue;
}
int mp3_header = mp3_header_buf[0];
mp3_header = (mp3_header<<8) | mp3_header_buf[1];
mp3_header = (mp3_header<<8) | mp3_header_buf[2];
mp3_header = (mp3_header<<8) | mp3_header_buf[3];
int bitrate = (mp3_header & 0xf000) >> 12;
int padding = (mp3_header & 0x200) >> 9;
int version = (mp3_header & 0x180000) >> 19;
mp3_samplerate = samplerates[version][ (mp3_header & 0xC00) >> 10 ];
if ((bitrate > 14) || (version == 1) || (mp3_samplerate == 0) || (bitrate == 0))//invalid frame, look for the next one
{
mp3_data_start = SeekNextFrame(mp3_handle);
if(mp3_data_start < 0)
{
eos = 1;
continue;
}
size -= mp3_data_start;
continue;
}
if (version == 3) //mpeg-1
{
mp3_sample_per_frame = 1152;
frame_size = 144000*bitrates[bitrate]/mp3_samplerate + padding;
}
else
{
mp3_sample_per_frame = 576;
frame_size = 72000*bitrates_v2[bitrate]/mp3_samplerate + padding;
}
if (achannel_set == 0) {
audio_channel = sceAudioChReserve(1, mp3_sample_per_frame , PSP_AUDIO_FORMAT_STEREO);
achannel_set = 1;
}
if ( mp3_data_buffer )
free(mp3_data_buffer);
mp3_data_buffer = (u8*)memalign(64, frame_size);
sceIoLseek32(mp3_handle, mp3_data_start, PSP_SEEK_SET); //seek back
size -= frame_size;
if ( sceIoRead( mp3_handle, mp3_data_buffer, frame_size ) != frame_size )
{
eos = 1;
continue;
}
mp3_data_start += frame_size;
mp3_codec_buffer[6] = (unsigned long)mp3_data_buffer;
mp3_codec_buffer[8] = (unsigned long)mp3_mix_buffer;
mp3_codec_buffer[7] = mp3_codec_buffer[10] = frame_size;
mp3_codec_buffer[9] = mp3_sample_per_frame * 4;
int res = sceAudiocodecDecode(mp3_codec_buffer, 0x1002);
if ( res < 0 ) {
mp3_data_start = SeekNextFrame(mp3_handle);
if (mp3_data_start < 0)
{
eos = 1;
continue;
}
size -= mp3_data_start;
continue;
}
memcpy(mp3_output_buffer[mp3_output_index], mp3_mix_buffer, mp3_sample_per_frame*4);
sceAudioOutputBlocking(audio_channel, PSP_AUDIO_VOLUME_MAX, mp3_output_buffer[mp3_output_index]);
mp3_output_index = (mp3_output_index+1)%4;
samplesdecoded = mp3_sample_per_frame;
}
return 0;
}
void Mp3_play()
{
if (module_started == 0) {
module_start(0, NULL);
module_started = 1;
} else {
sceKernelResumeThread(mp3_thread);
}
}
void Mp3_pause()
{
if (ispaused) {
sceKernelResumeThread(mp3_thread);
ispaused = 0;
} else {
sceKernelSuspendThread(mp3_thread);
ispaused = 1;
}
}
int Mp3_EndOfStream()
{
return eos;
}
int SeekNextFrame(SceUID fd)
{
int offset = 0;
unsigned char buf[1024];
unsigned char *pBuffer;
int i;
int size = 0;
offset = sceIoLseek32(fd, 0, PSP_SEEK_CUR);
sceIoRead(fd, buf, sizeof(buf));
if (!strncmp((char*)buf, "ID3", 3) || !strncmp((char*)buf, "ea3", 3)) //skip past id3v2 header, which can cause a false sync to be found
{
//get the real size from the syncsafe int
size = buf[6];
size = (size<<7) | buf[7];
size = (size<<7) | buf[8];
size = (size<<7) | buf[9];
size += 10;
if (buf[5] & 0x10) //has footer
size += 10;
}
sceIoLseek32(fd, offset + size, PSP_SEEK_SET); //now seek for a sync
while(1)
{
offset = sceIoLseek32(fd, 0, PSP_SEEK_CUR);
size = sceIoRead(fd, buf, sizeof(buf));
if (size <= 2)//at end of file
return -1;
if (!strncmp((char*)buf, "EA3", 3))//oma mp3 files have non-safe ints in the EA3 header
{
sceIoLseek32(fd, (buf[4]<<8)+buf[5], PSP_SEEK_CUR);
continue;
}
pBuffer = buf;
for( i = 0; i < size; i++)
{
//if this is a valid frame sync (0xe0 is for mpeg version 2.5,2+1)
if ( (pBuffer[i] == 0xff) && ((pBuffer[i+1] & 0xE0) == 0xE0))
{
offset += i;
sceIoLseek32(fd, offset, PSP_SEEK_SET);
return offset;
}
}
//go back two bytes to catch any syncs that on the boundary
sceIoLseek32(fd, -2, PSP_SEEK_CUR);
}
}
int module_start(SceSize args, void *argp)
{
/* Create a high priority thread */
mp3_thread = sceKernelCreateThread("FORMP3", Mp3thread, 0x12, 0x04000, 0, NULL);
if(mp3_thread >= 0)
{
sceKernelStartThread(mp3_thread, args, argp);
}
return 0;
}
|
^Based on work by cooleyes and joek2100, so many thanks to them. |
|
| Back to top |
|
 |
Art
Joined: 09 Nov 2005 Posts: 647
|
Posted: Fri Oct 05, 2007 2:54 pm Post subject: |
|
|
| Quote: | | after you stop a song, you can't start another one (program will freeze) |
I've got the same problem. My main program doesn't freeze, but once a
song has started playing, that's it. I can't start another one, or the same song again. |
|
| Back to top |
|
 |
Smong
Joined: 04 Sep 2007 Posts: 82
|
Posted: Fri Oct 05, 2007 8:41 pm Post subject: |
|
|
I notice your cleanup function is missing some code, original:
| Code: | void Mp3_stop()
{
if(mp3_handle >= 0){
sceIoClose(mp3_handle);
}
if(mp3_data_buffer){
free(mp3_data_buffer);
}
if ( mp3_getEDRAM ) {
sceAudiocodecReleaseEDRAM(mp3_codec_buffer);
}
//sceKernelSuspendThread (mp3_thread); ? Is there something special I need to do here?
} |
You should be setting all those variables to invalid values so you don't attempt to make future reads or frees on them. For example:
| Code: | if(mp3_handle >= 0){
sceIoClose(mp3_handle);
mp3_handle = -1;
} |
Also depending on how you are loading/calling your code you may want to change this:
| Code: | void Mp3_play()
{
if (module_started == 0) {
module_start(0, NULL);
module_started = 1;
} else {
sceKernelResumeThread(mp3_thread);
}
} | I would move module_started = 1 to be inside the module_start() function.
While I didn't look at all the code I notice Mp3_stop() is only called from Mp3_load(), assuming the mp3 doesn't loop it doesn't look like the mp3 will get cleaned up when it ends. _________________ (+[__]%) |
|
| Back to top |
|
 |
cools
Joined: 04 Mar 2006 Posts: 46
|
Posted: Sat Oct 06, 2007 5:44 am Post subject: |
|
|
| Well I did what you said, and it appears to be working! Thanks! The only problem that I have found out, after some testing, is that after so many seconds of playing the psp freezes and then shuts off. Any ideas? |
|
| Back to top |
|
 |
Smong
Joined: 04 Sep 2007 Posts: 82
|
Posted: Sat Oct 06, 2007 7:33 am Post subject: |
|
|
It could either be an exception (install psplinkusb to see it) or it could happen if you don't call sceKernelDelayThread or sceDisplayWaitVblankStart, the PSP will think your program has frozen and switch itself off. _________________ (+[__]%) |
|
| Back to top |
|
 |
cools
Joined: 04 Mar 2006 Posts: 46
|
Posted: Sat Oct 06, 2007 7:58 am Post subject: |
|
|
Major Thanks Smong! I got it working!
EDIT: Well, the mp3 player has some issues when the song ends and you don't stop it yourself, but I'm pretty sure I'll get it figured out. |
|
| Back to top |
|
 |
cools
Joined: 04 Mar 2006 Posts: 46
|
Posted: Mon Oct 08, 2007 5:00 am Post subject: |
|
|
| NVM: You have to wait a few sec, before you can switch songs... |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|