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