Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

libvorbis audio decode from memory in C++

Given an encoded buffer in C++, what would be the steps using oggvorbis structs to decode the already in-memory data?

OggVorbis_File cannot be used, because assets are within compressed archives.

I'm trying to research the necessary structs and methods, but I'm fairly new to audio encoding and decoding.

Any resources that can help further my reading are appreciated as well!

I should clarify, I intend to use the decoded data to stream into OpenAL.

Thanks.

like image 710
DubyaDubyaDubyaDot Avatar asked Nov 18 '12 04:11

DubyaDubyaDubyaDot


2 Answers

Answering my own question.

This can be done by providing custom callbacks to vorbis.

struct ogg_file
{
    char* curPtr;
    char* filePtr;
    size_t fileSize;
};

size_t AR_readOgg(void* dst, size_t size1, size_t size2, void* fh)
{
    ogg_file* of = reinterpret_cast<ogg_file*>(fh);
    size_t len = size1 * size2;
    if ( of->curPtr + len > of->filePtr + of->fileSize )
    {
        len = of->filePtr + of->fileSize - of->curPtr;
    }
    memcpy( dst, of->curPtr, len );
    of->curPtr += len;
    return len;
}

int AR_seekOgg( void *fh, ogg_int64_t to, int type ) {
    ogg_file* of = reinterpret_cast<ogg_file*>(fh);

    switch( type ) {
        case SEEK_CUR:
            of->curPtr += to;
            break;
        case SEEK_END:
            of->curPtr = of->filePtr + of->fileSize - to;
            break;
        case SEEK_SET:
            of->curPtr = of->filePtr + to;
            break;
        default:
            return -1;
    }
    if ( of->curPtr < of->filePtr ) {
        of->curPtr = of->filePtr;
        return -1;
    }
    if ( of->curPtr > of->filePtr + of->fileSize ) {
        of->curPtr = of->filePtr + of->fileSize;
        return -1;
    }
    return 0;
}

int AR_closeOgg(void* fh)
{
    return 0;
}

long AR_tellOgg( void *fh )
{
    ogg_file* of = reinterpret_cast<ogg_file*>(fh);
    return (of->curPtr - of->filePtr);
}

Usage

ov_callbacks callbacks;
ogg_file t;
t.curPtr = t.filePtr = compressedData;
t.fileSize = compressedDataSize;

OggVorbis_File* ov = new OggVorbis_File;
mOggFile = ov;
memset( ov, 0, sizeof( OggVorbis_File ) );

callbacks.read_func = AR_readOgg;
callbacks.seek_func = AR_seekOgg;
callbacks.close_func = AR_closeOgg;
callbacks.tell_func = AR_tellOgg;

int ret = ov_open_callbacks((void *)&t, ov, NULL, -1, callbacks);

vorbis_info* vi = ov_info(ov, -1);
assert(vi);

/* compressed data is available to use, to uncompress look into ov_read */

A Special thanks to the Doom3 GPL source for most of the help with this, it can be viewed at : here

like image 181
DubyaDubyaDubyaDot Avatar answered Nov 12 '22 19:11

DubyaDubyaDubyaDot


You also can don't reinvent the wheel and use fmemopen like this:

FILE* memfile = fmemopen(data, len, "r");

Where data is pointer to memory beginning and len is length of your data. Then pass memfile to ov_open like regular FILE object.

However, there is downside: this function seems linux-specific (but it can be found in arduino, so I'm a bit confused about its status), so you don't have it on other systems. But there is some implementations for them (check libconfuse for window or for apple OSes).

like image 37
val is still with Monica Avatar answered Nov 12 '22 19:11

val is still with Monica