1
0
mirror of https://github.com/parasyte/alt64 synced 2024-11-10 11:15:11 -05:00
alt64/src/mp3.c

434 lines
11 KiB
C
Raw Normal View History

2017-10-06 09:48:52 -04:00
//
// Copyright (c) 2017 The Altra64 project contributors
// Portions (c) 2013 saturnu (Alt64) based on libdragon, Neo64Menu, ED64IO, libn64-hkz, libmikmod
// See LICENSE file in the project root for full license information.
//
2014-06-29 01:10:11 -04:00
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libdragon.h>
2014-06-29 01:10:11 -04:00
#include <mad.h>
2014-06-30 03:16:08 -04:00
#include "mp3.h"
#include "ff.h"
2014-06-29 01:10:11 -04:00
static struct mad_stream Stream;
static struct mad_header Header;
static struct mad_frame Frame;
static struct mad_synth Synth;
static mad_timer_t Timer;
typedef struct {
2014-06-30 01:37:40 -04:00
short left;
short right;
2014-06-29 01:10:11 -04:00
} Sample;
static int eos;
#define INPUT_BUFFER_SIZE 2048
static unsigned char fileBuffer[INPUT_BUFFER_SIZE];
static unsigned char readBuffer[INPUT_BUFFER_SIZE];
static int useReadBuffer;
static int readPos;
static unsigned int readLen;
static int samplesRead;
char * mp3File;
char * mp3Fd;
long mp3File_fptr=0;
int mp3File_fsize;
extern int gBrowser;
extern char path[1024];
extern void c2wstrcpy(void *dst, void *src);
extern void c2wstrcat(void *dst, void *src);
2014-06-30 01:37:40 -04:00
static int mp3_seek(char* fd, int offset, int whence) {
//todo filesize and mp3File_fptr;
long offs = 0;
// libff routine
switch (whence)
{
case SEEK_SET:
offs = offset;
break;
case SEEK_CUR:
offs = mp3File_fptr + offset;
break;
case SEEK_END:
offs = mp3File_fsize + offset;
break;
}
//f_lseek(&mp3File, offs);
mp3File_fptr=offs;
2014-06-29 01:10:11 -04:00
2014-06-30 01:37:40 -04:00
return offs;
2014-06-29 01:10:11 -04:00
}
2014-06-30 01:37:40 -04:00
static int mp3_size(char* fd) {
FRESULT result;
FILINFO fno;
//TODO: error
result = f_stat (fd, &fno);
mp3File_fsize = fno.fsize;
2014-06-30 01:37:40 -04:00
return mp3File_fsize;
2014-06-29 01:10:11 -04:00
}
static void _f_read(char* fname, unsigned char *readBuffer, int size){
/*
2014-06-30 01:37:40 -04:00
FatRecord rec_tmpf;
u8 resp=0;
resp = fatOpenFileByName(fname, 0); //err if not found ^^
2014-06-29 01:10:11 -04:00
2014-06-30 01:37:40 -04:00
int fsize = file.sec_available*512; //fsize in bytes
mp3File_fsize = fsize;
2014-06-29 01:10:11 -04:00
2014-06-30 01:37:40 -04:00
//injecting in buffer... slow but working :/
if(file.sec_available*512>=size){
resp = fatReadPartialFile(readBuffer, size/512, mp3File_fptr);
//resp = fatReadFile(readBuffer+mp3File_fptr, size/512);//file.sec_available);
mp3File_fptr+=size;
2014-06-29 01:10:11 -04:00
2014-06-30 01:37:40 -04:00
}
//dma_write_s(buffer, 0xb0000000, fsize);
2014-06-29 01:10:11 -04:00
*/
mp3_size(fname);
FRESULT result;
FIL file;
UINT bytesread;
result = f_open(&file, fname, FA_READ);
if (result == FR_OK)
{
int fsize = f_size(&file);
if ( fsize > size)
{
//todo: set the read pointer
//readBuffer+mp3File_fptr
}
result =
f_read (
&file, /* [IN] File object */
2017-10-23 12:46:05 -04:00
readBuffer, /* [OUT] Buffer to store read data */
size, /* [IN] Number of bytes to read */
&bytesread /* [OUT] Number of bytes read */
);
2017-10-23 12:46:05 -04:00
f_close(&file);
mp3File_fptr+=size;
//dma_write_s(buffer, 0xb0000000, fsize);
}
2014-06-29 01:10:11 -04:00
}
static int mp3_read(char* fd, unsigned char *ptr, int size)
{
2014-06-30 01:37:40 -04:00
int ts=size;
_f_read(fd, ptr, size);
return ts;
2014-06-29 01:10:11 -04:00
}
2014-06-30 01:37:40 -04:00
static int id3_tag_size(unsigned char const *buf, int remaining) {
int size;
2014-06-29 01:10:11 -04:00
2014-06-30 01:37:40 -04:00
if (remaining < 10)
return 0;
2014-06-29 01:10:11 -04:00
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;
}
2014-06-30 01:37:40 -04:00
return size;
2014-06-29 01:10:11 -04:00
}
//Seek next valid frame after ID3/EA3 header
//NOTE: adapted from Music prx 0.55 source
// credit goes to joek2100.
static int MP3_SkipHdr(char* fd)
{
int offset = 0;
unsigned char buf[1024];
unsigned char *pBuffer;
int i;
int size = 0;
offset = mp3_seek(fd, 0, SEEK_CUR);
mp3_read(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;
2014-06-30 01:37:40 -04:00
offset += size;
2014-06-29 01:10:11 -04:00
}
mp3_seek(fd, offset, SEEK_SET);
2014-06-30 01:37:40 -04:00
//now seek for a sync
for ( ;;) {
2014-06-29 01:10:11 -04:00
offset = mp3_seek(fd, 0, SEEK_CUR);
size = mp3_read(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
{
mp3_seek(fd, (buf[4]<<8)+buf[5], SEEK_CUR);
continue;
}
pBuffer = buf;
2014-06-30 01:37:40 -04:00
for( i = 0; i < size; i++) {
2014-06-29 01:10:11 -04:00
//if this is a valid frame sync (0xe0 is for mpeg version 2.5,2+1)
2014-06-30 01:37:40 -04:00
if ( (pBuffer[i] == 0xff) && ((pBuffer[i+1] & 0xE0) == 0xE0) ) {
2014-06-29 01:10:11 -04:00
offset += i;
mp3_seek(fd, offset, SEEK_SET);
return offset;
}
}
2014-06-30 01:37:40 -04:00
//go back two bytes to catch any syncs that on the boundary
2014-06-29 01:10:11 -04:00
mp3_seek(fd, -2, SEEK_CUR);
}
}
2014-06-30 01:37:40 -04:00
static short convertSample(mad_fixed_t Fixed) {
2014-06-29 01:10:11 -04:00
/* Clipping */
if (Fixed >= MAD_F_ONE)
2014-06-30 01:37:40 -04:00
return (32767);
2014-06-29 01:10:11 -04:00
if (Fixed <= -MAD_F_ONE)
2014-06-30 01:37:40 -04:00
return (-32768);
2014-06-29 01:10:11 -04:00
/* Conversion. */
Fixed = Fixed >> (MAD_F_FRACBITS - 15);
2014-06-30 01:37:40 -04:00
return ((short)Fixed);
2014-06-29 01:10:11 -04:00
}
2014-06-30 01:37:40 -04:00
static int fillFileBuffer() {
int leftOver = Stream.bufend - Stream.next_frame;
int want = INPUT_BUFFER_SIZE - leftOver;
// move left-over bytes
if (leftOver > 0)
memmove(fileBuffer, fileBuffer + want, leftOver);
// fill remainder of buffer
unsigned char* bufferPos = fileBuffer + leftOver;
while (want > 0) {
int got = mp3_read(mp3Fd, bufferPos, want);
if (got <= 0)
return 1; // EOF
want -= got;
bufferPos += got;
}
return 0;
2014-06-29 01:10:11 -04:00
}
2014-06-30 01:37:40 -04:00
static void decode() {
while (mad_frame_decode(&Frame, &Stream) == -1) {
if ((Stream.error == MAD_ERROR_BUFLEN) || (Stream.error == MAD_ERROR_BUFPTR)) {
if (fillFileBuffer()) {
eos = 1;
break;
}
mad_stream_buffer(&Stream, fileBuffer, INPUT_BUFFER_SIZE);
}
else if (Stream.error == MAD_ERROR_LOSTSYNC) {
/* LOSTSYNC - due to ID3 tags? */
2014-06-29 01:10:11 -04:00
int tagsize = id3_tag_size(Stream.this_frame, Stream.bufend - Stream.this_frame);
2014-06-30 01:37:40 -04:00
if (tagsize > 0) {
mad_stream_skip (&Stream, tagsize);
2014-06-29 01:10:11 -04:00
continue;
2014-06-30 01:37:40 -04:00
}
}
}
2014-06-29 01:10:11 -04:00
mad_timer_add(&Timer, Frame.header.duration);
2014-06-30 01:37:40 -04:00
mad_synth_frame(&Synth, &Frame);
2014-06-29 01:10:11 -04:00
}
2014-06-30 01:37:40 -04:00
static void convertLeftSamples(Sample* first, Sample* last, const mad_fixed_t* src) {
for (Sample *dst = first; dst != last; ++dst)
dst->left = convertSample(*src++);
2014-06-29 01:10:11 -04:00
}
2014-06-30 01:37:40 -04:00
static void convertRightSamples(Sample* first, Sample* last, const mad_fixed_t* src) {
for (Sample *dst = first; dst != last; ++dst)
dst->right = convertSample(*src++);
2014-06-29 01:10:11 -04:00
}
2014-06-30 01:37:40 -04:00
static void MP3_Callback(void *buffer, unsigned int samplesToWrite) {
2014-06-29 01:10:11 -04:00
Sample *destination = (Sample*)buffer;
2014-06-30 01:37:40 -04:00
while (samplesToWrite > 0) {
while (!eos && (Synth.pcm.length == 0))
decode();
2014-06-29 01:10:11 -04:00
2014-06-30 01:37:40 -04:00
if (eos) {
// done
memset(destination, 0, samplesToWrite*4);
break;
}
2014-06-29 01:10:11 -04:00
unsigned int samplesAvailable = Synth.pcm.length - samplesRead;
2014-06-30 01:37:40 -04:00
if (samplesAvailable > samplesToWrite) {
2014-06-29 01:10:11 -04:00
convertLeftSamples(destination, destination + samplesToWrite, &Synth.pcm.samples[0][samplesRead]);
convertRightSamples(destination, destination + samplesToWrite, &Synth.pcm.samples[1][samplesRead]);
samplesRead += samplesToWrite;
samplesToWrite = 0;
2014-06-30 01:37:40 -04:00
}
else {
2014-06-29 01:10:11 -04:00
convertLeftSamples(destination, destination + samplesAvailable, &Synth.pcm.samples[0][samplesRead]);
convertRightSamples(destination, destination + samplesAvailable, &Synth.pcm.samples[1][samplesRead]);
destination += samplesAvailable;
samplesToWrite -= samplesAvailable;
samplesRead = 0;
2014-06-30 01:37:40 -04:00
decode();
2014-06-29 01:10:11 -04:00
}
}
}
2014-06-30 01:37:40 -04:00
static void MP3_Init() {
2014-06-29 01:10:11 -04:00
/* First the structures used by libmad must be initialized. */
mad_stream_init(&Stream);
2014-06-30 01:37:40 -04:00
mad_header_init(&Header);
2014-06-29 01:10:11 -04:00
mad_frame_init(&Frame);
mad_synth_init(&Synth);
mad_timer_reset(&Timer);
}
2014-06-30 01:37:40 -04:00
static void MP3_Exit() {
2014-06-29 01:10:11 -04:00
mad_synth_finish(&Synth);
mad_header_finish(&Header);
mad_frame_finish(&Frame);
mad_stream_finish(&Stream);
}
2014-06-30 01:37:40 -04:00
static void MP3_GetInfo(long long *samples, int *rate) {
unsigned long FrameCount = 0;
2014-06-29 01:10:11 -04:00
int bufferSize = 1024*512;
unsigned char *localBuffer;
long red = 0;
double totalBitrate = 0.0;
double mediumBitrate = 0.0;
2014-06-30 01:37:40 -04:00
struct mad_stream stream;
struct mad_header header;
int size = mp3_size(mp3Fd);
long count = size;
2014-06-29 01:10:11 -04:00
2014-06-30 01:37:40 -04:00
mad_stream_init (&stream);
mad_header_init (&header);
2014-06-29 01:10:11 -04:00
localBuffer = (unsigned char *)malloc(bufferSize);
2014-06-30 01:37:40 -04:00
for (int i=0; i<3; i++) {
2014-06-29 01:10:11 -04:00
memset(localBuffer, 0, bufferSize);
2014-06-30 01:37:40 -04:00
if (count > bufferSize)
red = mp3_read(mp3Fd, localBuffer, bufferSize);
else
red = mp3_read(mp3Fd, localBuffer, count);
count -= red;
if (!red)
break; // ran out of data
mad_stream_buffer (&stream, localBuffer, red);
for ( ;; ) {
2014-06-30 01:37:40 -04:00
if (mad_header_decode(&header, &stream) == -1) {
if (stream.buffer == NULL || stream.error == MAD_ERROR_BUFLEN) {
break;
}
else if (MAD_RECOVERABLE(stream.error)) {
continue;
}
else {
2014-06-29 01:10:11 -04:00
break;
2014-06-30 01:37:40 -04:00
}
}
if (FrameCount++ == 0)
*rate = header.samplerate;
totalBitrate += header.bitrate;
}
}
2014-06-29 01:10:11 -04:00
mediumBitrate = totalBitrate / (double)FrameCount;
int secs = size * 8 / mediumBitrate;
2014-06-30 01:37:40 -04:00
*samples = *rate * secs;
2014-06-29 01:10:11 -04:00
2014-06-30 01:37:40 -04:00
mad_header_finish (&header);
mad_stream_finish (&stream);
2014-06-29 01:10:11 -04:00
if (localBuffer)
2014-06-30 01:37:40 -04:00
free(localBuffer);
2014-06-29 01:10:11 -04:00
2014-06-30 01:37:40 -04:00
mp3_seek(mp3Fd, 0, SEEK_SET);
2014-06-29 01:10:11 -04:00
}
2016-12-29 19:38:05 -05:00
void mp3_Start(char *fname, long long *samples, int *rate, int *channels) {
2014-06-30 01:37:40 -04:00
sprintf(mp3Fd, "%s", fname);
2014-06-29 01:10:11 -04:00
2014-06-30 01:37:40 -04:00
//if (mp3Fd[0]!=0)
//{
useReadBuffer = 0;
MP3_GetInfo(samples, rate);
*channels = 2;
2014-06-29 01:10:11 -04:00
2014-06-30 01:37:40 -04:00
MP3_Init();
MP3_SkipHdr(mp3Fd);
eos = readLen = readPos = 0;
useReadBuffer = 1;
return;
//}
2014-06-29 01:10:11 -04:00
2014-06-30 01:37:40 -04:00
//*samples = 0;
2014-06-29 01:10:11 -04:00
}
2016-12-29 19:38:05 -05:00
void mp3_Stop(void) {
2014-06-30 01:37:40 -04:00
MP3_Exit();
mp3File_fptr=0;
2014-06-29 01:10:11 -04:00
/*
2014-06-30 01:37:40 -04:00
if (mp3Fd > 0)
{
if (gBrowser)
f_close(&mp3File);
else
dfs_close(mp3Fd);
}
mp3Fd = -1;
*/
2014-06-29 01:10:11 -04:00
}
2016-12-29 19:38:05 -05:00
int mp3_Update(char *buf, int bytes) {
2014-06-30 01:37:40 -04:00
MP3_Callback(buf, bytes/4);
return eos ? 0 : 1;
2014-06-29 01:10:11 -04:00
}