BizHawk/psx/mednadisc/cdrom/audioreader_opus.cpp

186 lines
4.8 KiB
C++

/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../mednafen.h"
#include "audioreader.h"
#include "audioreader_opus.h"
// OPUS SUPPORT NOT DONE YET!!!
/*
(int64)op_pcm_total() * 44100 / 48000
resampling vs seek, filter delay, etc. to consider
*/
static size_t iop_read_func(void *ptr, size_t size, size_t nmemb, void *user_data)
{
Stream *fw = (Stream*)user_data;
if(!size)
return(0);
try
{
return fw->read(ptr, size * nmemb, false) / size;
}
catch(...)
{
return(0);
}
}
static int iop_seek_func(void *user_data, opus_int64 offset, int whence)
{
Stream *fw = (Stream*)user_data;
try
{
fw->seek(offset, whence);
return(0);
}
catch(...)
{
return(-1);
}
}
static int iop_close_func(void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
fw->close();
return(0);
}
catch(...)
{
return EOF;
}
}
static opus_int64 iop_tell_func(void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
return fw->tell();
}
catch(...)
{
return(-1);
}
}
/* Error strings copied from libopusfile header file comments. */
static const char *op_errstring(int error)
{
static const struct
{
int code;
const char *str;
} error_table[] =
{
{ OP_EREAD, gettext_noop("OP_EREAD: An underlying read, seek, or tell operation failed when it should have succeeded.") },
{ OP_EFAULT, gettext_noop("OP_EFAULT: A NULL pointer was passed where one was unexpected, or an internal memory allocation failed, or an internal library error was encountered.") },
{ OP_EIMPL, gettext_noop("OP_EIMPL: The stream used a feature that is not implemented, such as an unsupported channel family.") },
{ OP_EINVAL, gettext_noop("OP_EINVAL: One or more parameters to a function were invalid.") },
{ OP_ENOTFORMAT, gettext_noop("OP_ENOTFORMAT: A purported Ogg Opus stream did not begin with an Ogg page, or a purported header packet did not start with one of the required strings, \"OpusHead\" or \"OpusTags\".") },
{ OP_EBADHEADER, gettext_noop("OP_EBADHEADER: A required header packet was not properly formatted, contained illegal values, or was missing altogether.") },
{ OP_EVERSION, gettext_noop("OP_EVERSION: The ID header contained an unrecognized version number.") },
{ OP_EBADPACKET, gettext_noop("OP_EBADPACKET: An audio packet failed to decode properly.") },
{ OP_EBADLINK, gettext_noop("OP_EBADLINK: We failed to find data we had seen before, or the bitstream structure was sufficiently malformed that seeking to the target destination was impossible.") },
{ OP_ENOSEEK, gettext_noop("OP_ENOSEEK: An operation that requires seeking was requested on an unseekable stream.") },
{ OP_EBADTIMESTAMP, gettext_noop("OP_EBADTIMESTAMP: The first or last granule position of a link failed basic validity checks.") },
};
for(unsigned i = 0; i < sizeof(error_table) / sizeof(error_table[0]); i++)
{
if(error_table[i].code == error)
{
return _(error_table[i].str);
}
}
return _("Unknown");
}
OggOpusReader::OggOpusReader(Stream *fp) : fw(fp)
{
OpusFileCallbacks cb;
int error = 0;
memset(&cb, 0, sizeof(cb));
cb.read_func = iop_read_func;
cb.seek_func = iop_seek_func;
cb.close_func = iop_close_func;
cb.tell_func = iop_tell_func;
fp->seek(0, SEEK_SET);
if(!(opfile = op_open_callbacks((void*)fp, &cb, NULL, 0, &error)))
{
switch(error)
{
default:
throw MDFN_Error(0, _("opusfile: error code: %d(%s)", error, op_errstring(error)));
break;
case OP_ENOTFORMAT:
throw(0);
break;
}
}
}
OggOpusReader::~OggOpusReader()
{
op_free(opfile);
}
int64 OggOpusReader::Read_(int16 *buffer, int64 frames)
{
int16 *tr_buffer = buffer;
int64 tr_count = frames * 2;
while(tr_count > 0)
{
int64 didread = op_read(opfile, tr_buffer, tr_count, NULL);
if(didread == 0)
break;
tr_buffer += didread * 2;
tr_count -= didread * 2;
}
return(frames - (tr_count / 2));
}
bool OggOpusReader::Seek_(int64 frame_offset)
{
op_pcm_seek(opfile, frame_offset);
return(true);
}
int64 OggOpusReader::FrameCount(void)
{
return(op_pcm_total(pvfile, -1));
}