mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-18 07:44:43 +01:00
commit
871671b5b8
@ -82,7 +82,7 @@ void block_update_ea_swvr(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
break;
|
||||
|
||||
default: /* ignore, 0 samples */
|
||||
VGM_LOG("EA SWVR: ignored 0x%08x at 0x%lx\n", block_id, block_offset);
|
||||
//;VGM_LOG("EA SWVR: ignored 0x%08x at 0x%lx\n", block_id, block_offset);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,9 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) {
|
||||
else if (read_32bit(start_offset+0x10, streamFile) == 0x53484452) /* "SHDR" (Future Cop PC) */
|
||||
start_offset += read_32bit(start_offset+0x04, streamFile);
|
||||
|
||||
if (read_32bit(start_offset+0x00, streamFile) == 0x46494C4C) /* "FILL" (Freekstyle) */
|
||||
start_offset += read_32bit(start_offset+0x04, streamFile);
|
||||
|
||||
total_subsongs = 1;
|
||||
block_id = read_32bit(start_offset, streamFile);
|
||||
|
||||
@ -65,7 +68,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) {
|
||||
coding = coding_PSX;
|
||||
if (read_16bit(start_offset+0x1a, streamFile) == 0x0024) {
|
||||
total_subsongs = read_32bit(start_offset+0x0c, streamFile)+1;
|
||||
sample_rate = 24000;
|
||||
sample_rate = 22050;
|
||||
}
|
||||
else {
|
||||
sample_rate = 14008;
|
||||
@ -75,7 +78,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) {
|
||||
case 0x56414742: /* "VAGB" */
|
||||
coding = coding_PSX;
|
||||
if (read_16bit(start_offset+0x1a, streamFile) == 0x6400) {
|
||||
sample_rate = 24000;
|
||||
sample_rate = 22050;
|
||||
}
|
||||
else {
|
||||
sample_rate = 14008;
|
||||
@ -85,13 +88,13 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) {
|
||||
case 0x4453504D: /* "DSPM" */
|
||||
coding = coding_NGC_DSP;
|
||||
total_subsongs = read_32bit(start_offset+0x0c, streamFile)+1;
|
||||
sample_rate = 24000;
|
||||
sample_rate = 22050;
|
||||
channel_count = 2;
|
||||
break;
|
||||
case 0x44535042: /* "DSPB" */
|
||||
coding = coding_NGC_DSP;
|
||||
channel_count = 1;
|
||||
sample_rate = 24000;
|
||||
sample_rate = 22050;
|
||||
break;
|
||||
case 0x4D534943: /* "MSIC" */
|
||||
coding = coding_PCM8_U_int;
|
||||
|
@ -673,6 +673,7 @@ VGMSTREAM * init_vgmstream_opus_std(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_opus_n1(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE * streamFile);
|
||||
|
||||
|
@ -233,3 +233,29 @@ VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE *streamFile) {
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Shin'en variation [Fast RMX (Switch)] */
|
||||
VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE *streamFile) {
|
||||
off_t offset = 0;
|
||||
int num_samples = 0, loop_start = 0, loop_end = 0;
|
||||
|
||||
/* checks */
|
||||
if ( !check_extensions(streamFile,"opus"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x08,streamFile) != 0x01000080)
|
||||
goto fail;
|
||||
|
||||
offset = 0x08;
|
||||
num_samples = 0;
|
||||
loop_start = read_32bitLE(0x00,streamFile);
|
||||
loop_end = read_32bitLE(0x04,streamFile); /* 0 if no loop */
|
||||
|
||||
if (loop_start > loop_end)
|
||||
goto fail; /* just in case */
|
||||
|
||||
return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end);
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,8 @@ typedef struct {
|
||||
|
||||
size_t loop_start_segment;
|
||||
size_t loop_end_segment;
|
||||
|
||||
size_t is_layered;
|
||||
} txtp_header;
|
||||
|
||||
static txtp_header* parse_txtp(STREAMFILE* streamFile);
|
||||
@ -28,7 +30,9 @@ static void clean_txtp(txtp_header* txtp);
|
||||
VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
txtp_header* txtp = NULL;
|
||||
segmented_layout_data *data = NULL;
|
||||
segmented_layout_data *data_s = NULL;
|
||||
layered_layout_data * data_l = NULL;
|
||||
int i;
|
||||
|
||||
|
||||
/* checks */
|
||||
@ -56,16 +60,14 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
|
||||
|
||||
vgmstream->channel_mask = txtp->entry[0].channel_mask;
|
||||
}
|
||||
else {
|
||||
/* multi file */
|
||||
int num_samples, loop_start_sample = 0, loop_end_sample = 0;
|
||||
int i;
|
||||
int loop_flag, channel_count;
|
||||
else if (txtp->is_layered) {
|
||||
/* layered multi file */
|
||||
int channel_count = 0, loop_flag;
|
||||
|
||||
|
||||
/* init layout */
|
||||
data = init_layout_segmented(txtp->entry_count);
|
||||
if (!data) goto fail;
|
||||
data_l = init_layout_layered(txtp->entry_count);
|
||||
if (!data_l) goto fail;
|
||||
|
||||
/* open each segment subfile */
|
||||
for (i = 0; i < txtp->entry_count; i++) {
|
||||
@ -73,15 +75,61 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
|
||||
if (!temp_streamFile) goto fail;
|
||||
temp_streamFile->stream_index = txtp->entry[i].subsong;
|
||||
|
||||
data->segments[i] = init_vgmstream_from_STREAMFILE(temp_streamFile);
|
||||
data_l->layers[i] = init_vgmstream_from_STREAMFILE(temp_streamFile);
|
||||
close_streamfile(temp_streamFile);
|
||||
if (!data->segments[i]) goto fail;
|
||||
if (!data_l->layers[i]) goto fail;
|
||||
|
||||
data->segments[i]->channel_mask = txtp->entry[0].channel_mask;
|
||||
channel_count += data_l->layers[i]->channels;
|
||||
}
|
||||
|
||||
/* setup layered VGMSTREAMs */
|
||||
if (!setup_layout_layered(data_l))
|
||||
goto fail;
|
||||
|
||||
loop_flag = data_l->layers[0]->loop_flag;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = data_l->layers[0]->sample_rate;
|
||||
vgmstream->num_samples = data_l->layers[0]->num_samples;
|
||||
vgmstream->loop_start_sample = data_l->layers[0]->loop_start_sample;
|
||||
vgmstream->loop_end_sample = data_l->layers[0]->loop_end_sample;
|
||||
|
||||
vgmstream->meta_type = meta_TXTP;
|
||||
vgmstream->coding_type = data_l->layers[0]->coding_type;
|
||||
vgmstream->layout_type = layout_layered;
|
||||
|
||||
vgmstream->channel_mask = txtp->entry[0].channel_mask;
|
||||
|
||||
vgmstream->layout_data = data_l;
|
||||
}
|
||||
else {
|
||||
/* segmented multi file */
|
||||
int num_samples, loop_start_sample = 0, loop_end_sample = 0;
|
||||
int loop_flag, channel_count;
|
||||
|
||||
|
||||
/* init layout */
|
||||
data_s = init_layout_segmented(txtp->entry_count);
|
||||
if (!data_s) goto fail;
|
||||
|
||||
/* open each segment subfile */
|
||||
for (i = 0; i < txtp->entry_count; i++) {
|
||||
STREAMFILE* temp_streamFile = open_streamfile_by_filename(streamFile, txtp->entry[i].filename);
|
||||
if (!temp_streamFile) goto fail;
|
||||
temp_streamFile->stream_index = txtp->entry[i].subsong;
|
||||
|
||||
data_s->segments[i] = init_vgmstream_from_STREAMFILE(temp_streamFile);
|
||||
close_streamfile(temp_streamFile);
|
||||
if (!data_s->segments[i]) goto fail;
|
||||
|
||||
data_s->segments[i]->channel_mask = txtp->entry[0].channel_mask;
|
||||
}
|
||||
|
||||
/* setup segmented VGMSTREAMs */
|
||||
if (!setup_layout_segmented(data))
|
||||
if (!setup_layout_segmented(data_s))
|
||||
goto fail;
|
||||
|
||||
/* get looping and samples */
|
||||
@ -89,37 +137,37 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
|
||||
txtp->loop_end_segment = txtp->entry_count;
|
||||
loop_flag = (txtp->loop_start_segment > 0 && txtp->loop_start_segment <= txtp->entry_count);
|
||||
num_samples = 0;
|
||||
for (i = 0; i < data->segment_count; i++) {
|
||||
for (i = 0; i < data_s->segment_count; i++) {
|
||||
|
||||
if (loop_flag && txtp->loop_start_segment == i+1) {
|
||||
loop_start_sample = num_samples;
|
||||
}
|
||||
|
||||
num_samples += data->segments[i]->num_samples;
|
||||
num_samples += data_s->segments[i]->num_samples;
|
||||
|
||||
if (loop_flag && txtp->loop_end_segment == i+1) {
|
||||
loop_end_sample = num_samples;
|
||||
}
|
||||
}
|
||||
|
||||
channel_count = data->segments[0]->channels;
|
||||
channel_count = data_s->segments[0]->channels;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = data->segments[0]->sample_rate;
|
||||
vgmstream->sample_rate = data_s->segments[0]->sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start_sample;
|
||||
vgmstream->loop_end_sample = loop_end_sample;
|
||||
|
||||
vgmstream->meta_type = meta_TXTP;
|
||||
vgmstream->coding_type = data->segments[0]->coding_type;
|
||||
vgmstream->coding_type = data_s->segments[0]->coding_type;
|
||||
vgmstream->layout_type = layout_segmented;
|
||||
|
||||
vgmstream->layout_data = data;
|
||||
vgmstream->layout_data = data_s;
|
||||
if (loop_flag)
|
||||
data->loop_segment = txtp->loop_start_segment-1;
|
||||
data_s->loop_segment = txtp->loop_start_segment-1;
|
||||
}
|
||||
|
||||
|
||||
@ -129,7 +177,8 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
|
||||
fail:
|
||||
clean_txtp(txtp);
|
||||
close_vgmstream(vgmstream);
|
||||
free_layout_segmented(data);
|
||||
free_layout_segmented(data_s);
|
||||
free_layout_layered(data_l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -267,6 +316,14 @@ static int parse_keyval(txtp_header * txtp, const char * key, const char * val)
|
||||
else if (0==strcmp(key,"loop_end_segment")) {
|
||||
if (!parse_num(val, &txtp->loop_end_segment)) goto fail;
|
||||
}
|
||||
if (0==strcmp(key,"mode")) {
|
||||
if (0==strcmp(val,"layers")) {
|
||||
txtp->is_layered = 1;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else {
|
||||
VGM_LOG("TXTP: unknown key=%s, val=%s\n", key,val);
|
||||
goto fail;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
#include <string.h>
|
||||
|
||||
/* most info from XWBtool, xactwb.h, xact2wb.h and xact3wb.h */
|
||||
|
||||
@ -25,7 +26,7 @@ static const int wma_block_align_index[17] = {
|
||||
};
|
||||
|
||||
|
||||
typedef enum { PCM, XBOX_ADPCM, MS_ADPCM, XMA1, XMA2, WMA, XWMA, ATRAC3, OGG } xact_codec;
|
||||
typedef enum { PCM, XBOX_ADPCM, MS_ADPCM, XMA1, XMA2, WMA, XWMA, ATRAC3, OGG, DSP } xact_codec;
|
||||
typedef struct {
|
||||
int little_endian;
|
||||
int version;
|
||||
@ -311,14 +312,19 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
xwb.loop_end = 0;
|
||||
}
|
||||
|
||||
/* Stardew Valley (Switch) format hijack, with full interleaved DSPs (including headers) */
|
||||
if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2
|
||||
&& xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04 && xwb.data_size == 0x55951c1c) {
|
||||
xwb.codec = DSP;
|
||||
}
|
||||
|
||||
/* test loop after the above fixes */
|
||||
xwb.loop_flag = (xwb.loop_end > 0 || xwb.loop_end_sample > xwb.loop_start)
|
||||
&& !(xwb.entry_flags & WAVEBANKENTRY_FLAGS_IGNORELOOP);
|
||||
|
||||
if (xwb.codec != OGG) {
|
||||
/* for Oddworld OGG the data_size value is size of uncompressed bytes instead */
|
||||
/* some BlazBlue Centralfiction songs have padding after data size (maybe wrong rip?) */
|
||||
/* Oddworld OGG the data_size value is size of uncompressed bytes instead; DSP uses some id/config as value */
|
||||
if (xwb.codec != OGG && xwb.codec != DSP) {
|
||||
/* some low-q rips don't remove padding, relax validation a bit */
|
||||
if (xwb.data_offset + xwb.data_size > get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
}
|
||||
@ -462,7 +468,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
break;
|
||||
}
|
||||
|
||||
case XWMA: { /* WMAudio2 (WMA v2): BlazBlue (X360), WMAudio3 (WMA Pro): ? */
|
||||
case XWMA: { /* WMAudio2 (WMA v2): BlazBlue (X360), WMAudio3 (WMA Pro): Bullet Witch (PC) voices */
|
||||
uint8_t buf[100];
|
||||
int bytes, bps_index, block_align, block_index, avg_bps, wma_codec;
|
||||
|
||||
@ -485,7 +491,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
break;
|
||||
}
|
||||
|
||||
case ATRAC3: { /* Techland PS3 extension: Sniper Ghost Warrior (PS3) */
|
||||
case ATRAC3: { /* Techland PS3 extension [Sniper Ghost Warrior (PS3)] */
|
||||
uint8_t buf[200];
|
||||
int bytes;
|
||||
|
||||
@ -503,7 +509,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
break;
|
||||
}
|
||||
|
||||
case OGG: { /* Oddworld: Strangers Wrath iOS/Android extension */
|
||||
case OGG: { /* Oddworld: Strangers Wrath (iOS/Android) extension */
|
||||
vgmstream->codec_data = init_ffmpeg_offset(streamFile, xwb.stream_offset, xwb.stream_size);
|
||||
if ( !vgmstream->codec_data ) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
@ -513,6 +519,17 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
|
||||
#endif
|
||||
|
||||
case DSP: { /* Stardew Valley (Switch) extension */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = xwb.stream_size / xwb.channels;
|
||||
|
||||
dsp_read_coefs(vgmstream,streamFile,xwb.stream_offset + 0x1c,vgmstream->interleave_block_size,!xwb.little_endian);
|
||||
dsp_read_hist (vgmstream,streamFile,xwb.stream_offset + 0x3c,vgmstream->interleave_block_size,!xwb.little_endian);
|
||||
xwb.stream_offset += 0x60; /* skip DSP header */
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
@ -591,7 +608,7 @@ typedef struct {
|
||||
|
||||
|
||||
/* try to find the stream name in a companion XSB file, a comically complex cue format. */
|
||||
static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamXwb) {
|
||||
static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamXwb, char* filename) {
|
||||
STREAMFILE *streamFile = NULL;
|
||||
int i,j, start_sound, cfg__start_sound = 0, cfg__selected_wavebank = 0;
|
||||
int xsb_version;
|
||||
@ -601,7 +618,10 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head
|
||||
xsb_header xsb = {0};
|
||||
|
||||
|
||||
streamFile = open_streamfile_by_ext(streamXwb, "xsb");
|
||||
if (filename)
|
||||
streamFile = open_streamfile_by_filename(streamXwb, filename);
|
||||
else
|
||||
streamFile = open_streamfile_by_ext(streamXwb, "xsb");
|
||||
if (!streamFile) goto fail;
|
||||
|
||||
/* check header */
|
||||
@ -718,8 +738,10 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head
|
||||
} else {
|
||||
suboff = size - 0x08;
|
||||
}
|
||||
//} else if (flag==0x11) { /* Stardew Valley (Switch) */
|
||||
// suboff = size; //???
|
||||
} else {
|
||||
VGM_LOG("XSB: xsb flag 0x%x at offset 0x%08lx not implemented\n", flag, off);
|
||||
VGM_LOG("XSB: xsb flag 0x%x (size=%x) at offset 0x%08lx not implemented\n", flag, size, off);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@ -871,18 +893,34 @@ fail:
|
||||
|
||||
static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile) {
|
||||
int name_found;
|
||||
char xwb_filename[PATH_LIMIT];
|
||||
char xsb_filename[PATH_LIMIT];
|
||||
|
||||
/* try inside this xwb */
|
||||
name_found = get_xwb_name(buf, maxsize, target_subsong, xwb, streamFile);
|
||||
if (name_found) return;
|
||||
|
||||
/* try again in external .xsb */
|
||||
get_xsb_name(buf, maxsize, target_subsong, xwb, streamFile);
|
||||
|
||||
/* try again in external .xsb, using a bunch of possible name pairs */
|
||||
get_streamfile_filename(streamFile,xwb_filename,PATH_LIMIT);
|
||||
|
||||
if (strcmp(xwb_filename,"Wave Bank.xwb")==0) {
|
||||
strcpy(xsb_filename,"Sound Bank.xsb");
|
||||
}
|
||||
else if (strcmp(xwb_filename,"UIMusicBank.xwb")==0) {
|
||||
strcpy(xsb_filename,"UISoundBank.xsb");
|
||||
}
|
||||
else {
|
||||
xsb_filename[0] = '\0';
|
||||
}
|
||||
//todo try others: InGameMusic.xwb + ingamemusic.xsb, NB_BGM_m0100_WB.xwb + NB_BGM_m0100_SB.xsb, etc
|
||||
|
||||
if (xsb_filename[0] != '\0') {
|
||||
name_found = get_xsb_name(buf, maxsize, target_subsong, xwb, streamFile, xsb_filename);
|
||||
if (name_found) return;
|
||||
}
|
||||
|
||||
|
||||
//todo try again with common names (xwb and xsb often are named slightly differently using a common convention):
|
||||
// InGameMusic.xwb + ingamemusic.xsb
|
||||
// UIMusicBank.xwb + UISoundBank.xsb
|
||||
// NB_BGM_m0100_WB.xwb + NB_BGM_m0100_SB.xsb
|
||||
// Wave Bank.xwb + Sound Bank.xsb
|
||||
/* one last time with same name */
|
||||
get_xsb_name(buf, maxsize, target_subsong, xwb, streamFile, NULL);
|
||||
}
|
||||
|
@ -367,6 +367,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_opus_n1,
|
||||
init_vgmstream_opus_capcom,
|
||||
init_vgmstream_opus_nop,
|
||||
init_vgmstream_opus_shinen,
|
||||
init_vgmstream_pc_al2,
|
||||
init_vgmstream_pc_ast,
|
||||
init_vgmstream_naac,
|
||||
|
@ -56,7 +56,7 @@ typedef struct {
|
||||
int loop_forever;
|
||||
int ignore_loop;
|
||||
int disable_subsongs;
|
||||
int downmix;
|
||||
int downmix_channels;
|
||||
} winamp_config;
|
||||
|
||||
winamp_config config;
|
||||
@ -318,7 +318,7 @@ static void cfg_char_to_wchar(TCHAR *wdst, size_t wdstsize, const char *src) {
|
||||
#define DEFAULT_LOOP_FOREVER 0
|
||||
#define DEFAULT_IGNORE_LOOP 0
|
||||
#define DEFAULT_DISABLE_SUBSONGS 0
|
||||
#define DEFAULT_DOWNMIX 0
|
||||
#define DEFAULT_DOWNMIX_CHANNELS 0
|
||||
|
||||
#define INI_ENTRY_FADE_SECONDS TEXT("fade_seconds")
|
||||
#define INI_ENTRY_FADE_DELAY_SECONDS TEXT("fade_delay")
|
||||
@ -327,7 +327,7 @@ static void cfg_char_to_wchar(TCHAR *wdst, size_t wdstsize, const char *src) {
|
||||
#define INI_ENTRY_LOOP_FOREVER TEXT("loop_forever")
|
||||
#define INI_ENTRY_IGNORE_LOOP TEXT("ignore_loop")
|
||||
#define INI_ENTRY_DISABLE_SUBSONGS TEXT("disable_subsongs")
|
||||
#define INI_ENTRY_DOWNMIX TEXT("downmix")
|
||||
#define INI_ENTRY_DOWNMIX_CHANNELS TEXT("downmix_channels")
|
||||
|
||||
TCHAR *priority_strings[] = {
|
||||
TEXT("Idle"),
|
||||
@ -434,12 +434,13 @@ static void load_config() {
|
||||
// config.disable_subsongs = DEFAULT_DISABLE_SUBSONGS;
|
||||
//}
|
||||
|
||||
config.downmix = GetPrivateProfileInt(CONFIG_APP_NAME,INI_ENTRY_DOWNMIX,DEFAULT_DOWNMIX,iniFile);
|
||||
//if (config.downmix < 0) { //unneeded?
|
||||
// sprintf(buf, TEXT("%d"),DEFAULT_DOWNMIX);
|
||||
// WritePrivateProfileString(CONFIG_APP_NAME,INI_ENTRY_DOWNMIX,buf,iniFile);
|
||||
// config.downmix = DEFAULT_DOWNMIX;
|
||||
//}
|
||||
config.downmix_channels = GetPrivateProfileInt(CONFIG_APP_NAME,INI_ENTRY_DOWNMIX_CHANNELS,DEFAULT_DOWNMIX_CHANNELS,iniFile);
|
||||
if (config.downmix_channels < 0) {
|
||||
cfg_sprintf(buf, TEXT("%d"),DEFAULT_DOWNMIX_CHANNELS);
|
||||
WritePrivateProfileString(CONFIG_APP_NAME,INI_ENTRY_DOWNMIX_CHANNELS,buf,iniFile);
|
||||
config.downmix_channels = DEFAULT_DOWNMIX_CHANNELS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* config dialog handler */
|
||||
@ -487,8 +488,8 @@ INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
|
||||
if (config.disable_subsongs)
|
||||
CheckDlgButton(hDlg,IDC_DISABLE_SUBSONGS,BST_CHECKED);
|
||||
|
||||
if (config.downmix)
|
||||
CheckDlgButton(hDlg,IDC_DOWNMIX,BST_CHECKED);
|
||||
cfg_sprintf(buf, TEXT("%d"),config.downmix_channels);
|
||||
SetDlgItemText(hDlg,IDC_DOWNMIX_CHANNELS,buf);
|
||||
|
||||
break;
|
||||
|
||||
@ -499,6 +500,7 @@ INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
|
||||
double temp_fade_seconds;
|
||||
double temp_fade_delay_seconds;
|
||||
double temp_loop_count;
|
||||
int temp_downmix_channels;
|
||||
int consumed, res;
|
||||
|
||||
GetDlgItemText(hDlg,IDC_FADE_SECONDS,buf,buf_size);
|
||||
@ -531,6 +533,17 @@ INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
|
||||
break;
|
||||
}
|
||||
|
||||
GetDlgItemText(hDlg,IDC_DOWNMIX_CHANNELS,buf,buf_size);
|
||||
res = cfg_sscanf(buf, TEXT("%d%n"),&temp_downmix_channels,&consumed);
|
||||
if (res < 1 || consumed != cfg_strlen(buf) || temp_downmix_channels < 0) {
|
||||
MessageBox(hDlg,
|
||||
TEXT("Invalid value for Downmix Channels\n")
|
||||
TEXT("Must be a number greater than or equal to zero"),
|
||||
TEXT("Error"),MB_OK|MB_ICONERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
GetINIFileName(iniFile);
|
||||
|
||||
config.thread_priority = mypri;
|
||||
@ -561,9 +574,13 @@ INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
|
||||
cfg_sprintf(buf, TEXT("%d"),config.disable_subsongs);
|
||||
WritePrivateProfileString(CONFIG_APP_NAME,INI_ENTRY_DISABLE_SUBSONGS,buf,iniFile);
|
||||
|
||||
config.downmix = (IsDlgButtonChecked(hDlg,IDC_DOWNMIX) == BST_CHECKED);
|
||||
cfg_sprintf(buf, TEXT("%d"),config.downmix);
|
||||
WritePrivateProfileString(CONFIG_APP_NAME,INI_ENTRY_DOWNMIX,buf,iniFile);
|
||||
config.loop_count = temp_loop_count;
|
||||
cfg_sprintf(buf, TEXT("%.2lf"),config.loop_count);
|
||||
WritePrivateProfileString(CONFIG_APP_NAME,INI_ENTRY_LOOP_COUNT,buf,iniFile);
|
||||
|
||||
config.downmix_channels = temp_downmix_channels;
|
||||
cfg_sprintf(buf, TEXT("%d"),config.downmix_channels);
|
||||
WritePrivateProfileString(CONFIG_APP_NAME,INI_ENTRY_DOWNMIX_CHANNELS,buf,iniFile);
|
||||
}
|
||||
|
||||
EndDialog(hDlg,TRUE);
|
||||
@ -593,7 +610,7 @@ INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
|
||||
CheckDlgButton(hDlg,IDC_LOOP_NORMALLY,BST_CHECKED);
|
||||
|
||||
CheckDlgButton(hDlg,IDC_DISABLE_SUBSONGS,BST_UNCHECKED);
|
||||
CheckDlgButton(hDlg,IDC_DOWNMIX,BST_UNCHECKED);
|
||||
SetDlgItemText(hDlg,IDC_DOWNMIX_CHANNELS,DEFAULT_DOWNMIX_CHANNELS);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -887,8 +904,8 @@ int winamp_Play(const in_char *fn) {
|
||||
vgmstream->loop_flag = 0;
|
||||
|
||||
output_channels = vgmstream->channels;
|
||||
if (config.downmix)
|
||||
output_channels = vgmstream->channels > 2 ? 2 : vgmstream->channels;
|
||||
if (config.downmix_channels > 0 && config.downmix_channels < vgmstream->channels)
|
||||
output_channels = config.downmix_channels;
|
||||
|
||||
|
||||
/* save original name */
|
||||
@ -1180,19 +1197,28 @@ DWORD WINAPI __stdcall decode(void *arg) {
|
||||
}
|
||||
|
||||
/* downmix enabled (useful when the stream's channels are too much for Winamp's output) */
|
||||
if (config.downmix) {
|
||||
if (config.downmix_channels > 0 && config.downmix_channels < vgmstream->channels) {
|
||||
short temp_buffer[(576*2) * 2];
|
||||
int s, ch;
|
||||
|
||||
/* just copy the first channels for now */
|
||||
for (s = 0; s < samples_to_do; s++) {
|
||||
for (ch = 0; ch < output_channels; ch++) {
|
||||
temp_buffer[s*output_channels + ch] = sample_buffer[s*vgmstream->channels + ch];
|
||||
/* copy channels up to max */
|
||||
for (ch = 0; ch < config.downmix_channels; ch++) {
|
||||
temp_buffer[s*config.downmix_channels + ch] = sample_buffer[s*vgmstream->channels + ch];
|
||||
}
|
||||
/* then mix the rest */
|
||||
for (ch = config.downmix_channels; ch < vgmstream->channels; ch++) {
|
||||
int downmix_ch = ch % config.downmix_channels;
|
||||
int new_sample = ((int)temp_buffer[s*config.downmix_channels + downmix_ch] + (int)sample_buffer[s*vgmstream->channels + ch]);
|
||||
new_sample = (int)(new_sample * 0.7); /* limit clipping without removing too much loudness... hopefully */
|
||||
if (new_sample > 32767) new_sample = 32767;
|
||||
else if (new_sample < -32768) new_sample = -32768;
|
||||
temp_buffer[s*config.downmix_channels + downmix_ch] = (short)new_sample;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy back to global buffer */
|
||||
memcpy(sample_buffer,temp_buffer, samples_to_do*output_channels*sizeof(short));
|
||||
/* copy back to global buffer... in case of multithreading stuff? */
|
||||
memcpy(sample_buffer,temp_buffer, samples_to_do*config.downmix_channels*sizeof(short));
|
||||
}
|
||||
|
||||
/* output samples */
|
||||
|
@ -9,4 +9,4 @@
|
||||
#define IDC_THREAD_PRIORITY_TEXT 1007
|
||||
#define IDC_DEFAULT_BUTTON 1008
|
||||
#define IDC_DISABLE_SUBSONGS 1009
|
||||
#define IDC_DOWNMIX 1010
|
||||
#define IDC_DOWNMIX_CHANNELS 1011
|
||||
|
@ -24,7 +24,8 @@ BEGIN
|
||||
CONTROL "Loop forever",IDC_LOOP_FOREVER,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,7,70,77,10
|
||||
CONTROL "Ignore looping",IDC_IGNORE_LOOP,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,7,83,77,10
|
||||
CONTROL "Disable subsongs",IDC_DISABLE_SUBSONGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,99,87,10
|
||||
CONTROL "Downmix",IDC_DOWNMIX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,112,87,10
|
||||
LTEXT "Downmix",IDC_STATIC,7,115,48,12
|
||||
EDITTEXT IDC_DOWNMIX_CHANNELS,52,112,37,14,ES_AUTOHSCROLL
|
||||
LTEXT "Thread Priority",IDC_STATIC,21,132,46,8
|
||||
CONTROL "Slider1",IDC_THREAD_PRIORITY_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,7,140,77,10
|
||||
CTEXT "DATARIFIC",IDC_THREAD_PRIORITY_TEXT,7,153,77,18
|
||||
|
Loading…
x
Reference in New Issue
Block a user