engine: soundlib: implemented Ogg Opus sounds playback
This commit is contained in:
parent
4e138ee795
commit
e982b7911f
3 changed files with 92 additions and 4 deletions
|
@ -19,6 +19,7 @@ GNU General Public License for more details.
|
||||||
#include <ogg/ogg.h>
|
#include <ogg/ogg.h>
|
||||||
#include <vorbis/codec.h>
|
#include <vorbis/codec.h>
|
||||||
#include <vorbis/vorbisfile.h>
|
#include <vorbis/vorbisfile.h>
|
||||||
|
#include <opusfile.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
@ -80,6 +81,16 @@ static long OggFilestream_Tell( void *datasource )
|
||||||
return filestream->position;
|
return filestream->position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int OpusCallback_Read( void *datasource, byte *ptr, int nbytes )
|
||||||
|
{
|
||||||
|
return OggFilestream_Read( ptr, 1, nbytes, datasource );
|
||||||
|
}
|
||||||
|
|
||||||
|
static opus_int64 OpusCallback_Tell( void *datasource )
|
||||||
|
{
|
||||||
|
return OggFilestream_Tell( datasource );
|
||||||
|
}
|
||||||
|
|
||||||
static const ov_callbacks ov_callbacks_membuf = {
|
static const ov_callbacks ov_callbacks_membuf = {
|
||||||
OggFilestream_Read,
|
OggFilestream_Read,
|
||||||
OggFilestream_Seek,
|
OggFilestream_Seek,
|
||||||
|
@ -87,6 +98,20 @@ static const ov_callbacks ov_callbacks_membuf = {
|
||||||
OggFilestream_Tell
|
OggFilestream_Tell
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const OpusFileCallbacks op_callbacks_membuf = {
|
||||||
|
OpusCallback_Read,
|
||||||
|
OggFilestream_Seek,
|
||||||
|
OpusCallback_Tell,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================================================================
|
||||||
|
|
||||||
|
Ogg Vorbis decompression
|
||||||
|
|
||||||
|
=================================================================
|
||||||
|
*/
|
||||||
qboolean Sound_LoadOggVorbis( const char *name, const byte *buffer, fs_offset_t filesize )
|
qboolean Sound_LoadOggVorbis( const char *name, const byte *buffer, fs_offset_t filesize )
|
||||||
{
|
{
|
||||||
long ret;
|
long ret;
|
||||||
|
@ -118,11 +143,10 @@ qboolean Sound_LoadOggVorbis( const char *name, const byte *buffer, fs_offset_t
|
||||||
sound.size = sound.samples * sound.width * sound.channels;
|
sound.size = sound.samples * sound.width * sound.channels;
|
||||||
sound.wav = (byte *)Mem_Calloc( host.soundpool, sound.size );
|
sound.wav = (byte *)Mem_Calloc( host.soundpool, sound.size );
|
||||||
|
|
||||||
while(( ret = ov_read( &vorbisFile, (char*)sound.wav + written, sound.size, 0, sound.width, 1, §ion )) != 0 )
|
while(( ret = ov_read( &vorbisFile, (char*)sound.wav + written, sound.size - written, 0, sound.width, 1, §ion )) != 0 )
|
||||||
{
|
{
|
||||||
if( ret < 0 )
|
if( ret < 0 ) {
|
||||||
{
|
Con_DPrintf( S_ERROR "%s: failed to load (%s): compressed data decoding error\n", __func__, file.name );
|
||||||
Con_DPrintf( S_ERROR "%s: failed to load (%s): error during Vorbis data decoding\n", __func__, file.name );
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
written += ret;
|
written += ret;
|
||||||
|
@ -131,3 +155,64 @@ qboolean Sound_LoadOggVorbis( const char *name, const byte *buffer, fs_offset_t
|
||||||
ov_clear( &vorbisFile );
|
ov_clear( &vorbisFile );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================================================================
|
||||||
|
|
||||||
|
Ogg Opus decompression
|
||||||
|
|
||||||
|
=================================================================
|
||||||
|
*/
|
||||||
|
qboolean Sound_LoadOggOpus( const char *name, const byte *buffer, fs_offset_t filesize )
|
||||||
|
{
|
||||||
|
long ret;
|
||||||
|
ogg_filestream_t file;
|
||||||
|
OggOpusFile *of;
|
||||||
|
const OpusHead *opusHead;
|
||||||
|
size_t written = 0;
|
||||||
|
|
||||||
|
if( !buffer )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OggFilestream_Init( &file, name, buffer, filesize );
|
||||||
|
of = op_open_callbacks( &file, &op_callbacks_membuf, NULL, 0, NULL );
|
||||||
|
if( !of ) {
|
||||||
|
Con_DPrintf( S_ERROR "%s: failed to load (%s): file reading error\n", __func__, file.name );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
opusHead = op_head( of, -1 );
|
||||||
|
if( opusHead->channel_count < 1 || opusHead->channel_count > 2 ) {
|
||||||
|
Con_DPrintf( S_ERROR "%s: failed to load (%s): unsuppored channels count\n", __func__, file.name );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// according to OggOpus specification, sound always encoded at 48kHz sample rate
|
||||||
|
// but this isn't a problem, engine can do resampling
|
||||||
|
sound.channels = opusHead->channel_count;
|
||||||
|
sound.rate = 48000;
|
||||||
|
sound.width = 2; // always 16-bit PCM
|
||||||
|
sound.type = WF_PCMDATA;
|
||||||
|
sound.flags = SOUND_RESAMPLE;
|
||||||
|
sound.samples = op_pcm_total( of, -1 );
|
||||||
|
sound.size = sound.samples * sound.width * sound.channels;
|
||||||
|
sound.wav = (byte *)Mem_Calloc( host.soundpool, sound.size );
|
||||||
|
|
||||||
|
// skip undesired samples before playing sound
|
||||||
|
if( op_pcm_seek( of, opusHead->pre_skip ) < 0 ) {
|
||||||
|
Con_DPrintf( S_ERROR "%s: failed to load (%s): pre-skip error\n", __func__, file.name );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(( ret = op_read( of, (opus_int16*)(sound.wav + written), (sound.size - written) / sound.width, NULL )) != 0 )
|
||||||
|
{
|
||||||
|
if( ret < 0 ) {
|
||||||
|
Con_DPrintf( S_ERROR "%s: failed to load (%s): compressed data decoding error\n", __func__, file.name );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
written += ret * sound.width * sound.channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
op_free( of );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ static const loadwavfmt_t load_game[] =
|
||||||
{ "%s%s.%s", "mp3", Sound_LoadMPG },
|
{ "%s%s.%s", "mp3", Sound_LoadMPG },
|
||||||
{ DEFAULT_SOUNDPATH "%s%s.%s", "ogg", Sound_LoadOggVorbis },
|
{ DEFAULT_SOUNDPATH "%s%s.%s", "ogg", Sound_LoadOggVorbis },
|
||||||
{ "%s%s.%s", "ogg", Sound_LoadOggVorbis },
|
{ "%s%s.%s", "ogg", Sound_LoadOggVorbis },
|
||||||
|
{ DEFAULT_SOUNDPATH "%s%s.%s", "opus", Sound_LoadOggOpus },
|
||||||
|
{ "%s%s.%s", "opus", Sound_LoadOggOpus },
|
||||||
{ NULL, NULL, NULL }
|
{ NULL, NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,7 @@ extern sndlib_t sound;
|
||||||
qboolean Sound_LoadWAV( const char *name, const byte *buffer, fs_offset_t filesize );
|
qboolean Sound_LoadWAV( const char *name, const byte *buffer, fs_offset_t filesize );
|
||||||
qboolean Sound_LoadMPG( const char *name, const byte *buffer, fs_offset_t filesize );
|
qboolean Sound_LoadMPG( const char *name, const byte *buffer, fs_offset_t filesize );
|
||||||
qboolean Sound_LoadOggVorbis( const char *name, const byte *buffer, fs_offset_t filesize );
|
qboolean Sound_LoadOggVorbis( const char *name, const byte *buffer, fs_offset_t filesize );
|
||||||
|
qboolean Sound_LoadOggOpus( const char *name, const byte *buffer, fs_offset_t filesize );
|
||||||
|
|
||||||
//
|
//
|
||||||
// stream operate
|
// stream operate
|
||||||
|
|
Loading…
Add table
Reference in a new issue