diff --git a/src/base/info.c b/src/base/info.c index eb24828d..df3cea81 100644 --- a/src/base/info.c +++ b/src/base/info.c @@ -1,6 +1,7 @@ #include #include "../vgmstream.h" #include "../coding/coding.h" +#include "../layout/layout.h" #include "mixing.h" #include "../util/channel_mappings.h" #include "../util/sf_utils.h" diff --git a/src/base/mixing.c b/src/base/mixing.c index 6ed4f275..afd4313e 100644 --- a/src/base/mixing.c +++ b/src/base/mixing.c @@ -4,6 +4,7 @@ #include "mixer.h" #include "mixer_priv.h" #include "sbuf.h" +#include "../layout/layout.h" #include #include diff --git a/src/base/mixing_macros.c b/src/base/mixing_macros.c index a9a6eb7e..fa3ce258 100644 --- a/src/base/mixing_macros.c +++ b/src/base/mixing_macros.c @@ -1,5 +1,6 @@ #include "../vgmstream.h" #include "../util/channel_mappings.h" +#include "../layout/layout.h" #include "mixing.h" #include "mixer_priv.h" #include diff --git a/src/base/plugins.h b/src/base/plugins.h index dbc25c79..f937f7b9 100644 --- a/src/base/plugins.h +++ b/src/base/plugins.h @@ -8,6 +8,14 @@ #include "../vgmstream.h" +/* List supported formats and return elements in the list, for plugins that need to know. + * The list disables some common formats that may conflict (.wav, .ogg, etc). */ +const char** vgmstream_get_formats(size_t* size); + +/* same, but for common-but-disabled formats in the above list. */ +const char** vgmstream_get_common_formats(size_t* size); + + /* ****************************************** */ /* CONTEXT: simplifies plugin code */ /* ****************************************** */ diff --git a/src/coding/ws_decoder.c b/src/coding/ws_decoder.c index ea0a36ed..9057986c 100644 --- a/src/coding/ws_decoder.c +++ b/src/coding/ws_decoder.c @@ -3,135 +3,148 @@ #include "../util.h" /* Westwood Studios ADPCM */ - /* Based on Valery V. Anisimovsky's WS-AUD.txt */ -static char WSTable2bit[4] = { -2,-1,0,1 }; -static char WSTable4bit[16] = { -9,-8,-6,-5,-4,-3,-2,-1, 0, 1, 2, 3, 4, 5 ,6, 8 }; +static char WSTable2bit[4] = { -2, -1, 0, 1 }; +static char WSTable4bit[16] = { -9, -8, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8 }; -/* We pass in the VGMSTREAM here, unlike in other codings, because - the decoder has to know about the block structure. */ -void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample, - int32_t samples_to_do) { - VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]); +/* We pass in the VGMSTREAM here, unlike in other codings, because the decoder has to know about the block structure. */ +void decode_ws(VGMSTREAM* vgmstream, int channel, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + VGMSTREAMCHANNEL* stream = &(vgmstream->ch[channel]); + STREAMFILE* sf = stream->streamfile; int16_t hist = stream->adpcm_history1_16; off_t offset = stream->offset; - int samples_left_in_frame = stream->samples_left_in_frame; - off_t header_off = stream->frame_header_offset; + int samples_left_in_frame = stream->ws_samples_left_in_frame; + off_t header_offset = stream->ws_frame_header_offset; - int i; - int32_t sample_count; + //int i; + int32_t sample_count = 0; if (vgmstream->ws_output_size == vgmstream->current_block_size) { - /* uncompressed, we just need to convert to 16-bit */ - for (i=first_sample,sample_count=0; istreamfile)-0x80)*0x100; + /* uncompressed pcm8 to pcm16 */ + for (int i = first_sample; i < first_sample + samples_to_do; i++) { + outbuf[sample_count] = (read_u8(offset,sf) - 0x80) * 0x100; + sample_count += channelspacing; + offset++; } - } else { + } + else { if (first_sample == 0) { hist = 0x80; samples_left_in_frame = 0; } - /* actually decompress */ - for (i=first_sample,sample_count=0; istreamfile); - count = header & 0x3f; - switch (header>>6) { /* code */ + uint8_t header = read_u8(header_offset, sf); + uint8_t count = header & 0x3f; + uint8_t code = header >> 6; + switch (code) { /* code */ case 0: /* 2-bit ADPCM */ - if (samples_left_in_frame == 0) { - samples_left_in_frame = (count + 1)*4; - } - for (;samples_left_in_frame>0 && /* read this frame */ - istreamfile); - sample = (sample >> (twobit*2)) & 0x3; + if (samples_left_in_frame == 0) + samples_left_in_frame = (count + 1) * 4; + + /* read this frame up to samples_to_do */ + for ( ; samples_left_in_frame>0 && i < first_sample + samples_to_do; i++) { + + int twobit = ((count + 1) * 4 - samples_left_in_frame) % 4; + uint8_t sample = read_u8(offset,sf); + sample = (sample >> (twobit * 2)) & 0x3; hist += WSTable2bit[sample]; + if (hist < 0) hist = 0; - if (hist > 0xff) hist = 0xff; - outbuf[sample_count]=(hist-0x80)*0x100; + else if (hist > 0xff) hist = 0xff; + + outbuf[sample_count] = (hist - 0x80) * 0x100; + sample_count += channelspacing; + samples_left_in_frame--; if (twobit == 3) offset++; /* done with that byte */ } break; + case 1: /* 4-bit ADPCM */ - if (samples_left_in_frame == 0) { - samples_left_in_frame = (count + 1)*2; - } - for (;samples_left_in_frame>0 && /* read this frame */ - istreamfile); + if (samples_left_in_frame == 0) + samples_left_in_frame = (count + 1) * 2; + + /* read this frame up to samples_to_do */ + for ( ; samples_left_in_frame>0 && i < first_sample + samples_to_do; i++) { + + int nibble = ((count + 1) * 4 - samples_left_in_frame) % 2; + uint8_t sample = read_u8(offset, sf); if (nibble == 0) sample &= 0xf; else sample >>= 4; hist += WSTable4bit[sample]; + if (hist < 0) hist = 0; - if (hist > 0xff) hist = 0xff; - outbuf[sample_count]=(hist-0x80)*0x100; + else if (hist > 0xff) hist = 0xff; + + outbuf[sample_count] = (hist - 0x80) * 0x100; + sample_count += channelspacing; + samples_left_in_frame--; if (nibble == 1) offset++; /* done with that byte */ } break; + case 2: /* no compression */ - if (count & 0x20) { /* delta */ - /* Note no checks against samples_to_do here, - at the top of the for loop we can always do at - least one sample */ + if (count & 0x20) { /* new delta */ + /* Note no checks against samples_to_do here, at the top of the for loop + * we can always do at least one sample */ + /* low 5 bits are a signed delta */ if (count & 0x10) { - hist -= ((count & 0xf)^0xf) + 1; + hist -= ((count & 0x0f) ^ 0x0f) + 1; } else { - hist += count & 0xf; + hist += count & 0x0f; } - /* Valery doesn't specify this, but I will assume */ + /* Valery doesn't specify this, but clamp just in case */ if (hist < 0) hist = 0; - if (hist > 0xff) hist = 0xff; + else if (hist > 0xff) hist = 0xff; - outbuf[sample_count]=(hist-0x80)*0x100; - sample_count+=channelspacing; i++; - /* just one, and we got it */ - samples_left_in_frame = 0; - } else { /* copy bytes verbatim */ + outbuf[sample_count] = (hist - 0x80) * 0x100; + sample_count += channelspacing; + samples_left_in_frame = 0; /* just one */ + } + else { + /* copy bytes verbatim */ if (samples_left_in_frame == 0) - samples_left_in_frame=count+1; - for (;samples_left_in_frame>0 && /* read this frame */ - istreamfile))-0x80)*0x100; + samples_left_in_frame = (count + 1); + + /* read this frame up to samples_to_do */ + for ( ; samples_left_in_frame > 0 && i < first_sample + samples_to_do; i++) { + hist = read_u8(offset,sf); + offset++; + + outbuf[sample_count] = (hist - 0x80) * 0x100; + sample_count += channelspacing; + samples_left_in_frame--; } } break; + case 3: /* RLE */ if (samples_left_in_frame == 0) - samples_left_in_frame=count+1; - for (;samples_left_in_frame>0 && /* read this frame */ - i 0 && i < first_sample + samples_to_do; i++) { + outbuf[sample_count] = (hist - 0x80) * 0x100; + sample_count += channelspacing; + samples_left_in_frame--; } default: break; @@ -141,6 +154,6 @@ void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channels stream->offset = offset; stream->adpcm_history1_16 = hist; - stream->samples_left_in_frame = samples_left_in_frame; - stream->frame_header_offset = header_off; + stream->ws_samples_left_in_frame = samples_left_in_frame; + stream->ws_frame_header_offset = header_offset; } diff --git a/src/formats.c b/src/formats.c index e06f5c05..27ab3b7f 100644 --- a/src/formats.c +++ b/src/formats.c @@ -1,5 +1,6 @@ #include "vgmstream.h" #include "coding/coding.h" +#include "layout/layout.h" /* Defines the list of accepted extensions. vgmstream doesn't use it internally so it's here @@ -1237,7 +1238,7 @@ static const meta_info meta_info_list[] = { {meta_EB_SF0, "assumed Excitebots .sf0 by extension"}, {meta_MTAF, "Konami MTAF header"}, {meta_ALP, "High Voltage ALP header"}, - {meta_WPD, "WPD 'DPW' header"}, + {meta_WPD, "Navel WPD header"}, {meta_MN_STR, "Mini Ninjas 'STR' header"}, {meta_MSS, "Guerilla MCSS header"}, {meta_PS2_HSF, "Lowrider 'HSF' header"}, diff --git a/src/layout/layout.h b/src/layout/layout.h index 3423e2d8..6b4d15e5 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -6,6 +6,53 @@ #include "../util/reader_sf.h" #include "../util/log.h" +/* basic layouts */ +void render_vgmstream_flat(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); + +void render_vgmstream_interleave(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); + +/* segmented layout */ +/* for files made of "continuous" segments, one per section of a song (using a complete sub-VGMSTREAM) */ +typedef struct { + int segment_count; + VGMSTREAM** segments; + int current_segment; + sample_t* buffer; + int input_channels; /* internal buffer channels */ + int output_channels; /* resulting channels (after mixing, if applied) */ + int mixed_channels; /* segments have different number of channels */ +} segmented_layout_data; + +void render_vgmstream_segmented(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); +segmented_layout_data* init_layout_segmented(int segment_count); +int setup_layout_segmented(segmented_layout_data* data); +void free_layout_segmented(segmented_layout_data* data); +void reset_layout_segmented(segmented_layout_data* data); +void seek_layout_segmented(VGMSTREAM* vgmstream, int32_t seek_sample); +void loop_layout_segmented(VGMSTREAM* vgmstream, int32_t loop_sample); +VGMSTREAM* allocate_segmented_vgmstream(segmented_layout_data* data, int loop_flag, int loop_start_segment, int loop_end_segment); + +/* layered layout */ +/* for files made of "parallel" layers, one per group of channels (using a complete sub-VGMSTREAM) */ +typedef struct { + int layer_count; + VGMSTREAM** layers; + sample_t* buffer; + int input_channels; /* internal buffer channels */ + int output_channels; /* resulting channels (after mixing, if applied) */ + int external_looping; /* don't loop using per-layer loops, but layout's own looping */ + int curr_layer; /* helper */ +} layered_layout_data; + +void render_vgmstream_layered(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); +layered_layout_data* init_layout_layered(int layer_count); +int setup_layout_layered(layered_layout_data* data); +void free_layout_layered(layered_layout_data* data); +void reset_layout_layered(layered_layout_data* data); +void seek_layout_layered(VGMSTREAM* vgmstream, int32_t seek_sample); +void loop_layout_layered(VGMSTREAM* vgmstream, int32_t loop_sample); +VGMSTREAM* allocate_layered_vgmstream(layered_layout_data* data); + /* blocked layouts */ void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); void block_update(off_t block_offset, VGMSTREAM* vgmstream); @@ -51,27 +98,4 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream); void block_update_tt_ad(off_t block_offset, VGMSTREAM* vgmstream); void block_update_vas(off_t block_offset, VGMSTREAM* vgmstream); -/* other layouts */ -void render_vgmstream_interleave(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); - -void render_vgmstream_flat(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); - -void render_vgmstream_segmented(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); -segmented_layout_data* init_layout_segmented(int segment_count); -int setup_layout_segmented(segmented_layout_data* data); -void free_layout_segmented(segmented_layout_data* data); -void reset_layout_segmented(segmented_layout_data* data); -void seek_layout_segmented(VGMSTREAM* vgmstream, int32_t seek_sample); -void loop_layout_segmented(VGMSTREAM* vgmstream, int32_t loop_sample); -VGMSTREAM *allocate_segmented_vgmstream(segmented_layout_data* data, int loop_flag, int loop_start_segment, int loop_end_segment); - -void render_vgmstream_layered(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); -layered_layout_data* init_layout_layered(int layer_count); -int setup_layout_layered(layered_layout_data* data); -void free_layout_layered(layered_layout_data* data); -void reset_layout_layered(layered_layout_data* data); -void seek_layout_layered(VGMSTREAM* vgmstream, int32_t seek_sample); -void loop_layout_layered(VGMSTREAM* vgmstream, int32_t loop_sample); -VGMSTREAM *allocate_layered_vgmstream(layered_layout_data* data); - #endif diff --git a/src/vgmstream.h b/src/vgmstream.h index 69f7575e..d7030a67 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -94,27 +94,28 @@ typedef struct { } play_state_t; -/* info for a single vgmstream channel */ +/* info for a single vgmstream 'channel' (or rather, mono stream) */ typedef struct { STREAMFILE* streamfile; /* file used by this channel */ off_t channel_start_offset; /* where data for this channel begins */ off_t offset; /* current location in the file */ - off_t frame_header_offset; /* offset of the current frame header (for WS) */ - int samples_left_in_frame; /* for WS */ + /* format and channel specific */ - /* format specific */ - - /* adpcm */ - int16_t adpcm_coef[16]; /* formats with decode coefficients built in (DSP, some ADX) */ - int32_t adpcm_coef_3by32[0x60]; /* Level-5 0x555 */ - int16_t vadpcm_coefs[8*2*8]; /* VADPCM: max 8 groups * max 2 order * fixed 8 subframe coefs */ + /* ADPCM with built or variable decode coefficients */ union { - int16_t adpcm_history1_16; /* previous sample */ + int16_t adpcm_coef[16]; /* DSP, some ADX (in rare cases may change per block) */ + int16_t vadpcm_coefs[8*2*8]; /* VADPCM: max 8 groups * max 2 order * fixed 8 subframe = 128 coefs */ + int32_t adpcm_coef_3by32[96]; /* Level-5 0x555 */ + }; + + /* previous ADPCM samples */ + union { + int16_t adpcm_history1_16; int32_t adpcm_history1_32; }; union { - int16_t adpcm_history2_16; /* previous previous sample */ + int16_t adpcm_history2_16; int32_t adpcm_history2_32; }; union { @@ -129,14 +130,20 @@ typedef struct { //double adpcm_history1_double; //double adpcm_history2_double; - int adpcm_step_index; /* for IMA */ - int adpcm_scale; /* for MS ADPCM */ + /* for ADPCM decoders that store steps (IMA) or scales (MSADPCM) */ + union { + int adpcm_step_index; + int adpcm_scale; + }; + + /* Westwood Studios decoder */ + off_t ws_frame_header_offset; /* offset of the current frame header */ + int ws_samples_left_in_frame; /* last decoded info */ /* state for G.721 decoder, sort of big but we might as well keep it around */ struct g72x_state g72x_state; /* ADX encryption */ - int adx_channels; uint16_t adx_xor; uint16_t adx_mult; uint16_t adx_add; @@ -147,9 +154,9 @@ typedef struct { /* main vgmstream info */ typedef struct { /* basic config */ - int32_t num_samples; /* the actual max number of samples */ + int channels; /* number of channels for the current stream */ int32_t sample_rate; /* sample rate in Hz */ - int channels; /* number of channels */ + int32_t num_samples; /* the actual max number of samples */ coding_t coding_type; /* type of encoding */ layout_t layout_type; /* type of layout */ meta_t meta_type; /* type of metadata */ @@ -180,13 +187,13 @@ typedef struct { int format_id; /* internal format ID */ /* layout/block state */ - size_t full_block_size; /* actual data size of an entire block (ie. may be fixed, include padding/headers, etc) */ int32_t current_sample; /* sample point within the file (for loop detection) */ int32_t samples_into_block; /* number of samples into the current block/interleave/segment/etc */ off_t current_block_offset; /* start of this block (offset of block header) */ size_t current_block_size; /* size in usable bytes of the block we're in now (used to calculate num_samples per block) */ int32_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */ off_t next_block_offset; /* offset of header of the next block */ + size_t full_block_size; /* actual data size of an entire block (ie. may be fixed, include padding/headers, etc) */ /* loop state (saved when loop is hit to restore later) */ int32_t loop_current_sample; /* saved from current_sample (same as loop_start_sample, but more state-like) */ @@ -228,35 +235,13 @@ typedef struct { play_state_t pstate; /* player state (applied over decoding) */ int loop_count; /* counter of complete loops (1=looped once) */ int loop_target; /* max loops before continuing with the stream end (loops forever if not set) */ + sample_t* tmpbuf; /* garbage buffer used for seeking/trimming */ size_t tmpbuf_size; /* for all channels (samples = tmpbuf_size / channels) */ } VGMSTREAM; -/* for files made of "continuous" segments, one per section of a song (using a complete sub-VGMSTREAM) */ -typedef struct { - int segment_count; - VGMSTREAM** segments; - int current_segment; - sample_t* buffer; - int input_channels; /* internal buffer channels */ - int output_channels; /* resulting channels (after mixing, if applied) */ - int mixed_channels; /* segments have different number of channels */ -} segmented_layout_data; - -/* for files made of "parallel" layers, one per group of channels (using a complete sub-VGMSTREAM) */ -typedef struct { - int layer_count; - VGMSTREAM** layers; - sample_t* buffer; - int input_channels; /* internal buffer channels */ - int output_channels; /* resulting channels (after mixing, if applied) */ - int external_looping; /* don't loop using per-layer loops, but layout's own looping */ - int curr_layer; /* helper */ -} layered_layout_data; - - // VGMStream description in structure format typedef struct { int sample_rate; @@ -304,9 +289,6 @@ void reset_vgmstream(VGMSTREAM* vgmstream); /* close an open vgmstream */ void close_vgmstream(VGMSTREAM* vgmstream); -/* calculate the number of samples to be played based on looping parameters */ -int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM* vgmstream); - /* Decode data into sample buffer. Returns < sample_count on stream end */ int render_vgmstream(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); @@ -321,20 +303,6 @@ void describe_vgmstream_info(VGMSTREAM* vgmstream, vgmstream_info* desc); /* Return the average bitrate in bps of all unique files contained within this stream. */ int get_vgmstream_average_bitrate(VGMSTREAM* vgmstream); -/* List supported formats and return elements in the list, for plugins that need to know. - * The list disables some common formats that may conflict (.wav, .ogg, etc). */ -const char** vgmstream_get_formats(size_t* size); - -/* same, but for common-but-disabled formats in the above list. */ -const char** vgmstream_get_common_formats(size_t* size); - -/* Force enable/disable internal looping. Should be done before playing anything (or after reset), - * and not all codecs support arbitrary loop values ATM. */ -void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sample, int loop_end_sample); - -/* Set number of max loops to do, then play up to stream end (for songs with proper endings) */ -void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target); - /* Return 1 if vgmstream detects from the filename that said file can be used even if doesn't physically exist */ int vgmstream_is_virtual_filename(const char* filename); @@ -358,5 +326,16 @@ void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t ou void get_vgmstream_layout_description(VGMSTREAM* vgmstream, char* out, size_t out_size); void get_vgmstream_meta_description(VGMSTREAM* vgmstream, char* out, size_t out_size); +/* calculate the number of samples to be played based on looping parameters */ +int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM* vgmstream); + void setup_state_vgmstream(VGMSTREAM* vgmstream); + +/* Force enable/disable internal looping. Should be done before playing anything (or after reset), + * and not all codecs support arbitrary loop values ATM. */ +void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sample, int loop_end_sample); + +/* Set number of max loops to do, then play up to stream end (for songs with proper endings) */ +void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target); + #endif diff --git a/xmplay/xmp_vgmstream.c b/xmplay/xmp_vgmstream.c index 2c52066e..94666d7c 100644 --- a/xmplay/xmp_vgmstream.c +++ b/xmplay/xmp_vgmstream.c @@ -237,8 +237,9 @@ void WINAPI xmplay_GetInfoText(char* format, char* length) { rate = vgmstream->sample_rate; samples = vgmstream->num_samples; bps = get_vgmstream_average_bitrate(vgmstream) / 1000; - get_vgmstream_coding_description(vgmstream, fmt, sizeof(fmt)); - if (strcmp(fmt, "FFmpeg") == 0) + + //get_vgmstream_coding_description(vgmstream, fmt, sizeof(fmt)); + //if (strcmp(fmt, "FFmpeg") == 0) { char buffer[1024]; buffer[0] = '\0';