diff --git a/README.md b/README.md index d968454..122bbc3 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ If you want to build the menu, you need an n64 toolchain. When using windows 10 * Windows 10 (Aniversary Update or above) / Ubuntu 16.04 (or above) * [libdragon](https://github.com/DragonMinded/libdragon) * [libmikmod (with n64 driver)](https://github.com/networkfusion/libmikmod) -* [libmad-n64](https://github.com/parasyte/libmad-n64) +* [libmad-n64](https://github.com/networkfusion/libmad-n64) * [libyaml](http://pyyaml.org/wiki/LibYAML) ### Build the Toolchain diff --git a/inc/rom.h b/inc/rom.h index 77b9cc3..ed52928 100644 --- a/inc/rom.h +++ b/inc/rom.h @@ -129,7 +129,7 @@ #define IO_WRITE(addr,data) (*(volatile u32*)PHYS_TO_K1(addr)=(u32)(data)) #define SAVE_SIZE_SRAM 32768 -#define SAVE_SIZE_SRAM96 131072 +#define SAVE_SIZE_SRAM96 131072 //TODO: or should this be 98304 #define SAVE_SIZE_EEP4k 4096 #define SAVE_SIZE_EEP16k 16384 #define SAVE_SIZE_FLASH 131072 diff --git a/src/main.c b/src/main.c index c1d11f2..d60fd65 100644 --- a/src/main.c +++ b/src/main.c @@ -141,7 +141,7 @@ u16 cursor_history[32]; u16 cursor_history_pos = 0; u8 empty = 0; -u8 playing = 0; +int mp3playing = 0; u8 gb_load_y = 0; FATFS *fs; @@ -850,7 +850,7 @@ void configure() asm_date = memRomRead32(0x38); //TODO: this should be displayed somewhere... evd_setCfgBit(ED_CFG_SDRAM_ON, 1); - firm = evd_readReg(REG_VER); //TODO: why not just use evd_getFirmVersion() + firm = evd_getFirmVersion(); if (firm >= 0x0200) { @@ -864,19 +864,22 @@ void configure() { msg |= 1 << 14; evd_writeReg(REG_MAX_MSG, msg); - if (firm == 0x0214) //need to take into account different default firmware versions for each ED64 version - {//TODO: use a case statement instead + + switch(firm) //need to take into account different default firmware versions for each ED64 version + { + case 0x0214: updateFirmware("/firmware/firmware_v2.bin"); - } - else if (firm == 0x0250) - { + break; + case 0x0250: updateFirmware("/firmware/firmware_v2_5.bin"); - } - else if (firm == 0x0300) - { + break; + case 0x0300: updateFirmware("/firmware/firmware_v3.bin"); + break; + default: + break; } - + sleep(1); evd_init(); } @@ -3171,27 +3174,31 @@ void loadFile(display_context_t disp) display_show(disp); break; case 10: - buf_size = audio_get_buffer_length() * 4; - buf_ptr = malloc(buf_size); - + { while (!(disp = display_lock())) - ; + ; clearScreen(disp); - drawShortInfoBox(disp, " playback", 0); - + drawShortInfoBox(disp, " Loading...", 0); + display_show(disp); long long start = 0, end = 0, curr, pause = 0, samples; int rate = 44100, last_rate = 44100, channels = 2; - audio_init(44100, 2); + audio_init(44100, 4); + buf_size = audio_get_buffer_length() * 4; + buf_ptr = malloc(buf_size); mp3_Start(name_file, &samples, &rate, &channels); - playing = 1; + mp3playing = 1; select_mode = 9; - input_mapping = mp3; //mp3 stop - + while (!(disp = display_lock())) + ; + clearScreen(disp); + drawShortInfoBox(disp, " MP3 Playback", 0); display_show(disp); + input_mapping = mp3; //mp3 stop break; + } default: break; } @@ -4291,11 +4298,13 @@ void handleInput(display_context_t disp, sprite_t *contr) break; case mp3: - - //stop mp3 - mp3_Stop(); - playing = 0; + mp3playing = 0; + audio_close(); + free(buf_ptr); + buf_ptr = 0; + + clearScreen(disp); //part clear? display_dir(list, cursor, page, MAX_LIST, count, disp); @@ -4475,8 +4484,15 @@ int main(void) handleInput(disp, contr); - if (playing == 1) - playing = mp3_Update(buf_ptr, buf_size); + if (mp3playing && audio_can_write()) + { + mp3playing = mp3_Update(buf_ptr, buf_size); + + if (mp3playing) + { + audio_write((short *)buf_ptr); + } + } if (input_mapping == file_manager) sleep(60); diff --git a/src/mp3.c b/src/mp3.c index b3b2764..e3fa429 100644 --- a/src/mp3.c +++ b/src/mp3.c @@ -1,6 +1,6 @@ // // Copyright (c) 2017 The Altra64 project contributors -// Portions (c) 2013 saturnu (Alt64) based on libdragon, Neo64Menu, ED64IO, libn64-hkz, libmikmod +// Portions (c) 2010 chillywilly (https://www.neoflash.com/forum/index.php?topic=6311.0) // See LICENSE file in the project root for full license information. // @@ -20,8 +20,8 @@ static struct mad_synth Synth; static mad_timer_t Timer; typedef struct { - short left; - short right; + short left; + short right; } Sample; static int eos; @@ -31,95 +31,89 @@ 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 UINT readLen; static int samplesRead; -char * mp3File; -char * mp3Fd; -long mp3File_fptr=0; -int mp3File_fsize; +static FIL mp3File; +static FRESULT mp3Fd; -extern int gBrowser; -extern char path[1024]; -extern void c2wstrcpy(void *dst, void *src); -extern void c2wstrcat(void *dst, void *src); - -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; - - return offs; -} - -static int mp3_size(char* fd) { - FRESULT result; - FILINFO fno; - //TODO: error - result = f_stat (fd, &fno); - mp3File_fsize = fno.fsize; - return mp3File_fsize; -} - -static void _f_read(char* fname, unsigned char *readBuffer, int size){ -//TODO: function not working... probably worth switching to http://www.underbit.com/products/mad/ anyway... - FRESULT result; - FIL file; - UINT bytesread; - result = f_open(&file, fname, FA_READ); - - if (result == FR_OK) - { - int fsize = f_size(&file); - - mp3File_fsize = fsize; - - f_lseek(&file, mp3File_fptr); - - result = - f_read ( - &file, /* [IN] File object */ - readBuffer, /* [OUT] Buffer to store read data */ - size, /* [IN] Number of bytes to read */ - &bytesread /* [OUT] Number of bytes read */ - ); - - f_close(&file); - - mp3File_fptr+=bytesread; - } -} - -static int mp3_read(char* fd, unsigned char *ptr, int size) +static int mp3_seek(int offset, int whence) { - int ts=size; - _f_read(fd, ptr, size); - return ts; + DWORD offs = 0; + switch (whence) + { + case SEEK_SET: + offs = offset; + break; + case SEEK_CUR: + offs = mp3File.fptr + offset; + break; + case SEEK_END: + offs = f_size(&mp3File) + offset; + break; + } + f_lseek(&mp3File, offs); + return offs; } -static int id3_tag_size(unsigned char const *buf, int remaining) { - int size; +static int mp3_size() +{ + return f_size(&mp3File); +} - if (remaining < 10) - return 0; +static int mp3_read(unsigned char *ptr, int size) +{ + UINT ts; + if (useReadBuffer) + { + int total = 0; + while (size) + { + if (!readLen) + { + f_read ( + &mp3File, /* [IN] File object */ + readBuffer, /* [OUT] Buffer to store read data */ + INPUT_BUFFER_SIZE, /* [IN] Number of bytes to read */ + &readLen /* [OUT] Number of bytes read */ + ); + readPos = 0; + if (readLen == 0) + return total; // EOF + } + int rlen = (size= MAD_F_ONE) - return (32767); + return (32767); if (Fixed <= -MAD_F_ONE) - return (-32768); + return (-32768); /* Conversion. */ - Fixed = Fixed >> (MAD_F_FRACBITS - 15); - - return ((short)Fixed); + Fixed = Fixed >> (MAD_F_FRACBITS - 15); + + return ((short)Fixed); } -static int fillFileBuffer() { - int leftOver = Stream.bufend - Stream.next_frame; - int want = INPUT_BUFFER_SIZE - leftOver; +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); + // 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 + // fill remainder of buffer + unsigned char* bufferPos = fileBuffer + leftOver; + while (want > 0) + { + int got = mp3_read(bufferPos, want); + if (got <= 0) + return 1; // EOF - want -= got; - bufferPos += got; - } - return 0; + want -= got; + bufferPos += got; + } + return 0; } -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? */ +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? */ int tagsize = id3_tag_size(Stream.this_frame, Stream.bufend - Stream.this_frame); - if (tagsize > 0) { - mad_stream_skip (&Stream, tagsize); + if (tagsize > 0) + { + mad_stream_skip (&Stream, tagsize); continue; - } - } - } + } + } + } mad_timer_add(&Timer, Frame.header.duration); - mad_synth_frame(&Synth, &Frame); + mad_synth_frame(&Synth, &Frame); } -static void convertLeftSamples(Sample* first, Sample* last, const mad_fixed_t* src) { - for (Sample *dst = first; dst != last; ++dst) - dst->left = convertSample(*src++); +static void convertLeftSamples(Sample* first, Sample* last, const mad_fixed_t* src) +{ + for (Sample *dst = first; dst != last; ++dst) + dst->left = convertSample(*src++); } -static void convertRightSamples(Sample* first, Sample* last, const mad_fixed_t* src) { - for (Sample *dst = first; dst != last; ++dst) - dst->right = convertSample(*src++); +static void convertRightSamples(Sample* first, Sample* last, const mad_fixed_t* src) +{ + for (Sample *dst = first; dst != last; ++dst) + dst->right = convertSample(*src++); } -static void MP3_Callback(void *buffer, unsigned int samplesToWrite) { +static void MP3_Callback(void *buffer, unsigned int samplesToWrite) +{ Sample *destination = (Sample*)buffer; - while (samplesToWrite > 0) { - while (!eos && (Synth.pcm.length == 0)) - decode(); + while (samplesToWrite > 0) + { + while (!eos && (Synth.pcm.length == 0)) + decode(); - if (eos) { - // done - memset(destination, 0, samplesToWrite*4); - break; - } + if (eos) + { + // done + memset(destination, 0, samplesToWrite*4); + break; + } unsigned int samplesAvailable = Synth.pcm.length - samplesRead; - if (samplesAvailable > samplesToWrite) { + if (samplesAvailable > samplesToWrite) + { convertLeftSamples(destination, destination + samplesToWrite, &Synth.pcm.samples[0][samplesRead]); convertRightSamples(destination, destination + samplesToWrite, &Synth.pcm.samples[1][samplesRead]); samplesRead += samplesToWrite; samplesToWrite = 0; - } - else { + } + else + { convertLeftSamples(destination, destination + samplesAvailable, &Synth.pcm.samples[0][samplesRead]); convertRightSamples(destination, destination + samplesAvailable, &Synth.pcm.samples[1][samplesRead]); @@ -288,124 +325,130 @@ static void MP3_Callback(void *buffer, unsigned int samplesToWrite) { samplesToWrite -= samplesAvailable; samplesRead = 0; - decode(); + decode(); } } } -static void MP3_Init() { +static void MP3_Init() +{ /* First the structures used by libmad must be initialized. */ mad_stream_init(&Stream); - mad_header_init(&Header); + mad_header_init(&Header); mad_frame_init(&Frame); mad_synth_init(&Synth); mad_timer_reset(&Timer); } -static void MP3_Exit() { +static void MP3_Exit() +{ mad_synth_finish(&Synth); mad_header_finish(&Header); mad_frame_finish(&Frame); mad_stream_finish(&Stream); } -static void MP3_GetInfo(long long *samples, int *rate) { - unsigned long FrameCount = 0; +static void MP3_GetInfo(long long *samples, int *rate) +{ + unsigned long FrameCount = 0; int bufferSize = 1024*512; unsigned char *localBuffer; - long red = 0; + long bytesread = 0; double totalBitrate = 0.0; double mediumBitrate = 0.0; - struct mad_stream stream; - struct mad_header header; - int size = mp3_size(mp3Fd); - long count = size; + struct mad_stream stream; + struct mad_header header; + long size = mp3_size(); + long count = size; - mad_stream_init (&stream); - mad_header_init (&header); + mad_stream_init (&stream); + mad_header_init (&header); localBuffer = (unsigned char *)malloc(bufferSize); - for (int i=0; i<3; i++) { + for (int i=0; i<3; i++) + { memset(localBuffer, 0, bufferSize); - 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 + if (count > bufferSize) + bytesread = mp3_read(localBuffer, bufferSize); + else + bytesread = mp3_read(localBuffer, count); + count -= bytesread; + if (!bytesread) + break; // ran out of data - mad_stream_buffer (&stream, localBuffer, red); + mad_stream_buffer (&stream, localBuffer, bytesread); - for ( ;; ) { - if (mad_header_decode(&header, &stream) == -1) { - if (stream.buffer == NULL || stream.error == MAD_ERROR_BUFLEN) { + for ( ;; ) + { + 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 { - break; - } - } - if (FrameCount++ == 0) - *rate = header.samplerate; - totalBitrate += header.bitrate; - } - } + else if (MAD_RECOVERABLE(stream.error)) + { + continue; + } + else + { + break; + } + } + if (FrameCount++ == 0) + *rate = header.samplerate; + totalBitrate += header.bitrate; + } + } mediumBitrate = totalBitrate / (double)FrameCount; int secs = size * 8 / mediumBitrate; - *samples = *rate * secs; + *samples = *rate * secs; - mad_header_finish (&header); - mad_stream_finish (&stream); + mad_header_finish (&header); + mad_stream_finish (&stream); if (localBuffer) - free(localBuffer); + free(localBuffer); - mp3_seek(mp3Fd, 0, SEEK_SET); + mp3_seek(0, SEEK_SET); } -void mp3_Start(char *fname, long long *samples, int *rate, int *channels) { - sprintf(mp3Fd, "%s", fname); +void mp3_Start(char *fname, long long *samples, int *rate, int *channels) +{ - //if (mp3Fd[0]!=0) - //{ - useReadBuffer = 0; - MP3_GetInfo(samples, rate); - *channels = 2; + mp3Fd = f_open(&mp3File, fname, FA_READ); - MP3_Init(); - MP3_SkipHdr(mp3Fd); - eos = readLen = readPos = 0; - useReadBuffer = 1; - return; - //} + if (mp3Fd == FR_OK) + { + useReadBuffer = 0; + MP3_GetInfo(samples, rate); + *channels = 2; - //*samples = 0; + MP3_Init(); + MP3_SkipHdr(); + eos = readLen = readPos = 0; + useReadBuffer = 1; + return; + } + + *samples = 0; + return; } -void mp3_Stop(void) { - MP3_Exit(); - mp3File_fptr=0; -/* - if (mp3Fd > 0) - { - if (gBrowser) - f_close(&mp3File); - else - dfs_close(mp3Fd); - } - mp3Fd = -1; - */ +void mp3_Stop(void) +{ + MP3_Exit(); + if (mp3Fd == FR_OK) + { + f_close(&mp3File); + } + mp3Fd = FR_NO_FILE; } -int mp3_Update(char *buf, int bytes) { - MP3_Callback(buf, bytes/4); - return eos ? 0 : 1; +int mp3_Update(char *buf, int bytes) +{ + MP3_Callback(buf, bytes/4); + return eos ? 0 : 1; } diff --git a/tools/setup-linux.sh b/tools/setup-linux.sh index 94d71f7..3d24e3a 100644 --- a/tools/setup-linux.sh +++ b/tools/setup-linux.sh @@ -67,6 +67,18 @@ LIBS="-ldragon -lc -ldragonsys -lnosys" \ make make install +cd .. +# install libmad (custom version) +git clone https://github.com/networkfusion/libmad-n64 +cd libmad-n64 +export PATH=$PATH:$N64_INST/bin +CFLAGS="-std=gnu99 -march=vr4300 -mtune=vr4300" \ +LDFLAGS="-L$N64_INST/lib -Tn64ld.x" \ +LIBS="-ldragon -lc -ldragonsys -lnosys" \ +./configure --host=mips64-elf --prefix=$N64_INST +make +make install + cd .. # Perform cleanup