/*************************************************************************** * Copyright (C) 2010 * by Dimok * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any * damages arising from the use of this software. * * Permission is granted to anyone to use this software for any * purpose, including commercial applications, and to alter it and * redistribute it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you * must not claim that you wrote the original software. If you use * this software in a product, an acknowledgment in the product * documentation would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and * must not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. * * for WiiXplorer 2010 ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include "fs/CFile.hpp" Mp3Decoder::Mp3Decoder(const char * filepath) : SoundDecoder(filepath) { SoundType = SOUND_MP3; ReadBuffer = NULL; mad_timer_reset(&Timer); mad_stream_init(&Stream); mad_frame_init(&Frame); mad_synth_init(&Synth); if(!file_fd) return; OpenFile(); } Mp3Decoder::Mp3Decoder(const uint8_t * snd, int32_t len) : SoundDecoder(snd, len) { SoundType = SOUND_MP3; ReadBuffer = NULL; mad_timer_reset(&Timer); mad_stream_init(&Stream); mad_frame_init(&Frame); mad_synth_init(&Synth); if(!file_fd) return; OpenFile(); } Mp3Decoder::~Mp3Decoder() { ExitRequested = true; while(Decoding) OSSleepTicks(OSMicrosecondsToTicks(100)); mad_synth_finish(&Synth); mad_frame_finish(&Frame); mad_stream_finish(&Stream); if(ReadBuffer) free(ReadBuffer); ReadBuffer = NULL; } void Mp3Decoder::OpenFile() { GuardPtr = NULL; ReadBuffer = (uint8_t *) memalign(32, SoundBlockSize*SoundBlocks); if(!ReadBuffer) { if(file_fd) delete file_fd; file_fd = NULL; return; } uint8_t dummybuff[4096]; int32_t ret = Read(dummybuff, 4096, 0); if(ret <= 0) { if(file_fd) delete file_fd; file_fd = NULL; return; } SampleRate = (uint32_t) Frame.header.samplerate; Format = ((MAD_NCHANNELS(&Frame.header) == 2) ? (FORMAT_PCM_16_BIT | CHANNELS_STEREO) : (FORMAT_PCM_16_BIT | CHANNELS_MONO)); Rewind(); } int32_t Mp3Decoder::Rewind() { mad_synth_finish(&Synth); mad_frame_finish(&Frame); mad_stream_finish(&Stream); mad_timer_reset(&Timer); mad_stream_init(&Stream); mad_frame_init(&Frame); mad_synth_init(&Synth); SynthPos = 0; GuardPtr = NULL; if(!file_fd) return -1; return SoundDecoder::Rewind(); } static inline int16_t FixedToShort(mad_fixed_t Fixed) { /* Clipping */ if(Fixed>=MAD_F_ONE) return(SHRT_MAX); if(Fixed<=-MAD_F_ONE) return(-SHRT_MAX); Fixed=Fixed>>(MAD_F_FRACBITS-15); return((int16_t)Fixed); } int32_t Mp3Decoder::Read(uint8_t * buffer, int32_t buffer_size, int32_t pos) { if(!file_fd) return -1; if(Format == (FORMAT_PCM_16_BIT | CHANNELS_STEREO)) buffer_size &= ~0x0003; else buffer_size &= ~0x0001; uint8_t * write_pos = buffer; uint8_t * write_end = buffer+buffer_size; while(1) { while(SynthPos < Synth.pcm.length) { if(write_pos >= write_end) return write_pos-buffer; *((int16_t *) write_pos) = FixedToShort(Synth.pcm.samples[0][SynthPos]); write_pos += 2; if(MAD_NCHANNELS(&Frame.header) == 2) { *((int16_t *) write_pos) = FixedToShort(Synth.pcm.samples[1][SynthPos]); write_pos += 2; } SynthPos++; } if(Stream.buffer == NULL || Stream.error == MAD_ERROR_BUFLEN) { uint8_t * ReadStart = ReadBuffer; int32_t ReadSize = SoundBlockSize*SoundBlocks; int32_t Remaining = 0; if(Stream.next_frame != NULL) { Remaining = Stream.bufend - Stream.next_frame; memmove(ReadBuffer, Stream.next_frame, Remaining); ReadStart += Remaining; ReadSize -= Remaining; } ReadSize = file_fd->read(ReadStart, ReadSize); if(ReadSize <= 0) { GuardPtr = ReadStart; memset(GuardPtr, 0, MAD_BUFFER_GUARD); ReadSize = MAD_BUFFER_GUARD; } CurPos += ReadSize; mad_stream_buffer(&Stream, ReadBuffer, Remaining+ReadSize); } if(mad_frame_decode(&Frame,&Stream)) { if(MAD_RECOVERABLE(Stream.error)) { if(Stream.error != MAD_ERROR_LOSTSYNC || !GuardPtr) continue; } else { if(Stream.error != MAD_ERROR_BUFLEN) return -1; else if(Stream.error == MAD_ERROR_BUFLEN && GuardPtr) return -1; } } mad_timer_add(&Timer,Frame.header.duration); mad_synth_frame(&Synth,&Frame); SynthPos = 0; } return 0; }