+#include <pspkernel.h>
+#include <pspsdk.h>
+#include <pspaudiocodec.h>
+
+#include "../../Pico/PicoInt.h"
+#include "../../Pico/sound/mix.h"
+#include "../common/lprintf.h"
+
+int mp3_last_error = 0;
+
+static int initialized = 0;
+static SceUID thread_job_sem = -1;
+static SceUID thread_busy_sem = -1;
+static int thread_exit = 0;
+
+// MPEG-1, layer 3
+static int bitrates[] = { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 };
+//static int samplerates[] = { 44100, 48000, 32000, 0 };
+
+#define MIN_INFRAME_SIZE 96
+#define IN_BUFFER_SIZE (2*1024)
+
+static unsigned long mp3_codec_struct[65] __attribute__((aligned(64)));
+
+static unsigned char mp3_src_buffer[2][IN_BUFFER_SIZE] __attribute__((aligned(64)));
+static short mp3_mix_buffer[2][1152*2] __attribute__((aligned(64)));
+static int working_buf = 0;
+
+static const char *mp3_fname = NULL;
+static SceUID mp3_handle = -1;
+static int mp3_src_pos = 0, mp3_src_size = 0;
+
+static int decode_thread(SceSize args, void *argp);
+
+
+static void psp_sem_lock(SceUID sem)
+{
+ int ret = sceKernelWaitSema(sem, 1, 0);
+ if (ret < 0) lprintf("sceKernelWaitSema(%08x) failed with %08x\n", sem, ret);
+}
+
+static void psp_sem_unlock(SceUID sem)
+{
+ int ret = sceKernelSignalSema(sem, 1);
+ if (ret < 0) lprintf("sceKernelSignalSema(%08x) failed with %08x\n", sem, ret);
+}
+
+// only accepts MPEG-1, layer3
+static int find_sync_word(unsigned char *data, int len)
+{
+ int i;
+ for (i = 0; i < len-1; i++)
+ {
+ if ( data[i+0] != 0xff) continue;
+ if ((data[i+1] & 0xfe) == 0xfa) return i;
+ i++;
+ }
+ return -1;
+}
+
+static int read_next_frame(int which_buffer)
+{
+ int i, bytes_read, frame_offset;
+ int bitrate, padding, frame_size = 0;
+
+ for (i = 0; i < 32; i++)
+ {
+ bytes_read = sceIoRead(mp3_handle, mp3_src_buffer[which_buffer], sizeof(mp3_src_buffer[which_buffer]));
+ mp3_src_pos += bytes_read;
+ if (bytes_read < MIN_INFRAME_SIZE) {
+ mp3_src_pos = mp3_src_size;
+ return 0; // EOF/IO failure
+ }
+ frame_offset = find_sync_word(mp3_src_buffer[which_buffer], bytes_read);
+ if (frame_offset < 0) {
+ lprintf("missing syncword, foffs=%i\n", mp3_src_pos - bytes_read);
+ mp3_src_pos--;
+ sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET);
+ continue;
+ }
+ if (bytes_read - frame_offset < 4) {
+ lprintf("syncword @ EOB, foffs=%i\n", mp3_src_pos - bytes_read);
+ mp3_src_pos--;
+ sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET);
+ continue;
+ }
+
+ bitrate = mp3_src_buffer[which_buffer][frame_offset+2] >> 4;
+ padding = (mp3_src_buffer[which_buffer][frame_offset+2] & 2) >> 1;
+
+ frame_size = 144000*bitrates[bitrate]/44100 + padding;
+ if (frame_size <= 0) {
+ lprintf("bad frame, foffs=%i\n", mp3_src_pos - bytes_read);
+ continue; // bad frame
+ }
+
+ if (bytes_read - frame_offset < frame_size)
+ {
+ lprintf("unfit, foffs=%i\n", mp3_src_pos - bytes_read);
+ mp3_src_pos -= bytes_read - frame_offset;
+ if (mp3_src_size - mp3_src_pos < frame_size) {
+ mp3_src_pos = mp3_src_size;
+ return 0; // EOF
+ }
+ sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET);
+ continue; // didn't fit, re-read..
+ }
+
+ if (frame_offset) {
+ //lprintf("unaligned, foffs=%i, offs=%i\n", mp3_src_pos - bytes_read, frame_offset);
+ memmove(mp3_src_buffer[which_buffer], mp3_src_buffer[which_buffer] + frame_offset, frame_size);
+ }
+
+ // align for next frame read
+ mp3_src_pos -= bytes_read - (frame_offset + frame_size);
+ sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET);
+
+ break;
+ }
+
+ return frame_size > 0 ? frame_size : -1;
+}
+
+
+static SceUID load_start_module(const char *prxname)