diff --git a/src/Audio.c b/src/Audio.c index 3d1ae615d..e7d0bdf03 100644 --- a/src/Audio.c +++ b/src/Audio.c @@ -744,8 +744,7 @@ static cc_result Music_Buffer(int i, cc_int16* data, int maxSamples, struct Vorb } static cc_result Music_PlayOgg(struct Stream* source) { - cc_uint8 buffer[OGG_BUFFER_SIZE]; - struct Stream stream; + struct OggState ogg; struct VorbisState vorbis = { 0 }; struct AudioFormat fmt; @@ -755,8 +754,8 @@ static cc_result Music_PlayOgg(struct Stream* source) { int i, next; cc_result res; - Ogg_MakeStream(&stream, buffer, source); - vorbis.source = &stream; + Ogg_Init(&ogg, source); + vorbis.source = &ogg; if ((res = Vorbis_DecodeHeaders(&vorbis))) goto cleanup; fmt.Channels = vorbis.channels; diff --git a/src/Resources.c b/src/Resources.c index 1f76487f3..68c1f591d 100644 --- a/src/Resources.c +++ b/src/Resources.c @@ -667,8 +667,8 @@ static void SoundPatcher_DecodeAudio(struct Stream* s, struct VorbisState* ctx) static void SoundPatcher_Save(const char* name, struct HttpRequest* req) { String path; char pathBuffer[STRING_SIZE]; - cc_uint8 buffer[OGG_BUFFER_SIZE]; - struct Stream src, ogg, dst; + struct OggState ogg; + struct Stream src, dst; struct VorbisState ctx = { 0 }; cc_result res; @@ -679,7 +679,7 @@ static void SoundPatcher_Save(const char* name, struct HttpRequest* req) { res = Stream_CreateFile(&dst, &path); if (res) { Logger_Warn(res, "creating .wav file"); return; } - Ogg_MakeStream(&ogg, buffer, &src); + Ogg_Init(&ogg, &src); ctx.source = &ogg; SoundPatcher_DecodeAudio(&dst, &ctx); diff --git a/src/Vorbis.c b/src/Vorbis.c index 6fe968a99..241200857 100644 --- a/src/Vorbis.c +++ b/src/Vorbis.c @@ -12,7 +12,7 @@ *-------------------------------------------------------Ogg stream--------------------------------------------------------* *#########################################################################################################################*/ #define OGG_FourCC(a, b, c, d) (((cc_uint32)a << 24) | ((cc_uint32)b << 16) | ((cc_uint32)c << 8) | (cc_uint32)d) -static cc_result Ogg_NextPage(struct Stream* stream) { +static cc_result Ogg_NextPage(struct OggState* ctx) { cc_uint8 header[27]; struct Stream* source; cc_uint32 sig, size; @@ -32,7 +32,7 @@ static cc_result Ogg_NextPage(struct Stream* stream) { * [number of segments] number of bytes in each segment * [sum of bytes in each segment] page data */ - source = stream->Meta.Ogg.Source; + source = ctx->source; if ((res = Stream_Read(source, header, sizeof(header)))) return res; sig = Stream_GetU32_BE(&header[0]); @@ -44,52 +44,68 @@ static cc_result Ogg_NextPage(struct Stream* stream) { if ((res = Stream_Read(source, segments, numSegments))) return res; for (i = 0; i < numSegments; i++) size += segments[i]; - if ((res = Stream_Read(source, stream->Meta.Ogg.Base, size))) return res; - stream->Meta.Ogg.Cur = stream->Meta.Ogg.Base; - stream->Meta.Ogg.Left = size; - stream->Meta.Ogg.Last = header[5] & 4; + if ((res = Stream_Read(source, ctx->buffer, size))) return res; + ctx->cur = ctx->buffer; + ctx->left = size; + ctx->last = header[5] & 4; return 0; } -static cc_result Ogg_Read(struct Stream* stream, cc_uint8* data, cc_uint32 count, cc_uint32* modified) { +static cc_result Ogg_Read(struct OggState* ctx, cc_uint8* data, cc_uint32 count) { + cc_uint32 left = count; cc_result res; - for (;;) { - if (stream->Meta.Ogg.Left) { - count = min(count, stream->Meta.Ogg.Left); - Mem_Copy(data, stream->Meta.Ogg.Cur, count); + while (left) { + if (ctx->left) { + count = min(left, ctx->left); + Mem_Copy(data, ctx->cur, count); - *modified = count; - stream->Meta.Ogg.Cur += count; - stream->Meta.Ogg.Left -= count; - return 0; + ctx->cur += count; + ctx->left -= count; + left -= count; + } else { + if (ctx->last) return ERR_END_OF_STREAM; + if ((res = Ogg_NextPage(ctx))) return res; } - - /* try again with data from next page */ - *modified = 0; - if (stream->Meta.Ogg.Last) return 0; - if ((res = Ogg_NextPage(stream))) return res; } -} - -static cc_result Ogg_ReadU8(struct Stream* stream, cc_uint8* data) { - if (!stream->Meta.Ogg.Left) return Stream_DefaultReadU8(stream, data); - - *data = *stream->Meta.Ogg.Cur; - stream->Meta.Ogg.Cur++; - stream->Meta.Ogg.Left--; return 0; } -void Ogg_MakeStream(struct Stream* stream, cc_uint8* buffer, struct Stream* source) { - Stream_Init(stream); - stream->Read = Ogg_Read; - stream->ReadU8 = Ogg_ReadU8; +static cc_result Ogg_Skip(struct OggState* ctx, cc_uint32 count) { + cc_uint8 tmp[3584]; /* not quite 4 KB to avoid chkstk call */ + cc_uint32 left = count; + cc_result res; - stream->Meta.Ogg.Cur = buffer; - stream->Meta.Ogg.Base = buffer; - stream->Meta.Ogg.Left = 0; - stream->Meta.Ogg.Last = 0; - stream->Meta.Ogg.Source = source; + /* TODO: Should Ogg_Read be duplicated here to avoid Mem_Copy call? */ + /* Probably not worth it considering how small comments are */ + while (left) { + count = min(left, sizeof(tmp)); + if ((res = Ogg_Read(ctx, tmp, count))) return res; + left -= count; + } + return 0; +} + +static cc_result Ogg_ReadU8(struct OggState* ctx, cc_uint8* data) { + /* The fast path below almost always gets used */ + if (!ctx->left) return Ogg_Read(ctx, data, 1); + + *data = *ctx->cur; + ctx->cur++; + ctx->left--; + return 0; +} + +static cc_result Ogg_ReadU32(struct OggState* ctx, cc_uint32* value) { + cc_uint8 data[4]; cc_result res; + if ((res = Ogg_Read(ctx, data, 4))) return res; + *value = Stream_GetU32_LE(data); return 0; +} + +void Ogg_Init(struct OggState* ctx, struct Stream* source) { + ctx->cur = ctx->buffer; + ctx->left = 0; + ctx->last = 0; + ctx->source = source; } @@ -109,7 +125,7 @@ static cc_uint32 Vorbis_ReadBits(struct VorbisState* ctx, cc_uint32 bitsCount) { cc_result res; while (ctx->NumBits < bitsCount) { - res = ctx->source->ReadU8(ctx->source, &portion); + res = Ogg_ReadU8(ctx->source, &portion); if (res) { Logger_Abort2(res, "Failed to read byte for vorbis"); } Vorbis_PushByte(ctx, portion); } @@ -123,7 +139,7 @@ static cc_result Vorbis_TryReadBits(struct VorbisState* ctx, cc_uint32 bitsCount cc_result res; while (ctx->NumBits < bitsCount) { - res = ctx->source->ReadU8(ctx->source, &portion); + res = Ogg_ReadU8(ctx->source, &portion); if (res) return res; Vorbis_PushByte(ctx, portion); } @@ -138,7 +154,7 @@ static cc_uint32 Vorbis_ReadBit(struct VorbisState* ctx) { cc_result res; if (!ctx->NumBits) { - res = ctx->source->ReadU8(ctx->source, &portion); + res = Ogg_ReadU8(ctx->source, &portion); if (res) { Logger_Abort2(res, "Failed to read byte for vorbis"); } Vorbis_PushByte(ctx, portion); } @@ -1131,7 +1147,7 @@ static cc_result Vorbis_CheckHeader(struct VorbisState* ctx, cc_uint8 type) { cc_bool OK; cc_result res; - if ((res = Stream_Read(ctx->source, header, sizeof(header)))) return res; + if ((res = Ogg_Read(ctx->source, header, sizeof(header)))) return res; if (header[0] != type) return VORBIS_ERR_WRONG_HEADER; OK = @@ -1145,7 +1161,7 @@ static cc_result Vorbis_DecodeIdentifier(struct VorbisState* ctx) { cc_uint32 version; cc_result res; - if ((res = Stream_Read(ctx->source, header, sizeof(header)))) return res; + if ((res = Ogg_Read(ctx->source, header, sizeof(header)))) return res; version = Stream_GetU32_LE(&header[0]); if (version != 0) return VORBIS_ERR_VERSION; @@ -1168,21 +1184,21 @@ static cc_result Vorbis_DecodeComments(struct VorbisState* ctx) { cc_uint32 i, len, comments; cc_uint8 flag; cc_result res; - struct Stream* stream = ctx->source; + struct OggState* source = ctx->source; /* vendor name, followed by comments */ - if ((res = Stream_ReadU32_LE(stream, &len))) return res; - if ((res = stream->Skip(stream, len))) return res; - if ((res = Stream_ReadU32_LE(stream, &comments))) return res; + if ((res = Ogg_ReadU32(source, &len))) return res; + if ((res = Ogg_Skip(source, len))) return res; + if ((res = Ogg_ReadU32(source, &comments))) return res; for (i = 0; i < comments; i++) { /* comments such as artist, year, etc */ - if ((res = Stream_ReadU32_LE(stream, &len))) return res; - if ((res = stream->Skip(stream, len))) return res; + if ((res = Ogg_ReadU32(source, &len))) return res; + if ((res = Ogg_Skip(source, len))) return res; } /* check framing flag */ - if ((res = stream->ReadU8(stream, &flag))) return res; + if ((res = Ogg_ReadU8(source, &flag))) return res; return (flag & 1) ? 0 : VORBIS_ERR_FRAMING; } diff --git a/src/Vorbis.h b/src/Vorbis.h index 42a302898..221ed20d4 100644 --- a/src/Vorbis.h +++ b/src/Vorbis.h @@ -9,9 +9,15 @@ struct Stream; #define VORBIS_MAX_BLOCK_SIZE 8192 #define OGG_BUFFER_SIZE (255 * 256) -/* Wraps an OGG container stream around an existing stream. */ -/* NOTE: buffer must be of size OGG_BUFFER_SIZE at least. */ -void Ogg_MakeStream(struct Stream* stream, cc_uint8* buffer, struct Stream* source); +struct OggState { + cc_uint8* cur; + cc_uint32 left, last; + struct Stream* source; + cc_uint8 buffer[OGG_BUFFER_SIZE]; +}; + +/* Wraps an OGG container around an existing stream. */ +void Ogg_Init(struct OggState* ctx, struct Stream* source); struct Codebook; struct Floor; struct Residue; struct Mapping; struct Mode; struct imdct_state { @@ -26,7 +32,7 @@ struct VorbisWindow { float* Prev; float* Cur; }; struct VorbisState { cc_uint32 Bits; /* Holds bits across byte boundaries*/ cc_uint32 NumBits; /* Number of bits in Bits buffer*/ - struct Stream* source; /* Source for filling Input buffer */ + struct OggState* source; /* Source for filling Input buffer */ cc_uint8 channels, modeNumBits; cc_uint16 curBlockSize, prevBlockSize, dataSize, numCodebooks;