Hardware MP3 Player

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

Moderators: cheriff, TyRaNiD

Post Reply
cools
Posts: 46
Joined: Sat Mar 04, 2006 12:57 pm

Hardware MP3 Player

Post by cools »

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

#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&#91;&#93; = &#123;0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 &#125;;
static int bitrates_v2&#91;&#93; = &#123;0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 &#125;;

static int samplerates&#91;4&#93;&#91;3&#93; = 
&#123;
    &#123;11025, 12000, 8000,&#125;,//mpeg 2.5
    &#123;0, 0, 0,&#125;, //reserved
    &#123;22050, 24000, 16000,&#125;,//mpeg 2
    &#123;44100, 48000, 32000&#125;//mpeg 1
&#125;;

unsigned long mp3_codec_buffer&#91;65&#93; __attribute__&#40;&#40;aligned&#40;64&#41;&#41;&#41;;
short mp3_mix_buffer&#91;1152 * 2&#93; __attribute__&#40;&#40;aligned&#40;64&#41;&#41;&#41;;

short mp3_output_buffer&#91;4&#93;&#91;1152 * 2&#93; __attribute__&#40;&#40;aligned&#40;64&#41;&#41;&#41;;
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&#40;char *path&#41;
&#123;
    u32 loadResult;
    u32 startResult;
    int status;

    loadResult = sceKernelLoadModule&#40;path, 0, NULL&#41;;
    if &#40;loadResult & 0x80000000&#41;
		return -1;
    else
		startResult =
	    sceKernelStartModule&#40;loadResult, 0, NULL, &status, NULL&#41;;

    if &#40;loadResult != startResult&#41;
	return -2;

    return 0;
&#125;

void Mp3_init&#40;&#41;
&#123;  
   LoadStartModule&#40;"flash0&#58;/kd/me_for_vsh.prx"&#41;;
   
   LoadStartModule&#40;"flash0&#58;/kd/videocodec.prx"&#41;;
   
   LoadStartModule&#40;"flash0&#58;/kd/audiocodec.prx"&#41;;
   
   LoadStartModule&#40;"flash0&#58;/kd/mpegbase.prx"&#41;;
   
   LoadStartModule&#40;"flash0&#58;/kd/mpeg_vsh.prx"&#41;;
   
   sceMpegInit&#40;&#41;;
&#125;

void Mp3_stop&#40;&#41;
&#123;
    if&#40;mp3_handle >= 0&#41;&#123;
        sceIoClose&#40;mp3_handle&#41;;
	&#125;
	if&#40;mp3_data_buffer&#41;&#123;
		free&#40;mp3_data_buffer&#41;;
	&#125;
	if &#40; mp3_getEDRAM &#41; &#123;
		sceAudiocodecReleaseEDRAM&#40;mp3_codec_buffer&#41;;
	&#125;
	//sceKernelSuspendThread &#40;mp3_thread&#41;; ? Is there something special I need to do here?
&#125;

void Mp3_load&#40;char *filename&#41;
&#123;
   mp3_handle = sceIoOpen&#40;filename, PSP_O_RDONLY, 0777&#41;;
   if &#40;  ! mp3_handle &#41;&#123;
      Mp3_stop&#40;&#41;; 
	&#125;
   mp3_channels = 2;
   
   size = sceIoLseek32&#40;mp3_handle, 0, PSP_SEEK_END&#41;;
   sceIoLseek32&#40;mp3_handle, 0, PSP_SEEK_SET&#41;;
   mp3_data_start = SeekNextFrame&#40;mp3_handle&#41;;
   if &#40;mp3_data_start < 0&#41;&#123;
		Mp3_stop&#40;&#41;; 
	&#125;
	
	size -= mp3_data_start;
   
   memset&#40;mp3_codec_buffer, 0, sizeof&#40;mp3_codec_buffer&#41;&#41;;
	if &#40; sceAudiocodecCheckNeedMem&#40;mp3_codec_buffer, 0x1002&#41; < 0 &#41; &#123;
		Mp3_stop&#40;&#41;;
	&#125;
	if &#40; sceAudiocodecGetEDRAM&#40;mp3_codec_buffer, 0x1002&#41; < 0 &#41; &#123;
        Mp3_stop&#40;&#41;;
	&#125;
   mp3_getEDRAM = 1;
   
   if &#40; sceAudiocodecInit&#40;mp3_codec_buffer, 0x1002&#41; < 0 &#41; &#123;
      Mp3_stop&#40;&#41;; 
   &#125;
   
   eos = 0;
&#125;

