1
0
Fork 0

Refactor and pass-through the decode buffer

This commit is contained in:
krcroft 2019-11-11 13:27:19 -08:00
parent 83e1979f26
commit 5a9dd2866b
No known key found for this signature in database
GPG key ID: 94D8F8D0D171A64B
5 changed files with 87 additions and 424 deletions

View file

@ -40,11 +40,11 @@
#define DR_FLAC_IMPLEMENTATION
#define DR_FLAC_NO_STDIO 1
#define DR_FLAC_NO_WIN32_IO 1
#define DRFLAC_MALLOC(sz) SDL_malloc((sz))
#define DRFLAC_REALLOC(p, sz) SDL_realloc((p), (sz))
#define DRFLAC_FREE(p) SDL_free((p))
#define DRFLAC_COPY_MEMORY(dst, src, sz) SDL_memcpy((dst), (src), (sz))
#define DRFLAC_ZERO_MEMORY(p, sz) SDL_memset((p), 0, (sz))
#define DRFLAC_FREE(p) SDL_free((p))
#define DRFLAC_MALLOC(sz) SDL_malloc((sz))
#define DRFLAC_REALLOC(p, sz) SDL_realloc((p), (sz))
#define DRFLAC_ZERO_MEMORY(p, sz) SDL_memset((p), 0, (sz))
#define DRFLAC_COPY_MEMORY(dst, src, sz) SDL_memcpy((dst), (src), (sz))
#include "dr_flac.h"
static size_t flac_read(void* pUserData, void* pBufferOut, size_t bytesToRead)
@ -131,14 +131,14 @@ static void FLAC_close(Sound_Sample *sample)
} /* FLAC_close */
static Uint32 FLAC_read(Sound_Sample *sample)
static Uint32 FLAC_read(Sound_Sample *sample, void* buffer, Uint32 desired_frames)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
drflac *dr = (drflac *) internal->decoder_private;
const drflac_uint64 rc = drflac_read_pcm_frames_s16(dr,
internal->buffer_size / (dr->channels * sizeof(drflac_int16)),
(drflac_int16 *) internal->buffer);
return (Uint32) rc * dr->channels * sizeof (drflac_int16);
const drflac_uint64 decoded_frames = drflac_read_pcm_frames_s16(dr,
desired_frames,
(drflac_int16 *) buffer);
return (Uint32) decoded_frames;
} /* FLAC_read */

View file

@ -28,12 +28,12 @@
#include <SDL.h> // provides: SDL_malloc, SDL_realloc, SDL_free, SDL_memcpy, and SDL_memset
#define DR_MP3_IMPLEMENTATION
#define DR_MP3_NO_STDIO 1
#define DRMP3_ASSERT(x) assert((x))
#define DRMP3_MALLOC(sz) SDL_malloc((sz))
#define DRMP3_REALLOC(p, sz) SDL_realloc((p), (sz))
#define DRMP3_FREE(p) SDL_free((p))
#define DRMP3_COPY_MEMORY(dst, src, sz) SDL_memcpy((dst), (src), (sz))
#define DRMP3_ZERO_MEMORY(p, sz) SDL_memset((p), 0, (sz))
#define DRMP3_FREE(p) SDL_free((p))
#define DRMP3_ASSERT(x) assert((x))
#define DRMP3_MALLOC(sz) SDL_malloc((sz))
#define DRMP3_REALLOC(p, sz) SDL_realloc((p), (sz))
#define DRMP3_ZERO_MEMORY(p, sz) SDL_memset((p), 0, (sz))
#define DRMP3_COPY_MEMORY(dst, src, sz) SDL_memcpy((dst), (src), (sz))
#include "dr_mp3.h" // provides: drmp3
#include "mp3_seek_table.h" // provides: populate_seek_table and SDL_Sound headers
@ -101,45 +101,15 @@ static void MP3_close(Sound_Sample* const sample)
}
} /* MP3_close */
static Uint32 MP3_read(Sound_Sample* const sample)
static Uint32 MP3_read(Sound_Sample* const sample, void* buffer, Uint32 desired_frames)
{
Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal* const>(sample->opaque);
const Sint32 channels = (Sint32) sample->actual.channels;
mp3_t* p_mp3 = static_cast<mp3_t*>(internal->decoder_private);
// setup our 32-bit input buffer
float in_buffer[4096];
const drmp3_uint16 in_buffer_frame_capacity = 4096 / channels;
// setup our 16-bit output buffer
drmp3_int16* out_buffer = static_cast<drmp3_int16*>(internal->buffer);
drmp3_uint16 remaining_frames = (internal->buffer_size / sizeof(drmp3_int16)) / channels;
// LOG_MSG("read: remaining_frames: %u", remaining_frames);
drmp3_uint16 total_samples_read = 0;
while (remaining_frames > 0) {
const drmp3_uint16 num_frames = (remaining_frames > in_buffer_frame_capacity) ? in_buffer_frame_capacity : remaining_frames;
// LOG_MSG("read-while: num_frames: %u", num_frames);
const drmp3_uint16 frames_just_read = static_cast<drmp3_uint16>(drmp3_read_pcm_frames_f32(p_mp3->p_dr, num_frames, in_buffer));
// LOG_MSG("read-while: frames_just_read: %u", frames_just_read);
if (frames_just_read == 0) {
break; // Reached the end.
}
const drmp3_uint16 samples_just_read = frames_just_read * channels;
// f32 -> s16
drmp3dec_f32_to_s16(in_buffer, out_buffer, samples_just_read);
remaining_frames -= frames_just_read;
out_buffer += samples_just_read;
total_samples_read += samples_just_read;
}
// SNDDBG(("encoded stream offset: %d", SDL_RWtell(internal->rw) ));
return total_samples_read * sizeof(drmp3_int16);
// LOG_MSG("read-while: num_frames: %u", num_frames);
return static_cast<Uint32>(drmp3_read_pcm_frames_s16(p_mp3->p_dr,
static_cast<drmp3_uint64>(desired_frames),
static_cast<drmp3_int16*>(buffer)));
} /* MP3_read */
static Sint32 MP3_open(Sound_Sample* const sample, const char* const ext)

