Add TXTP 'm' channel mapper/swapper

Credit to topher-au for the initial idea/code
This commit is contained in:
bnnm 2018-07-22 00:46:08 +02:00
parent 099a4abc66
commit 57b8c6acd3
3 changed files with 88 additions and 7 deletions

View File

@ -9,6 +9,8 @@ typedef struct {
char filename[TXT_LINE_MAX];
int subsong;
uint32_t channel_mask;
int channel_mappings_on;
int channel_mappings[32];
} txtp_entry;
typedef struct {
@ -59,6 +61,13 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
if (!vgmstream) goto fail;
vgmstream->channel_mask = txtp->entry[0].channel_mask;
vgmstream->channel_mappings_on = txtp->entry[0].channel_mappings_on;
if(vgmstream->channel_mappings_on) {
for (i = 0; i < 32; i++) {
vgmstream->channel_mappings[i] = txtp->entry[0].channel_mappings[i];
}
}
}
else if (txtp->is_layered) {
/* layered multi file */
@ -103,6 +112,13 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
vgmstream->channel_mask = txtp->entry[0].channel_mask;
vgmstream->channel_mappings_on = txtp->entry[0].channel_mappings_on;
if (vgmstream->channel_mappings_on) {
for (i = 0; i < 32; i++) {
vgmstream->channel_mappings[i] = txtp->entry[0].channel_mappings[i];
}
}
vgmstream->layout_data = data_l;
}
else {
@ -186,6 +202,8 @@ fail:
static int add_filename(txtp_header * txtp, char *filename) {
int i;
uint32_t channel_mask = 0;
int channel_mappings_on = 0;
int channel_mappings[32] = {0};
size_t range_start, range_end;
//;VGM_LOG("TXTP: filename=%s\n", filename);
@ -193,7 +211,9 @@ static int add_filename(txtp_header * txtp, char *filename) {
/* parse config:
* - file.ext#2 = play subsong 2
* - file.ext#2~10 = play subsongs in 2 to 10 range
* - file.ext#c1,2 = play channels 1,2 */
* - file.ext#c1,2 = play channels 1,2
* - file.ext#m1-2,3-4 = swaps channels 1<>2 and 3<>4
*/
{
char *config;
@ -213,6 +233,7 @@ static int add_filename(txtp_header * txtp, char *filename) {
config[0] = '\0';
config++;
//todo: alt long words, s or number=subsong
if (config[0] == 'c') {
/* mask channels */
@ -221,7 +242,7 @@ static int add_filename(txtp_header * txtp, char *filename) {
config++;
channel_mask = 0;
while (sscanf(config, "%d%n", &ch,&n) == 1) {
if (ch > 0 && ch < 32)
if (ch > 0 && ch <= 32)
channel_mask |= (1 << (ch-1));
config += n;
@ -231,6 +252,35 @@ static int add_filename(txtp_header * txtp, char *filename) {
break;
};
}
else if (config[0] == 'm') {
/* channel mappings */
int n, ch_from = 0, ch_to = 0;
config++;
channel_mappings_on = 1;
while (config[0] != '\0') {
if (sscanf(config, "%d%n", &ch_from, &n) != 1)
break;
config += n;
if (config[0]== ',' || config[0]== '-')
config++;
else if (config[0] != '\0')
break;
if (sscanf(config, "%d%n", &ch_to, &n) != 1)
break;
config += n;
if (config[0]== ',' || config[0]== '-')
config++;
else if (config[0] != '\0')
break;
if (ch_from > 0 && ch_from <= 32 && ch_to > 0 && ch_to <= 32) {
channel_mappings[ch_from-1] = ch_to-1;
}
}
}
else {
/* subsong range */
int subsong_start = 0, subsong_end = 0;
@ -287,7 +337,16 @@ static int add_filename(txtp_header * txtp, char *filename) {
memset(&txtp->entry[txtp->entry_count],0, sizeof(txtp_entry));
strcpy(txtp->entry[txtp->entry_count].filename, filename);
txtp->entry[txtp->entry_count].channel_mask = channel_mask;
if (channel_mappings_on) {
int ch;
txtp->entry[txtp->entry_count].channel_mappings_on = channel_mappings_on;
for (ch = 0; ch < 32; ch++) {
txtp->entry[txtp->entry_count].channel_mappings[ch] = channel_mappings[ch];
}
}
txtp->entry[txtp->entry_count].subsong = (i+1);
txtp->entry_count++;
}

View File

@ -959,6 +959,26 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
}
/* swap channels if set, to create custom channel mappings */
if (vgmstream->channel_mappings_on) {
int ch_from,ch_to,s;
sample temp;
for (s = 0; s < sample_count; s++) {
for (ch_from = 0; ch_from < vgmstream->channels; ch_from++) {
if (ch_from > 32)
continue;
ch_to = vgmstream->channel_mappings[ch_from];
if (ch_to < 1 || ch_to > 32 || ch_to > vgmstream->channels-1 || ch_from == ch_to)
continue;
temp = buffer[s*vgmstream->channels + ch_from];
buffer[s*vgmstream->channels + ch_from] = buffer[s*vgmstream->channels + ch_to];
buffer[s*vgmstream->channels + ch_to] = temp;
}
}
}
/* channel bitmask to silence non-set channels (up to 32)
* can be used for 'crossfading subsongs' or layered channels, where a set of channels make a song section */
if (vgmstream->channel_mask) {

View File

@ -747,12 +747,14 @@ typedef struct {
layout_t layout_type; /* type of layout for data */
meta_t meta_type; /* how we know the metadata */
/* subsongs */
int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */
int stream_index; /* selected stream (also 1-based) */
/* subsongs and internal config */
int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */
int stream_index; /* selected stream (also 1-based) */
char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */
size_t stream_size; /* info to properly calculate bitrate */
uint32_t channel_mask; /* to silence crossfading subsongs/layers */
size_t stream_size; /* info to properly calculate bitrate */
uint32_t channel_mask; /* to silence crossfading subsongs/layers */
int channel_mappings_on; /* channel mappings are active */
int channel_mappings[32]; /* swap channel "i" with "[i]" */
/* looping */
int loop_flag; /* is this stream looped? */