int Mp3thread&#40;SceSize args, void *argp&#41;
&#123;
   while&#40; !eos &#41; &#123;
      memset&#40;mp3_mix_buffer, 0, mp3_sample_per_frame*2*2&#41;; 
      unsigned char mp3_header_buf&#91;4&#93;;
      if &#40; sceIoRead&#40; mp3_handle, mp3_header_buf, 4 &#41; != 4 &#41; &#123;
         eos = 1;
         continue;
      &#125;
      int mp3_header = mp3_header_buf&#91;0&#93;;
      mp3_header = &#40;mp3_header<<8&#41; | mp3_header_buf&#91;1&#93;;
      mp3_header = &#40;mp3_header<<8&#41; | mp3_header_buf&#91;2&#93;;
      mp3_header = &#40;mp3_header<<8&#41; | mp3_header_buf&#91;3&#93;;
       
      int bitrate = &#40;mp3_header & 0xf000&#41; >> 12;
      int padding = &#40;mp3_header & 0x200&#41; >> 9;
      int version = &#40;mp3_header & 0x180000&#41; >> 19;
      mp3_samplerate = samplerates&#91;version&#93;&#91; &#40;mp3_header & 0xC00&#41; >> 10 &#93;;
	  
        if &#40;&#40;bitrate > 14&#41; || &#40;version == 1&#41; || &#40;mp3_samplerate == 0&#41; || &#40;bitrate == 0&#41;&#41;//invalid frame, look for the next one
        &#123;
            mp3_data_start = SeekNextFrame&#40;mp3_handle&#41;;
            if&#40;mp3_data_start < 0&#41;
            &#123;
                eos = 1;
                continue;
            &#125;
			size -= mp3_data_start;
            continue;
        &#125;
	  
	  if &#40;version == 3&#41; //mpeg-1
      &#123;
		mp3_sample_per_frame = 1152;
        frame_size = 144000*bitrates&#91;bitrate&#93;/mp3_samplerate + padding;
      &#125;
      else
      &#123;
        mp3_sample_per_frame = 576;
        frame_size = 72000*bitrates_v2&#91;bitrate&#93;/mp3_samplerate + padding;
      &#125;
       
	if &#40;achannel_set == 0&#41; &#123;
		audio_channel = sceAudioChReserve&#40;1, mp3_sample_per_frame , PSP_AUDIO_FORMAT_STEREO&#41;;
		achannel_set = 1;
	&#125;
       
      if &#40; mp3_data_buffer &#41;
         free&#40;mp3_data_buffer&#41;;
      mp3_data_buffer = &#40;u8*&#41;memalign&#40;64, frame_size&#41;;
       
      sceIoLseek32&#40;mp3_handle, mp3_data_start, PSP_SEEK_SET&#41;; //seek back
	  
	  size -= frame_size;
      if &#40; sceIoRead&#40; mp3_handle, mp3_data_buffer, frame_size &#41; != frame_size &#41;
	  &#123;
         eos = 1;
         continue;
      &#125;
       
      mp3_data_start += frame_size;
       
      mp3_codec_buffer&#91;6&#93; = &#40;unsigned long&#41;mp3_data_buffer;
      mp3_codec_buffer&#91;8&#93; = &#40;unsigned long&#41;mp3_mix_buffer;
       
      mp3_codec_buffer&#91;7&#93; = mp3_codec_buffer&#91;10&#93; = frame_size;
      mp3_codec_buffer&#91;9&#93; = mp3_sample_per_frame * 4;
   
      int res = sceAudiocodecDecode&#40;mp3_codec_buffer, 0x1002&#41;;
      if &#40; res < 0 &#41; &#123;
		 mp3_data_start = SeekNextFrame&#40;mp3_handle&#41;;
		 if &#40;mp3_data_start < 0&#41;
		 &#123;
			eos = 1;
			continue;
		 &#125;
		 size -= mp3_data_start;
		 continue;
      &#125;
      memcpy&#40;mp3_output_buffer&#91;mp3_output_index&#93;, mp3_mix_buffer, mp3_sample_per_frame*4&#41;;
      sceAudioOutputBlocking&#40;audio_channel, PSP_AUDIO_VOLUME_MAX, mp3_output_buffer&#91;mp3_output_index&#93;&#41;;
      mp3_output_index = &#40;mp3_output_index+1&#41;%4;
      samplesdecoded = mp3_sample_per_frame;
   &#125;
   return 0;
&#125;

void Mp3_play&#40;&#41;
&#123;
	if &#40;module_started == 0&#41; &#123;
		module_start&#40;0, NULL&#41;;
		module_started = 1;
	&#125; else &#123;
	sceKernelResumeThread&#40;mp3_thread&#41;;
	&#125;
&#125;

void Mp3_pause&#40;&#41;
&#123;
	if &#40;ispaused&#41; &#123;
		sceKernelResumeThread&#40;mp3_thread&#41;;
		ispaused = 0;
	&#125; else &#123;
		sceKernelSuspendThread&#40;mp3_thread&#41;;
		ispaused = 1;
	&#125;
&#125;

int Mp3_EndOfStream&#40;&#41;
&#123;
	return eos;
&#125;