View file

@ -4,19 +4,14 @@
*
* This decoders makes use of:
* - libopusfile, for .opus file handing and frame decoding
* - speexdsp, for resampling to the original input rate, if needed
*
* Source links
* - libogg: https://github.com/xiph/ogg
* - libopus: https://github.com/xiph/opus
* - opusfile: https://github.com/xiph/opusfile
* - speexdsp: https://github.com/xiph/speexdsp
* - opus-tools: https://github.com/xiph/opus-tools
* Documentation references
* - Ogg Opus: https://www.opus-codec.org/docs
* - OpusFile: https://mf4.xiph.org/jenkins/view/opus/job/opusfile-unix/ws/doc/html/index.html
* - Resampler: https://www.speex.org/docs/manual/speex-manual/node7.html
*
*/
@ -24,31 +19,13 @@
# include <config.h>
#endif
#ifdef _MSC_VER
// Avoid warning about getenv()
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdlib.h> // getenv
#include <math.h> // ceilf
// On macOS with GCC, pkg-config only include the opus/ subdirectory
// itself instead of the parent, so we take this into account:
#if defined(MACOSX) && ! defined(__clang__) && defined(__GNUC__)
#include <opusfile.h>
#else
#include <opus/opusfile.h>
#endif
#include <speex/speex_resampler.h>
#include "SDL_sound.h"
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
// The minimum buffer samples per channel: 120 ms @ 48 samples/ms, defined by opus
#define OPUS_MIN_BUFFER_SAMPLES_PER_CHANNEL 5760
// Opus's internal sample rates, to which all encoded streams get resampled
// Opus's internal sampling rates to which all encoded streams get resampled
#define OPUS_SAMPLE_RATE 48000
#define OPUS_SAMPLE_RATE_PER_MS 48
@ -56,7 +33,7 @@ static Sint32 opus_init (void);
static void opus_quit (void);
static Sint32 opus_open (Sound_Sample* sample, const char* ext);
static void opus_close (Sound_Sample* sample);
static Uint32 opus_read (Sound_Sample* sample);
static Uint32 opus_read (Sound_Sample* sample, void* buffer, Uint32 desired_frames);
static Sint32 opus_rewind (Sound_Sample* sample);
static Sint32 opus_seek (Sound_Sample* sample, const Uint32 ms);
@ -80,24 +57,6 @@ const Sound_DecoderFunctions __Sound_DecoderFunctions_OPUS =
opus_seek /* seek() method */
};
// Our private-decoder structure where we hold the opusfile, resampler,
// circular buffer, and buffer tracking variables.
typedef struct
{
Uint64 of_pcm; // absolute position in consumed Opus samples
OggOpusFile* of; // the actual opusfile we open/read/seek within
opus_int16* buffer; // pointer to the start of our circular buffer
SpeexResamplerState* resampler; // pointer to an instantiated resampler
float rate_ratio; // OPUS_RATE (48KHz) divided by desired sample rate
Uint16 buffer_size; // maximum number of samples we can hold in our buffer
Uint16 decoded; // number of samples decoded in our buffer
Uint16 consumed; // number of samples consumed in our buffer
Uint16 frame_size; // number of samples decoded in one opus frame
SDL_bool eof; // indicates if we've hit end-of-file decoding
} opus_t;
static Sint32 opus_init(void)
{
SNDDBG(("Opus init: done\n"));
@ -166,11 +125,9 @@ static Sint32 RWops_opus_read(void* stream, unsigned char* ptr, Sint32 nbytes)
static Sint32 RWops_opus_seek(void* stream, const opus_int64 offset, const Sint32 whence)
{
const Sint64 offset_after_seek = SDL_RWseek((SDL_RWops*)stream, (int)offset, whence);
SNDDBG(("Opus ops seek: "
"{requested offset: %ld, seeked offset: %ld}\n",
offset, offset_after_seek));
return (offset_after_seek != -1 ? 0 : -1);
} /* RWops_opus_seek */
@ -198,10 +155,8 @@ static Sint32 RWops_opus_close(void* stream)
static opus_int64 RWops_opus_tell(void* stream)
{
const Sint64 current_offset = SDL_RWtell((SDL_RWops*)stream);
SNDDBG(("Opus ops tell: "
"%ld\n", current_offset));
return current_offset;
} /* RWops_opus_tell */
@ -219,27 +174,20 @@ static __inline__ void output_opus_info(const OggOpusFile* of, const OpusHead* o
{
#if (defined DEBUG_CHATTER)
const OpusTags* ot = op_tags(of, -1);
// Guard
if ( of == NULL
|| oh == NULL
|| ot == NULL) {
return;
if (of != NULL && oh != NULL && ot != NULL) {
SNDDBG(("Opus serial number: %u\n", op_serialno(of, -1)));
SNDDBG(("Opus format version: %d\n", oh->version));
SNDDBG(("Opus channel count: %d\n", oh->channel_count ));
SNDDBG(("Opus seekable: %s\n", op_seekable(of) ? "True" : "False"));
SNDDBG(("Opus pre-skip samples: %u\n", oh->pre_skip));
SNDDBG(("Opus input sample rate: %u\n", oh->input_sample_rate));
SNDDBG(("Opus logical streams: %d\n", oh->stream_count));
SNDDBG(("Opus vendor: %s\n", ot->vendor));
for (int i = 0; i < ot->comments; i++) {
SNDDBG(("Opus: user comment: '%s'\n", ot->user_comments[i]));
}
}
// Dump info
SNDDBG(("Opus serial number: %u\n", op_serialno(of, -1)));
SNDDBG(("Opus format version: %d\n", oh->version));
SNDDBG(("Opus channel count: %d\n", oh->channel_count ));
SNDDBG(("Opus seekable: %s\n", op_seekable(of) ? "True" : "False"));
SNDDBG(("Opus pre-skip samples: %u\n", oh->pre_skip));
SNDDBG(("Opus input sample rate: %u\n", oh->input_sample_rate));
SNDDBG(("Opus logical streams: %d\n", oh->stream_count));
SNDDBG(("Opus vendor: %s\n", ot->vendor));
for (int i = 0; i < ot->comments; i++) {
SNDDBG(("Opus: user comment: '%s'\n", ot->user_comments[i]));
}
#endif
} /* output_opus_comments */
@ -247,19 +195,16 @@ static __inline__ void output_opus_info(const OggOpusFile* of, const OpusHead* o
* Opus Open
* ---------
* - Creates a new opus file object by using our our callback structure for all IO operations.
* - We also intialize and allocate memory for fields in the opus_t decode structure.
* - SDL expects a returns of 1 on success
*/
static Sint32 opus_open(Sound_Sample* sample, const char* ext)
{
Sint32 rcode;
Sound_SampleInternal* internal = (Sound_SampleInternal*)sample->opaque;
// Open the Opus File and print some info
OggOpusFile* of = op_open_callbacks(internal->rw, &RWops_opus_callbacks, NULL, 0, &rcode);
internal->decoder_private = of;
if (rcode != 0) {
op_free(of);
of = NULL;
opus_close(sample);
SNDDBG(("Opus open error: "
"'Could not open opus file: %s'\n", opus_strerror(rcode)));
BAIL_MACRO("Opus open fatal: 'Not a valid Ogg Opus file'", 0);
@ -267,65 +212,13 @@ static Sint32 opus_open(Sound_Sample* sample, const char* ext)
const OpusHead* oh = op_head(of, -1);
output_opus_info(of, oh);
// Initialize our decoder struct elements
opus_t* decoder = SDL_malloc(sizeof(opus_t));
decoder->of = of;
decoder->of_pcm = 0;
decoder->decoded = 0;
decoder->consumed = 0;
decoder->frame_size = 0;
decoder->eof = SDL_FALSE;
decoder->buffer = NULL;
// Connect our long-lived internal decoder to the one we're building here
internal->decoder_private = decoder;
if ( sample->desired.rate != 0
&& sample->desired.rate != OPUS_SAMPLE_RATE
&& getenv("SDL_DONT_RESAMPLE") == NULL) {
// Opus resamples all inputs to 48kHz. By default (if env-var SDL_DONT_RESAMPLE doesn't exist)
// we resample to the desired rate so the recieving SDL_sound application doesn't have to.
// This avoids breaking applications that don't expect 48kHz audio and also gives us
// quality-control by using the speex resampler, which has a noise floor of -140 dB, which
// is ~40dB lower than the -96dB offered by 16-bit CD-quality audio.
//
sample->actual.rate = sample->desired.rate;
decoder->rate_ratio = OPUS_SAMPLE_RATE / (float)(sample->desired.rate);
decoder->resampler = speex_resampler_init(oh->channel_count,
OPUS_SAMPLE_RATE,
sample->desired.rate,
// SPEEX_RESAMPLER_QUALITY_VOIP, // consumes ~20 Mhz
SPEEX_RESAMPLER_QUALITY_DEFAULT, // consumes ~40 Mhz
// SPEEX_RESAMPLER_QUALITY_DESKTOP, // consumes ~80 Mhz
&rcode);
// If we failed to initialize the resampler, then tear down
if (rcode < 0) {
opus_close(sample);
BAIL_MACRO("Opus: failed initializing the resampler", 0);
}
// Otherwise use native sampling
} else {
sample->actual.rate = OPUS_SAMPLE_RATE;
decoder->rate_ratio = 1.0;
decoder->resampler = NULL;
}
// Allocate our buffer to hold PCM samples from the Opus decoder
decoder->buffer_size = (Uint16) (oh->channel_count * OPUS_MIN_BUFFER_SAMPLES_PER_CHANNEL * 1.5);
decoder->buffer = SDL_malloc(decoder->buffer_size * sizeof(opus_int16));
// Gather static properties about our stream (channels, seek-ability, format, and duration)
sample->actual.rate = OPUS_SAMPLE_RATE;
sample->actual.channels = (Uint8)(oh->channel_count);
sample->flags = op_seekable(of) ? SOUND_SAMPLEFLAG_CANSEEK: 0;
sample->actual.format = AUDIO_S16LSB; // returns least-significant-byte order regardless of architecture
ogg_int64_t total_time = op_pcm_total(of, -1); // total PCM samples in the stream
sample->actual.format = AUDIO_S16SYS;
ogg_int64_t total_time = op_pcm_total(of, -1); // total PCM samples in the stream
internal->total_time = total_time == OP_EINVAL ? -1 : // total milliseconds in the stream
(Sint32)( (double)total_time / OPUS_SAMPLE_RATE_PER_MS);
return 1;
} /* opus_open */
@ -333,7 +226,7 @@ static Sint32 opus_open(Sound_Sample* sample, const char* ext)
/*
* Opus Close
* ----------
* Free and NULL all allocated memory pointers.
* Free and NULL all heap-allocated codec objects.
*/
static void opus_close(Sound_Sample* sample)
{
@ -341,26 +234,10 @@ static void opus_close(Sound_Sample* sample)
* then we are still responsible for freeing the OggOpusFile with op_free().
*/
Sound_SampleInternal* internal = (Sound_SampleInternal*) sample->opaque;
opus_t* d = internal->decoder_private;
if (d != NULL) {
if (d->of != NULL) {
op_free(d->of);
d->of = NULL;
}
if (d->resampler != NULL) {
speex_resampler_destroy(d->resampler);
d->resampler = NULL;
}
if (d->buffer != NULL) {
SDL_free(d->buffer);
d->buffer = NULL;
}
SDL_free(d);
d = NULL;
OggOpusFile* of = internal->decoder_private;
if (of != NULL) {
op_free(of);
internal->decoder_private = NULL;
}
return;
@ -370,137 +247,19 @@ static void opus_close(Sound_Sample* sample)
/*
* Opus Read
* ---------
* Decode, resample (if needed), and write the output to the
* requested buffer.
*/
static Uint32 opus_read(Sound_Sample* sample)
static Uint32 opus_read(Sound_Sample* sample, void* buffer, Uint32 desired_frames)
{
Sound_SampleInternal* internal = (Sound_SampleInternal*) sample->opaque;
opus_t* d = internal->decoder_private;
opus_int16* output_buffer = internal->buffer;
const Uint16 requested_output_size = internal->buffer_size / sizeof(opus_int16);
const Uint16 derived_consumption_size = (Uint16) ceilf(requested_output_size * d->rate_ratio);
// Three scenarios in order of probabilty:
//
// 1. consume: resample (if needed) a chunk from our decoded queue
// sufficient to fill the requested buffer.
//
// If the decoder has hit the end-of-file, drain any
// remaining decoded data before setting the EOF flag.
//
// 2. decode: decode chunks unil our buffer is full or we hit EOF.
//
// 3. wrap: we've decoded and consumed to edge of our buffer
// so wrap any remaining decoded samples back around.
Sint32 rcode = 1;
SDL_bool have_consumed = SDL_FALSE;
while (! have_consumed){
// consume ...
const Uint16 unconsumed_size = d->decoded - d->consumed;
if (unconsumed_size >= derived_consumption_size || d->eof) {
// If we're at the start of the stream, ignore 'pre-skip' samples
// per-channel. Pre-skip describes how much data must be decoded
// before valid output is obtained.
//
const OpusHead* oh = op_head(d->of, -1);
if (d->of_pcm == 0) {
d->consumed += oh->pre_skip * oh->channel_count;
}
// We use these to record the actual consumed and output sizes
Uint32 actual_consumed_size = unconsumed_size;
Uint32 actual_output_size = requested_output_size;
// If we need to resample
if (d->resampler) {
(void) speex_resampler_process_int(d->resampler, 0,
d->buffer + d->consumed,
&actual_consumed_size,
output_buffer,
&actual_output_size);
}
// Otherwise copy the bytes
else {
if (unconsumed_size < requested_output_size) {
actual_output_size = unconsumed_size;
}
actual_consumed_size = actual_output_size;
SDL_memcpy(output_buffer, d->buffer + d->consumed, actual_output_size * sizeof(opus_int16));
}
// bump our comsumption count and absolute pcm position
d->consumed += actual_consumed_size;
d->of_pcm += actual_consumed_size;
SNDDBG(("Opus read consuming: "
"{output: %u, so_far: %u, remaining_buffer: %u}\n",
actual_output_size, d->consumed, d->decoded - d->consumed));
// if we wrote less than requested then we're at the end-of-file
if (actual_output_size < requested_output_size) {
sample->flags |= SOUND_SAMPLEFLAG_EOF;
SNDDBG(("Opus read consuming: "
"{end_of_buffer: True, requested: %u, resampled_output: %u}\n",
requested_output_size, actual_output_size));
}
rcode = actual_output_size * sizeof(opus_int16); // covert from samples to bytes
have_consumed = SDL_TRUE;
}
else {
// wrap ...
if (d->frame_size > 0) {
SDL_memcpy(d->buffer,
d->buffer + d->consumed,
(d->decoded - d->consumed)*sizeof(opus_int16));
d->decoded -= d->consumed;
d->consumed = 0;
SNDDBG(("Opus read wrapping: "
"{wrapped: %u}\n", d->decoded));
}
// decode ...
while (rcode > 0 && d->buffer_size - d->decoded >= d->frame_size) {
rcode = sample->actual.channels * op_read(d->of,
d->buffer + d->decoded,
d->buffer_size - d->decoded, NULL);
// Use the largest decoded frame to know when
// our buffer is too small to hold a frame, to
// avoid constraining the decoder to fill sizes
// smaller than the stream's frame-size
if (rcode > d->frame_size) {
SNDDBG(("Opus read decoding: "
"{frame_previous: %u, frame_new: %u}\n",
d->frame_size, rcode));
d->frame_size = rcode;
}
// assess the validity of the return code
if (rcode > 0) { d->decoded += rcode;} // reading
else if (rcode == 0) { d->eof = SDL_TRUE;} // done
else if (rcode == OP_HOLE) { rcode = 1;} // hole in the data, carry on
else { // (rcode < 0) // error
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
}
SNDDBG(("Opus read decoding: "
"{decoded: %u, remaining buffer: %u, end_of_file: %s}\n",
rcode, d->buffer_size - d->decoded, d->eof ? "True" : "False"));
}
}
} // end while.
return rcode;
int decoded_frames = 0;
if (desired_frames > 0) {
Sound_SampleInternal* internal = (Sound_SampleInternal*) sample->opaque;
OggOpusFile* of = internal->decoder_private;
decoded_frames = op_read(of, (opus_int16*)buffer, desired_frames * sample->actual.channels, NULL);
if (decoded_frames == 0) { sample->flags |= SOUND_SAMPLEFLAG_EOF; }
else if (decoded_frames == OP_HOLE) { sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; }
else if (decoded_frames < 0) { sample->flags |= SOUND_SAMPLEFLAG_ERROR; }
}
return decoded_frames;
} /* opus_read */
@ -526,7 +285,7 @@ static Sint32 opus_rewind(Sound_Sample* sample)
static Sint32 opus_seek(Sound_Sample* sample, const Uint32 ms)
{
Sound_SampleInternal* internal = (Sound_SampleInternal*) sample->opaque;
opus_t* d = internal->decoder_private;
OggOpusFile* of = internal->decoder_private;
int rcode = -1;
#if (defined DEBUG_CHATTER)
@ -539,81 +298,17 @@ static Sint32 opus_seek(Sound_Sample* sample, const Uint32 ms)
// convert the desired ms offset into OPUS PCM samples
const ogg_int64_t desired_pcm = ms * OPUS_SAMPLE_RATE_PER_MS;
rcode = op_pcm_seek(of, desired_pcm);
// Is our stream already positioned at the requested offset?
if (d->of_pcm == desired_pcm) {
SNDDBG(("Opus seek avoided: "
"{requested_time: '%02d:%02d:%.2f', becomes_opus_pcm: %ld, actual_pcm_pos: %ld}\n",
hours, minutes, seconds, desired_pcm, d->of_pcm));
rcode = 1;
if (rcode != 0) {
SNDDBG(("Opus seek error: %s\n", opus_strerror(rcode)));
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
} else {
SNDDBG(("Opus seek in file: "
"{requested_time: '%02d:%02d:%.2f', becomes_opus_pcm: %ld}\n",
hours, minutes, seconds, desired_pcm));
}
// If not, check if we can jump within our circular buffer (and not actually seek!)
// In this scenario, we don't have to waste our currently decoded samples
// or incur the cost of 80ms of pre-roll decoding behind the scene in libopus.
else {
Uint64 pcm_start = d->of_pcm - d->consumed;
Uint64 pcm_end = pcm_start + d->decoded;
// In both scenarios below we're going to seek, in which case
// our sample flags should be reset and let the read function
// re-assess the flag.
//
// Is the requested pcm offset within our decoded range?
if ( (Uint64) desired_pcm >= pcm_start && (Uint64) desired_pcm <= pcm_end) {
SNDDBG(("Opus seek avoided: "
"{requested_time: '%02d:%02d:%.2f', becomes_opus_pcm: %ld, buffer_start: %ld, buffer_end: %ld}\n",
hours, minutes, seconds, desired_pcm, pcm_start, pcm_end));
// Yes, so simply adjust our existing pcm offset and consumption position
// No seeks or pre-roll needed!
d->consumed = (Uint16)(desired_pcm - pcm_start);
d->of_pcm = desired_pcm;
// reset our sample flags and let our consumption state re-apply
// the flags per its own rules
if (op_seekable(d->of)) {
sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
}
// note, we don't reset d->eof because our decode state is unchanged
rcode = 1;
// rcode is 1, confirming we successfully seeked
}
// No; the requested pcm offset is outside our circular decode buffer,
// so actually seek and reset our decode and consumption counters.
else {
rcode = op_pcm_seek(d->of, desired_pcm) + 1;
// op_pcm_seek(..) returns 0, to which we add 1, on success
// ... or a negative value on error.
if (rcode > 0) {
d->of_pcm = desired_pcm;
d->consumed = 0;
d->decoded = 0;
d->eof = SDL_FALSE;
SNDDBG(("Opus seek in file: "
"{requested_time: '%02d:%02d:%.2f', becomes_opus_pcm: %ld}\n",
hours, minutes, seconds, desired_pcm));
// reset our sample flags and let the read function re-apply
// sample flags as it hits them from our our offset
if (op_seekable(d->of)) {
sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
}
}
// otherwise we failed to seek.. so leave everything as-is.
}
}
BAIL_IF_MACRO(rcode < 0, ERR_IO_ERROR, 0);
return rcode;
return (rcode == 0);
} /* opus_seek */
/* end of ogg_opus.c ... */

View file

@ -51,20 +51,18 @@
# define memset SDL_memset
#endif
#define memcmp SDL_memcmp
#define qsort SDL_qsort
#define malloc SDL_malloc
#define realloc SDL_realloc
#define free SDL_free
#define dealloca(x) SDL_stack_free((x))
#define free SDL_free
#define qsort SDL_qsort
#define memcmp SDL_memcmp
#define malloc SDL_malloc
#define realloc SDL_realloc
#define dealloca(x) SDL_stack_free((x))
/* Configure and include stb_vorbis for compiling... */
#define STB_VORBIS_NO_STDIO 1
#define STB_VORBIS_NO_CRT 1
#define STB_VORBIS_NO_PUSHDATA_API 1
#define STB_VORBIS_MAX_CHANNELS 2
// #define STBV_CDECL
// #define STB_FORCEINLINE SDL_FORCE_INLINE
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define STB_VORBIS_BIG_ENDIAN 1
#endif
@ -153,28 +151,28 @@ static void VORBIS_close(Sound_Sample *sample)
} /* VORBIS_close */
static Uint32 VORBIS_read(Sound_Sample *sample)
static Uint32 VORBIS_read(Sound_Sample *sample, void* buffer, Uint32 desired_frames)
{
Uint32 retval;
int rc;
int err;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
stb_vorbis *stb = (stb_vorbis *) internal->decoder_private;
const int channels = (int) sample->actual.channels;
const int want_samples = (int) (internal->buffer_size / sizeof (int16_t));
const int desired_samples = desired_frames * channels;
// Note that for interleaved data, you pass in the number of shorts (the
// size of your array), but the return value is the number of samples per
// channel, not the total number of samples.
stb_vorbis_get_error(stb); /* clear any error state */
rc = stb_vorbis_get_samples_short_interleaved(stb, channels, (int16_t *) internal->buffer, want_samples);
retval = (Uint32) (rc * channels * sizeof (int16_t)); /* rc == number of sample frames read */
err = stb_vorbis_get_error(stb);
const int decoded_frames = stb_vorbis_get_samples_short_interleaved(stb, channels, (int16_t *) buffer, desired_samples);
const int err = stb_vorbis_get_error(stb);
if (retval == 0) {
if (decoded_frames == 0) {
sample->flags |= (err ? SOUND_SAMPLEFLAG_ERROR : SOUND_SAMPLEFLAG_EOF);
}
else if (retval < internal->buffer_size) {
else if (decoded_frames < (int) desired_frames) {
sample->flags |= SOUND_SAMPLEFLAG_EAGAIN;
}
return retval;
return decoded_frames;
} /* VORBIS_read */

View file

@ -120,14 +120,14 @@ static int WAV_open(Sound_Sample *sample, const char *ext)
} /* WAV_open */
static Uint32 WAV_read(Sound_Sample *sample)
static Uint32 WAV_read(Sound_Sample *sample, void* buffer, Uint32 desired_frames)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
drwav *dr = (drwav *) internal->decoder_private;
const drwav_uint64 frames_read = drwav_read_pcm_frames_s16(dr,
internal->buffer_size / (dr->channels * sizeof(drwav_int16)),
(drwav_int16 *) internal->buffer);
return (Uint32)frames_read * dr->channels * sizeof (drwav_int16);
desired_frames,
(drwav_int16 *) buffer);
return (Uint32)frames_read;
} /* WAV_read */