/**************************************************************************** * libwiigui * * Tantric 2009 * * gui_sound_plugin_ogg.cpp * * by ardi 2009 * * Decoder for ogg-vorbis with libtremor * * GUI class definitions ***************************************************************************/ #include #include #include #include #include #include "gui_sound_decoder.h" /* functions to read the Ogg file from memory */ static struct { char *mem; int size; int pos; } file[4]; static int f_read(void * punt, int bytes, int blocks, int *f) { int b; int c = 0; int d; if (bytes * blocks <= 0) return 0; blocks *= bytes; while (blocks > 0) { b = blocks; if (b > 4096) b = 4096; d = (*f) - 0x666; if ((unsigned) (d) <= (0x669 - 0x666)) { if (file[d].size == 0) return -1; if ((file[d].pos + b) > file[d].size) b = file[d].size - file[d].pos; if (b > 0) { memcpy(punt, file[d].mem + file[d].pos, b); file[d].pos += b; } } else b = read(*f, ((char *) punt) + c, b); if (b <= 0) { return c / bytes; } c += b; blocks -= b; } return c / bytes; } static int f_seek(int *f, ogg_int64_t offset, int mode) { if (f == NULL) return (-1); int k; mode &= 3; int d = (*f) - 0x666; if ((unsigned) (d) <= (0x669 - 0x666)) { k = 0; if (file[d].size == 0) return -1; if (mode == 0) { if ((offset) >= file[d].size) { file[d].pos = file[d].size; k = -1; } else if ((offset) < 0) { file[d].pos = 0; k = -1; } else file[d].pos = offset; } else if (mode == 1) { if ((file[d].pos + offset) >= file[d].size) { file[d].pos = file[d].size; k = -1; } else if ((file[d].pos + offset) < 0) { file[d].pos = 0; k = -1; } else file[d].pos += offset; } else if (mode == 2) { if ((file[d].size + offset) >= file[d].size) { file[d].pos = file[d].size; k = -1; } else if ((file[d].size + offset) < 0) { file[d].pos = 0; k = -1; } else file[d].pos = file[d].size + offset; } } else k = lseek(*f, (int) offset, mode); if (k < 0) k = -1; else k = 0; return k; } static int f_close(int *f) { int d = (*f) - 0x666; if ((unsigned) (d) <= (0x669 - 0x666)) { file[d].size = 0; file[d].pos = 0; if (file[d].mem) { file[d].mem = (char *) 0; } return 0; } else return close(*f); return 0; } static long f_tell(int *f) { int k; int d = (*f) - 0x666; if ((unsigned) (d) <= (0x669 - 0x666)) { k = file[d].pos; } else k = lseek(*f, 0, 1); return (long) k; } static int mem_open(char * ogg, int size) { static int one = 1; int n; if (one) { one = 0; file[0].size = 0; file[1].size = 0; file[2].size = 0; file[3].size = 0; file[0].mem = ogg; file[0].size = size; file[0].pos = 0; return (0x666); } for (n = 0; n < 4; n++) { if (file[n].size == 0) { file[n].mem = ogg; file[n].size = size; file[n].pos = 0; return (0x666 + n); } } return -1; } static int mem_close(int fd) { if ((unsigned) ((fd) - 0x666) <= (0x669 - 0x666)) // it is a memory file descriptor? { fd -= 0x666; file[fd].size = 0; return 0; } else return f_close(&fd); } static ov_callbacks callbacks = { (size_t(*)(void *, size_t, size_t, void *)) f_read, (int(*)(void *, ogg_int64_t, int)) f_seek, (int(*)(void *)) f_close, (long(*)(void *)) f_tell }; class GuiSoundDecoderOGG: public GuiSoundDecoder { protected: GuiSoundDecoderOGG(const u8 * snd, u32 len, bool snd_is_allocated) { sound = snd; is_allocated = snd_is_allocated; ogg_fd = mem_open((char *) snd, len); if (ogg_fd < 0) throw("mem open failed"); if (ov_open_callbacks((void*) &ogg_fd, &ogg_file, NULL, 0, callbacks) < 0) { mem_close(ogg_fd); throw("ogg open failed"); } ogg_info = ov_info(&ogg_file, -1); bitstream = 0; is_running = false; } public: ~GuiSoundDecoderOGG() { while (is_running) usleep(50); ov_clear(&ogg_file); if (is_allocated) delete[] sound; } static GuiSoundDecoder *Create(const u8 * snd, u32 len, bool snd_is_allocated) { if (snd && len > 4 && snd[0] == 'O' && snd[1] == 'g' && snd[2] == 'g' && snd[3] == 'S') return new GuiSoundDecoderOGG( snd, len, snd_is_allocated); return NULL; } s32 GetFormat() { return ogg_info->channels == 2 ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT; } s32 GetSampleRate() { return ogg_info->rate; } /* Read reads data from stream to buffer return: >0 = readed bytes; 0 = EOF; <0 = Error; */ int Read(u8 * buffer, int buffer_size) { is_running = true; int ret = ov_read(&ogg_file, (char *) buffer, buffer_size, &bitstream); if (ret < 0) { /* error in the stream. Not a problem, just reporting it in case we (the app) cares. In this case, we don't. */ if (ret != OV_HOLE) ret = 0; // we says EOF } is_running = false; return ret; } int Rewind() { return ov_time_seek(&ogg_file, 0); } private: const u8 *sound; bool is_allocated; int ogg_fd; OggVorbis_File ogg_file; vorbis_info *ogg_info; int bitstream; bool is_running; }; REGISTER_GUI_SOUND_DECODER( GuiSoundDecoderOGG );