int SeekNextFrame&#40;SceUID fd&#41;
&#123;
    int offset = 0;
    unsigned char buf&#91;1024&#93;;
    unsigned char *pBuffer;
    int i;
    int size = 0;

    offset = sceIoLseek32&#40;fd, 0, PSP_SEEK_CUR&#41;;
    sceIoRead&#40;fd, buf, sizeof&#40;buf&#41;&#41;;
    if &#40;!strncmp&#40;&#40;char*&#41;buf, "ID3", 3&#41; || !strncmp&#40;&#40;char*&#41;buf, "ea3", 3&#41;&#41; //skip past id3v2 header, which can cause a false sync to be found
    &#123;
        //get the real size from the syncsafe int
        size = buf&#91;6&#93;;
        size = &#40;size<<7&#41; | buf&#91;7&#93;;
        size = &#40;size<<7&#41; | buf&#91;8&#93;;
        size = &#40;size<<7&#41; | buf&#91;9&#93;;

        size += 10;

        if &#40;buf&#91;5&#93; & 0x10&#41; //has footer
            size += 10;
    &#125;

    sceIoLseek32&#40;fd, offset + size, PSP_SEEK_SET&#41;; //now seek for a sync
    while&#40;1&#41; 
    &#123;
        offset = sceIoLseek32&#40;fd, 0, PSP_SEEK_CUR&#41;;
        size = sceIoRead&#40;fd, buf, sizeof&#40;buf&#41;&#41;;

        if &#40;size <= 2&#41;//at end of file
            return -1;
    
        if &#40;!strncmp&#40;&#40;char*&#41;buf, "EA3", 3&#41;&#41;//oma mp3 files have non-safe ints in the EA3 header
        &#123;
            sceIoLseek32&#40;fd, &#40;buf&#91;4&#93;<<8&#41;+buf&#91;5&#93;, PSP_SEEK_CUR&#41;;
            continue;
        &#125;

        pBuffer = buf;
        for&#40; i = 0; i < size; i++&#41;
        &#123;
            //if this is a valid frame sync &#40;0xe0 is for mpeg version 2.5,2+1&#41;
            if &#40; &#40;pBuffer&#91;i&#93; == 0xff&#41; && &#40;&#40;pBuffer&#91;i+1&#93; & 0xE0&#41; == 0xE0&#41;&#41;
            &#123;
                offset += i;
                sceIoLseek32&#40;fd, offset, PSP_SEEK_SET&#41;;
                return offset;
            &#125;
        &#125;
       //go back two bytes to catch any syncs that on the boundary
        sceIoLseek32&#40;fd, -2, PSP_SEEK_CUR&#41;;
    &#125; 
&#125;

int module_start&#40;SceSize args, void *argp&#41;
&#123;
	/* Create a high priority thread */
	mp3_thread = sceKernelCreateThread&#40;"FORMP3", Mp3thread, 0x12, 0x04000, 0, NULL&#41;;
	if&#40;mp3_thread >= 0&#41;
	&#123;
		sceKernelStartThread&#40;mp3_thread, args, argp&#41;;
	&#125;
	return 0;
&#125;
^Based on work by cooleyes and joek2100, so many thanks to them.
Art
Posts: 642
Joined: Wed Nov 09, 2005 8:01 am

Post by Art »

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.
Smong
Posts: 82
Joined: Tue Sep 04, 2007 4:44 am

Post by Smong »

I notice your cleanup function is missing some code, original:

Code: Select all

void Mp3_stop&#40;&#41;
&#123;
    if&#40;mp3_handle >= 0&#41;&#123;
        sceIoClose&#40;mp3_handle&#41;;
   &#125;
   if&#40;mp3_data_buffer&#41;&#123;
      free&#40;mp3_data_buffer&#41;;
   &#125;
   if &#40; mp3_getEDRAM &#41; &#123;
      sceAudiocodecReleaseEDRAM&#40;mp3_codec_buffer&#41;;
   &#125;
   //sceKernelSuspendThread &#40;mp3_thread&#41;; ? Is there something special I need to do here?
&#125;
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: Select all

    if&#40;mp3_handle >= 0&#41;&#123;
        sceIoClose&#40;mp3_handle&#41;;
        mp3_handle = -1;
   &#125;
Also depending on how you are loading/calling your code you may want to change this:

Code: Select all

void Mp3_play&#40;&#41;
&#123;
   if &#40;module_started == 0&#41; &#123;
      module_start&#40;0, NULL&#41;;
      module_started = 1;
   &#125; else &#123;
   sceKernelResumeThread&#40;mp3_thread&#41;;
   &#125;
&#125;
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.
(+[__]%)
cools
Posts: 46
Joined: Sat Mar 04, 2006 12:57 pm

Post by cools »

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?
Smong
Posts: 82
Joined: Tue Sep 04, 2007 4:44 am

Post by Smong »

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.
(+[__]%)
cools
Posts: 46
Joined: Sat Mar 04, 2006 12:57 pm

Post by cools »

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.
cools
Posts: 46
Joined: Sat Mar 04, 2006 12:57 pm

Post by cools »

NVM: You have to wait a few sec, before you can switch songs...
Post Reply