From 01b9880a6ad9b82aca5bb3ff52844cc9598213a9 Mon Sep 17 00:00:00 2001 From: Sergeanur Date: Mon, 30 Jan 2017 17:54:40 +0200 Subject: [PATCH] VAG decoder implemented Supports original mono VAG audio files as well as modified VAG format with stereo support (interleave 0x10). --- vbdec.cpp | 188 ++++++++++++++++++++++++++++-------------------------- vbdec.h | 27 ++++++-- 2 files changed, 117 insertions(+), 98 deletions(-) diff --git a/vbdec.cpp b/vbdec.cpp index 273d43f..a9f3277 100644 --- a/vbdec.cpp +++ b/vbdec.cpp @@ -1,7 +1,7 @@ #include "vbdec.h" #define GET_BLOCK(offset) ( offset >> 13 ) - +#define BE_TO_LE(i) ( ((i & 0xFF) << 24) | ((i & 0xFF00) << 8) | ((i & 0xFF0000) >> 8) | ((i & 0xFF000000) >> 24) ) static HPROVIDER providerHandle; void RegisterVBInterface() @@ -67,11 +67,11 @@ U32 AILCALL FAR PROVIDER_query_attribute(HATTRIB index) switch ( index ) { case PROVIDER_NAME: - return (U32)"MSS VB Audio Decoder"; + return (U32)"MSS VAG Audio Decoder"; case PROVIDER_VERSION: return 0x100; case IN_FTYPES: // Input file types - return (U32)"VB audio files\0*.VB"; + return (U32)"VAG audio files\0*.VAG"; //case IN_WTAG: // Input wave tag // return 85; case OUT_FTYPES: // Output file types; @@ -112,11 +112,27 @@ C8 FAR* AILCALL FAR ASI_error(void) // ASI stream -void FetchStr( ASISTREAM *STR, int offset = -1 ) +bool FetchStr( ASISTREAM *STR, int offset = -1 ) { - for (int i = 0; i < 2; i++) + U8 in_buf[0x20]; + + if ( STR->loop ) + { + offset = sizeof(STR->VAGheader); + STR->loop = false; + } + + auto bytes_read = STR->fetch_CB(STR->user, in_buf, STR->num_of_channels * 0x10, offset); + if (bytes_read < STR->num_of_channels * 0x10) return false; + + if (*(U32*)&in_buf[0] == 'pGAV') // hooray loop + { + bytes_read = STR->fetch_CB(STR->user, in_buf, STR->num_of_channels * 0x10, sizeof(STR->VAGheader)); + if (bytes_read < STR->num_of_channels * 0x10) return false; + } + + for (int i = 0; i < STR->num_of_channels; i++) { - STR->fetch_CB(STR->user, STR->channels[i].block_buffer, sizeof(STR->channels[i].block_buffer), offset); if (offset != -1) { STR->channels[i].s_1 = 0.0; @@ -130,44 +146,43 @@ void FetchStr( ASISTREAM *STR, int offset = -1 ) { 98.0 / 64.0, -55.0 / 64.0 }, { 122.0 / 64.0, -60.0 / 64.0 } }; - U8 *bufs[] = { STR->channels[0].block_buffer, STR->channels[1].block_buffer }; - S16 *dest[] = { STR->channels[0].frame, STR->channels[1].frame }; - for (int c = 0; c < 2; c++) + U8 *bufs[] = { in_buf, &in_buf[0x10] }; + S16 *dest[] = { STR->channels[0].decoded_samples, STR->channels[1].decoded_samples }; + for (int c = 0; c < STR->num_of_channels; c++) { - for ( int a = 0; a < 0x7000; a+=(28 * 2)) - { - //if (buffer_size <= 0) break; - int predict_nr = *(bufs[c]++); - int shift_factor = predict_nr & 0xf; - predict_nr >>= 4; - int flags = *(bufs[c]++); - if ( flags == 7 ) break; - for ( int i = 0; i < 28; i += 2 ) - { - int d = *(bufs[c]++); - int s = ( d & 0xf ) << 12; - if ( s & 0x8000 ) - s |= 0xffff0000; - STR->channels[c].samples[i] = (double) ( s >> shift_factor ); - s = ( d & 0xf0 ) << 8; - if ( s & 0x8000 ) - s |= 0xffff0000; - STR->channels[c].samples[i+1] = (double) ( s >> shift_factor ); + int predict_nr = *(bufs[c]++); + int shift_factor = predict_nr & 0xf; + predict_nr >>= 4; + int flags = *(bufs[c]++); + if ( flags == 7 ) return false; + for ( int i = 0; i < 28; i += 2 ) + { + int d = *(bufs[c]++); + int s = ( d & 0xf ) << 12; + if ( s & 0x8000 ) + s |= 0xffff0000; + STR->channels[c].samples[i] = (double) ( s >> shift_factor ); + s = ( d & 0xf0 ) << 8; + if ( s & 0x8000 ) + s |= 0xffff0000; + STR->channels[c].samples[i+1] = (double) ( s >> shift_factor ); - } - for ( int i = 0; i < 28; i++ ) - { - STR->channels[c].samples[i] = STR->channels[c].samples[i] + STR->channels[c].s_1 * f[predict_nr][0] + STR->channels[c].s_2 * f[predict_nr][1]; - STR->channels[c].s_2 = STR->channels[c].s_1; - STR->channels[c].s_1 = STR->channels[c].samples[i]; - int d = (int) ( STR->channels[c].samples[i] + 0.5 ); - *(dest[c]++) = (d & 0xffff); - } - if ( flags == 1 ) break; } + for ( int i = 0; i < 28; i++ ) + { + STR->channels[c].samples[i] = STR->channels[c].samples[i] + STR->channels[c].s_1 * f[predict_nr][0] + STR->channels[c].s_2 * f[predict_nr][1]; + STR->channels[c].s_2 = STR->channels[c].s_1; + STR->channels[c].s_1 = STR->channels[c].samples[i]; + int d = (int) ( STR->channels[c].samples[i] + 0.5 ); + if ( d & 0x8000 ) d |= 0xffff0000; + *(dest[c]++) = (d & 0xffff); + } + if ( flags == 1 ) break; } + return true; } + HASISTREAM AILCALL FAR ASI_stream_open(U32 user, AILASIFETCHCB fetch_CB, U32 total_size) { ASISTREAM FAR *STR = (ASISTREAM FAR *) AIL_mem_alloc_lock( sizeof(ASISTREAM) ); @@ -183,19 +198,23 @@ HASISTREAM AILCALL FAR ASI_stream_open(U32 user, AILASIFETCHCB fetch_CB, U32 tot STR->size = total_size; STR->cursor = 0; - STR->offset = 0; - STR->cur_block = -1; - STR->blocks = total_size / 0x4000; + STR->loop = false; - for (int i = 0; i < 2; i++) + fetch_CB(user, &STR->VAGheader, sizeof(STR->VAGheader), -1); + STR->VAGheader.version = BE_TO_LE(STR->VAGheader.version); + STR->VAGheader.sample_rate = BE_TO_LE(STR->VAGheader.sample_rate); + STR->VAGheader.size = BE_TO_LE(STR->VAGheader.size); + STR->num_of_channels = (STR->VAGheader.stereo != 0) ? 2 : 1; + + for (int i = 0; i < STR->num_of_channels; i++) { STR->channels[i].s_1 = 0.0; STR->channels[i].s_2 = 0.0; memset(STR->channels[i].samples, 0, sizeof(STR->channels[i].samples)); - memset(STR->channels[i].block_buffer, 0, sizeof(STR->channels[i].block_buffer)); - memset(STR->channels[i].frame, 0, sizeof(STR->channels[i].frame)); + memset(STR->channels[i].decoded_samples, 0, sizeof(STR->channels[i].decoded_samples)); } + FetchStr(STR, sizeof(STR->VAGheader)); return (HASISTREAM)STR; } @@ -211,58 +230,45 @@ ASIRESULT AILCALL ASI_stream_close(HASISTREAM stream) S32 AILCALL FAR ASI_stream_process(HASISTREAM stream, void FAR *buffer, S32 buffer_size) { ASISTREAM *STR = (ASISTREAM*)stream; - - if (GET_BLOCK(STR->offset) != STR->cur_block) + + S16 *dest = (S16 *)buffer; + S32 bytes_decoded = 0; + + for (int i = 0; i < buffer_size / (2 * STR->num_of_channels); i++) { - FetchStr(STR); - STR->cur_block = GET_BLOCK(STR->offset); - } - - - S16 *dest = (S16 *)buffer; - S32 bytes_decoded = 0; - - for (int i = 0; i < buffer_size / 4; i++) - { - if ( STR->cursor + i >= _countof(STR->channels[0].frame) ) - break; - - for (int c = 0; c < 2; c++) - { - dest[i * 2 + c] = STR->channels[c].frame[STR->cursor + i]; - bytes_decoded += sizeof(S16); - } - } - STR->cursor += buffer_size / 4; - if (STR->cursor >= _countof(STR->channels[0].frame)) - { - STR->cursor = 0; - STR->offset += _countof(STR->channels[0].block_buffer); + for (int c = 0; c < STR->num_of_channels; c++) + { + dest[i * STR->num_of_channels + c] = STR->channels[c].decoded_samples[STR->cursor]; + bytes_decoded += sizeof(S16); + } + STR->cursor++; + if (STR->cursor >= 28) + { + STR->cursor = 0; + if (!FetchStr(STR)) + break; + } } - return bytes_decoded; } -bool sfile = false; ASIRESULT AILCALL ASI_stream_seek (HASISTREAM stream, S32 stream_offset) { ASISTREAM *STR = (ASISTREAM*)stream; if (stream_offset > STR->size) return ASI_INVALID_PARAM; - if (stream_offset != -2) // loop - stream_offset = 0; - - stream_offset &= 0xFFFFE000; - STR->offset = stream_offset; - - if (GET_BLOCK(STR->offset) != STR->cur_block) + if (stream_offset == -2) // loop { - FetchStr(STR, STR->offset); - STR->cur_block = GET_BLOCK(STR->offset); + stream_offset = sizeof(STR->VAGheader); + STR->loop = true; + } + else + { + STR->cursor = 0; + FetchStr(STR, stream_offset); } - STR->cursor = 0; - return ASI_NOERR; + return ASI_NOERR; } S32 AILCALL FAR ASI_stream_attribute (HASISTREAM stream, HATTRIB attrib) @@ -270,21 +276,21 @@ S32 AILCALL FAR ASI_stream_attribute (HASISTREAM stream, HATTRIB attrib) ASISTREAM *STR = (ASISTREAM*)stream; switch (attrib) { - case INPUT_BIT_RATE: return 0x7D00000; - case INPUT_SAMPLE_RATE: return 32000; + case INPUT_BIT_RATE: return 0x10 * 1000 * STR->num_of_channels * 8; // align 0x10 bytes * channels + case INPUT_SAMPLE_RATE: return STR->VAGheader.sample_rate; case INPUT_BITS: return 4; - case INPUT_CHANNELS: return 2; - case OUTPUT_BIT_RATE: return 32000 * 16 * 2; - case OUTPUT_SAMPLE_RATE: return 32000; + case INPUT_CHANNELS: return STR->num_of_channels; + case OUTPUT_BIT_RATE: return STR->VAGheader.sample_rate * 16 * STR->num_of_channels; + case OUTPUT_SAMPLE_RATE: return STR->VAGheader.sample_rate; case OUTPUT_BITS: return 16; - case OUTPUT_CHANNELS: return 2; + case OUTPUT_CHANNELS: return STR->num_of_channels; case POSITION: return STR->offset; case PERCENT_DONE: { - float percent = ((float)(100.0 * STR->offset) / (STR->size / (2 * 16 * 512))); + float percent = ((float)(100.0 * STR->offset) / (STR->size / (STR->num_of_channels * 16 * 512))); return *(S32*)&percent; } - case MIN_INPUT_BLOCK_SIZE: return 2 * 16 * 512; + case MIN_INPUT_BLOCK_SIZE: return STR->num_of_channels * 16 * 512; } return -1; } diff --git a/vbdec.h b/vbdec.h index 8c3633e..f7d1a6f 100644 --- a/vbdec.h +++ b/vbdec.h @@ -1,7 +1,6 @@ #pragma once #include "mss/mss.h" -#include enum PROPERTY { @@ -54,20 +53,34 @@ struct ASISTREAM U32 offset; - U32 cursor; + S32 cursor; U32 blocks; U32 cur_block; - //U32 block_buffer[2][0x1000]; + U32 num_of_channels; + + bool loop; + + struct // Big endian + { + char VAG[3]; + char field_3; // can be p or i + int version; // doesn't seem to matter at all + int field_8; // always 0? + int size; // size minus 0x30 + int sample_rate; + int stereo; + int field_18[2]; // always 0? + char filename[16]; + char padding[16]; + } VAGheader; + struct { double s_1, s_2; double samples[28]; - U8 block_buffer[0x2000]; - S16 frame[0x3800]; + S16 decoded_samples[28]; } channels[2]; - - //FILE* debug_file; }; void RegisterVBInterface();