diff --git a/src/meta/adx_header.c b/src/meta/adx_header.c index 9da93014..9718f6e5 100644 --- a/src/meta/adx_header.c +++ b/src/meta/adx_header.c @@ -448,6 +448,9 @@ static struct { */ {0x5c33,0x4133,0x4ce7}, + // Storm Lover Natsu Koi!! (2011-08-04)(Vridge)(D3 Publisher) + {0x4133,0x5a01,0x5723}, + }; /* type 9 keys */ diff --git a/src/meta/bcstm.c b/src/meta/bcstm.c index e210682b..38a25582 100644 --- a/src/meta/bcstm.c +++ b/src/meta/bcstm.c @@ -7,12 +7,13 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { coding_t coding_type; - off_t head_offset; - off_t seek_offset; + off_t info_offset, seek_offset, data_offset, regn_offset, pdat_offset; + size_t info_size, seek_size, data_size, regn_size, pdat_size; + uint16_t temp_id; int codec_number; int channel_count; int loop_flag; - int ima = 0; + int i, ima = 0; off_t start_offset; /* check extension, case insensitive */ @@ -24,24 +25,45 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { /* check header */ if ((uint32_t)read_32bitBE(0, streamFile) != 0x4353544D) /* "CSTM" */ goto fail; - if ((uint32_t)read_32bitBE(4, streamFile) != 0xFFFE4000) + if ((uint16_t)read_16bitLE(4, streamFile) != 0xFEFF) goto fail; + int section_count = read_16bitLE(0x10, streamFile); + for (i = 0; i < section_count; i++) { + temp_id = read_16bitLE(0x14 + i * 0xc, streamFile); + switch(temp_id) { + case 0x4000: + info_offset = read_32bitLE(0x18 + i * 0xc, streamFile); + info_size = read_32bitLE(0x1c + i * 0xc, streamFile); + break; + case 0x4001: + seek_offset = read_32bitLE(0x18 + i * 0xc, streamFile); + seek_size = read_32bitLE(0x1c + i * 0xc, streamFile); + break; + case 0x4002: + data_offset = read_32bitLE(0x18 + i * 0xc, streamFile); + data_size = read_32bitLE(0x1c + i * 0xc, streamFile); + break; + case 0x4003: + regn_offset = read_32bitLE(0x18 + i * 0xc, streamFile); + regn_size = read_32bitLE(0x1c + i * 0xc, streamFile); + break; + case 0x4004: + pdat_offset = read_32bitLE(0x18 + i * 0xc, streamFile); + pdat_size = read_32bitLE(0x1c + i * 0xc, streamFile); + break; + default: + break; + } + } - - /* get head offset, check */ - head_offset = read_32bitLE(0x18, streamFile); - if ((uint32_t)read_32bitBE(head_offset, streamFile) != 0x494E464F) /* "INFO" */ - goto fail; - - seek_offset = read_32bitLE(0x24, streamFile); /* check type details */ - codec_number = read_8bit(head_offset + 0x20, streamFile); - loop_flag = read_8bit(head_offset + 0x21, streamFile); - channel_count = read_8bit(head_offset + 0x22, streamFile); + codec_number = read_8bit(info_offset + 0x20, streamFile); + loop_flag = read_8bit(info_offset + 0x21, streamFile); + channel_count = read_8bit(info_offset + 0x22, streamFile); switch (codec_number) { case 0: @@ -70,12 +92,12 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { if (!vgmstream) goto fail; /* fill in the vital statistics */ - vgmstream->num_samples = read_32bitLE(head_offset + 0x2c, streamFile); - vgmstream->sample_rate = (uint16_t)read_16bitLE(head_offset + 0x24, streamFile); + vgmstream->num_samples = read_32bitLE(info_offset + 0x2c, streamFile); + vgmstream->sample_rate = (uint16_t)read_16bitLE(info_offset + 0x24, streamFile); /* channels and loop flag are set by allocate_vgmstream */ if (ima) //Shift the loop points back slightly to avoid stupid pops in some IMA streams due to DC offsetting { - vgmstream->loop_start_sample = read_32bitLE(head_offset + 0x28, streamFile); + vgmstream->loop_start_sample = read_32bitLE(info_offset + 0x28, streamFile); if (vgmstream->loop_start_sample > 10000) { vgmstream->loop_start_sample -= 5000; @@ -86,7 +108,7 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { } else { - vgmstream->loop_start_sample = read_32bitLE(head_offset + 0x28, streamFile); + vgmstream->loop_start_sample = read_32bitLE(info_offset + 0x28, streamFile); vgmstream->loop_end_sample = vgmstream->num_samples; } @@ -105,13 +127,13 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { if (ima) vgmstream->interleave_block_size = 0x200; else { - vgmstream->interleave_block_size = read_32bitLE(head_offset + 0x34, streamFile); - vgmstream->interleave_smallblock_size = read_32bitLE(head_offset + 0x44, streamFile); + vgmstream->interleave_block_size = read_32bitLE(info_offset + 0x34, streamFile); + vgmstream->interleave_smallblock_size = read_32bitLE(info_offset + 0x44, streamFile); } if (vgmstream->coding_type == coding_NGC_DSP) { off_t coef_offset; - off_t tempoffset = head_offset; + off_t tempoffset = info_offset; int foundcoef = 0; int i, j; int coef_spacing = 0x2E; @@ -120,7 +142,7 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { { if ((uint32_t)read_32bitLE(tempoffset, streamFile) == 0x00004102) { - coef_offset = read_32bitLE(tempoffset + 4, streamFile) + tempoffset + (channel_count * 8) - 4 - head_offset; + coef_offset = read_32bitLE(tempoffset + 4, streamFile) + tempoffset + (channel_count * 8) - 4 - info_offset; foundcoef++; break; } @@ -129,17 +151,15 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { for (j = 0; jchannels; j++) { for (i = 0; i<16; i++) { - vgmstream->ch[j].adpcm_coef[i] = read_16bitLE(head_offset + coef_offset + j*coef_spacing + i * 2, streamFile); + vgmstream->ch[j].adpcm_coef[i] = read_16bitLE(info_offset + coef_offset + j*coef_spacing + i * 2, streamFile); } } } if (ima) // No SEEK (ADPC) header, so just start where the SEEK header is supposed to be. start_offset = seek_offset; - else if (vgmstream->coding_type == coding_NGC_DSP) - start_offset = read_32bitLE(0x30, streamFile) + 0x20; - else // No SEEK header and not IMA, so just start after the DATA header - start_offset = 0x120; + else + start_offset = data_offset + 0x20; diff --git a/src/meta/bfstm.c b/src/meta/bfstm.c index d3094d4d..f0e2d6dd 100644 --- a/src/meta/bfstm.c +++ b/src/meta/bfstm.c @@ -1,5 +1,6 @@ #include "meta.h" #include "../util.h" +#include "../stack_alloc.h" VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; @@ -7,12 +8,13 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { coding_t coding_type; - off_t head_offset; - off_t seek_offset; - off_t data_offset; + off_t info_offset, seek_offset, data_offset, regn_offset, pdat_offset; + size_t info_size, seek_size, data_size, regn_size, pdat_size; + uint16_t temp_id; int codec_number; int channel_count; int loop_flag; + int i, j; int ima = 0; off_t start_offset; int founddata; @@ -30,33 +32,44 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { if ((uint16_t)read_16bitBE(4, streamFile) != 0xFEFF) goto fail; - founddata = 0; - tempoffset1 = 0x8; - - while (!(founddata)) - { - if ((uint32_t)read_32bitBE(tempoffset1, streamFile) == 0x40020000) - { - data_offset = read_32bitBE(tempoffset1 + 4, streamFile); - founddata++; - break; + int section_count = read_16bitBE(0x10, streamFile); + for (i = 0; i < section_count; i++) { + temp_id = read_16bitBE(0x14 + i * 0xc, streamFile); + switch(temp_id) { + case 0x4000: + info_offset = read_32bitBE(0x18 + i * 0xc, streamFile); + info_size = read_32bitBE(0x1c + i * 0xc, streamFile); + break; + case 0x4001: + seek_offset = read_32bitBE(0x18 + i * 0xc, streamFile); + seek_size = read_32bitBE(0x1c + i * 0xc, streamFile); + break; + case 0x4002: + data_offset = read_32bitBE(0x18 + i * 0xc, streamFile); + data_size = read_32bitBE(0x1c + i * 0xc, streamFile); + break; + case 0x4003: + regn_offset = read_32bitBE(0x18 + i * 0xc, streamFile); + regn_size = read_32bitBE(0x1c + i * 0xc, streamFile); + break; + case 0x4004: + pdat_offset = read_32bitBE(0x18 + i * 0xc, streamFile); + pdat_size = read_32bitBE(0x1c + i * 0xc, streamFile); + break; + default: + break; } - tempoffset1++; } - /* get head offset, check */ - head_offset = read_32bitBE(0x18, streamFile); - if ((uint32_t)read_32bitBE(head_offset, streamFile) != 0x494E464F) /* "INFO" */ + if ((uint32_t)read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */ goto fail; - seek_offset = read_32bitBE(0x24, streamFile); - /* check type details */ - codec_number = read_8bit(head_offset + 0x20, streamFile); - loop_flag = read_8bit(head_offset + 0x21, streamFile); - channel_count = read_8bit(head_offset + 0x22, streamFile); + codec_number = read_8bit(info_offset + 0x20, streamFile); + loop_flag = read_8bit(info_offset + 0x21, streamFile); + channel_count = read_8bit(info_offset + 0x22, streamFile); switch (codec_number) { case 0: @@ -80,12 +93,12 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { if (!vgmstream) goto fail; /* fill in the vital statistics */ - vgmstream->num_samples = read_32bitBE(head_offset + 0x2c, streamFile); - vgmstream->sample_rate = (uint16_t)read_16bitBE(head_offset + 0x26, streamFile); + vgmstream->num_samples = read_32bitBE(info_offset + 0x2c, streamFile); + vgmstream->sample_rate = (uint16_t)read_16bitBE(info_offset + 0x26, streamFile); /* channels and loop flag are set by allocate_vgmstream */ if (ima) //Shift the loop points back slightly to avoid stupid pops in some IMA streams due to DC offsetting { - vgmstream->loop_start_sample = read_32bitBE(head_offset + 0x28, streamFile); + vgmstream->loop_start_sample = read_32bitBE(info_offset + 0x28, streamFile); if (vgmstream->loop_start_sample > 10000) { vgmstream->loop_start_sample -= 5000; @@ -96,7 +109,7 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { } else { - vgmstream->loop_start_sample = read_32bitBE(head_offset + 0x28, streamFile); + vgmstream->loop_start_sample = read_32bitBE(info_offset + 0x28, streamFile); vgmstream->loop_end_sample = vgmstream->num_samples; } @@ -115,41 +128,32 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { if (ima) vgmstream->interleave_block_size = 0x200; else { - vgmstream->interleave_block_size = read_32bitBE(head_offset + 0x34, streamFile); - vgmstream->interleave_smallblock_size = read_32bitBE(head_offset + 0x44, streamFile); + vgmstream->interleave_block_size = read_32bitBE(info_offset + 0x34, streamFile); + vgmstream->interleave_smallblock_size = read_32bitBE(info_offset + 0x44, streamFile); } if (vgmstream->coding_type == coding_NGC_DSP) { - off_t coef_offset; - off_t tempoffset2 = head_offset; - int foundcoef = 0; - int i, j; - int coef_spacing = 0x2E; - - while (!(foundcoef)) - { - if ((uint32_t)read_32bitBE(tempoffset2, streamFile) == 0x41020000) - { - coef_offset = read_32bitBE(tempoffset2 + 4, streamFile) + tempoffset2 + (channel_count * 8) - 4 - head_offset; - foundcoef++; - break; - } - tempoffset2++; - } - + VARDECL(off_t, coef_offset); + ALLOC(coef_offset, channel_count, off_t); + off_t coeff_ptr_table = read_32bitBE(info_offset + 0x1c, streamFile) + info_offset + 8; // Getting pointer for coefficient pointer table + + for (i = 0; i < channel_count; i++) { + tempoffset1 = read_32bitBE(coeff_ptr_table + 8 + i * 8, streamFile); + coef_offset[i] = tempoffset1 + coeff_ptr_table; + coef_offset[i] += read_32bitBE(coef_offset[i] + 4, streamFile); + } + for (j = 0; jchannels; j++) { for (i = 0; i<16; i++) { - vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(head_offset + coef_offset + j*coef_spacing + i * 2, streamFile); + vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_offset[j] + i * 2, streamFile); } } } if (ima) // No SEEK (ADPC) header, so just start where the SEEK header is supposed to be. start_offset = seek_offset; - else if (vgmstream->coding_type == coding_NGC_DSP) + else start_offset = data_offset + 0x20; - else // No SEEK header and not IMA, so just start after the DATA header - start_offset = 0x120; diff --git a/unix/Makefile.unix.am b/unix/Makefile.unix.am index bd31422a..a69e249e 100644 --- a/unix/Makefile.unix.am +++ b/unix/Makefile.unix.am @@ -7,4 +7,4 @@ AM_CXXFLAGS = -Wall -std=c++11 -fpermissive @CXXFLAGS@ -I$(top_builddir) -I$(top AM_LIBS = libvgmstream_la_LDFLAGS = -no-undefined -module -avoid-version -export-symbols-regex get_plugin_info ../src/libvgmstream.la -libvgmstream_la_SOURCES = exts.cc plugin.cc settings.cc vfs.cc +libvgmstream_la_SOURCES = exts.cc plugin.cc vfs.cc diff --git a/unix/exts.cc b/unix/exts.cc index 14ee0266..1ad5e1de 100644 --- a/unix/exts.cc +++ b/unix/exts.cc @@ -1,8 +1,7 @@ #include +#include "plugin.h" -#include "exts.h" - -const char *vgmstream_exts[] = +const char *const VgmStreamPlugin::exts[] = { "2dx9", "2pfs", diff --git a/unix/exts.h b/unix/exts.h deleted file mode 100644 index 2fa30055..00000000 --- a/unix/exts.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __EXTS__ -#define __EXTS__ - -extern const char *vgmstream_exts[]; - -#endif diff --git a/unix/plugin.cc b/unix/plugin.cc index da109a6e..a2d789df 100644 --- a/unix/plugin.cc +++ b/unix/plugin.cc @@ -1,216 +1,235 @@ #include #include -#include -#include -#include +#if DEBUG +#include +#include +#endif extern "C" { #include "../src/vgmstream.h" } -#include "version.h" +#include "plugin.h" #include "vfs.h" -#include "settings.h" +#include + +VgmStreamPlugin aud_plugin_instance; +Settings vgmstream_cfg; VGMSTREAM *vgmstream = NULL; -#define CFG_ID "vgmstream" //ID for storing in audacious +#define CFG_ID "vgmstream" // ID for storing in audacious -// //default-values for Settings -#define DEFAULT_LOOP_FOREVER "1" -#define DEFAULT_FADE_LENGTH "3" -#define DEFAULT_FADE_DELAY "3" -#define DEFAULT_LOOP_COUNT "2" +const char *const VgmStreamPlugin::defaults[] = { + "loop_forever", "1", "loop_count", "2", "fade_length", + "3", "fade_delay", "3", NULL}; -static const char* const defaults[] = -{ - "loop_forever", DEFAULT_LOOP_FOREVER, - "loop_count", DEFAULT_LOOP_COUNT, - "fade_length", DEFAULT_FADE_LENGTH, - "fade_delay", DEFAULT_FADE_DELAY, - NULL +const char VgmStreamPlugin::about[] = + "audacious-vgmstream version: " AUDACIOUSVGMSTREAM_VERSION "\n\n" + "ported to audacious 3.6 by Brandon Whitehead\n" + "adopted from audacious 3 port by Thomas Eppers\n" + "originally written by Todd Jeffreys (http://voidpointer.org/)\n" + "vgmstream written by hcs, FastElbja, manakoAT, and bxaimc " + "(http://www.sf.net/projects/vgmstream)"; + +const PreferencesWidget VgmStreamPlugin::widgets[] = { + WidgetLabel(N_("VGMStream Config")), + WidgetCheck(N_("Loop Forever:"), WidgetBool(vgmstream_cfg.loop_forever)), + WidgetSpin(N_("Loop Count:"), WidgetInt(vgmstream_cfg.loop_count), + {1, 20, 1}), + WidgetSpin(N_("Fade Length:"), WidgetFloat(vgmstream_cfg.fade_length), + {0, 60, 0.1}), + WidgetSpin(N_("Fade Delay:"), WidgetFloat(vgmstream_cfg.fade_delay), + {0, 60, 0.1}), }; -bool vgmstream_init() -{ - debugMessage("init"); - vgmstream_cfg_load(); - debugMessage("after load cfg"); - return true; +void vgmstream_cfg_load() { + debugMessage("cfg_load called"); + aud_config_set_defaults(CFG_ID, VgmStreamPlugin::defaults); + vgmstream_cfg.loop_forever = aud_get_bool(CFG_ID, "loop_forever"); + vgmstream_cfg.loop_count = aud_get_int(CFG_ID, "loop_count"); + vgmstream_cfg.fade_length = aud_get_double(CFG_ID, "fade_length"); + vgmstream_cfg.fade_delay = aud_get_double(CFG_ID, "fade_delay"); } -void vgmstream_cleanup() -{ - debugMessage("cleanup"); - vgmstream_cfg_save(); +void vgmstream_cfg_save() { + debugMessage("cfg_save called"); + aud_set_bool(CFG_ID, "loop_forever", vgmstream_cfg.loop_forever); + aud_set_int(CFG_ID, "loop_count", vgmstream_cfg.loop_count); + aud_set_double(CFG_ID, "fade_length", vgmstream_cfg.fade_length); + aud_set_double(CFG_ID, "fade_delay", vgmstream_cfg.fade_delay); } -void vgmstream_seek(int seek_value, int& current_sample_pos) -{ - debugMessage("seeking"); - // compute from ms to samples - int seek_needed_samples = (long long)seek_value * vgmstream->sample_rate / 1000L; - short buffer[576 * vgmstream->channels]; - int max_buffer_samples = sizeof(buffer) / sizeof(buffer[0]) / vgmstream->channels; +const PluginPreferences VgmStreamPlugin::prefs = { + {widgets}, vgmstream_cfg_load, vgmstream_cfg_save}; - int samples_to_do = 0; - if (seek_needed_samples < current_sample_pos) - { - // go back in time, reopen file - debugMessage("reopen file to seek backward"); - reset_vgmstream(vgmstream); - current_sample_pos = 0; - samples_to_do = seek_needed_samples; - } - else if (current_sample_pos < seek_needed_samples) - { - // go forward in time - samples_to_do = seek_needed_samples - current_sample_pos; - } - - // do the actual seeking - if (samples_to_do >= 0) - { - debugMessage("render forward"); - - //render till seeked sample - while (samples_to_do >0) - { - int seek_samples = std::min(max_buffer_samples, samples_to_do); - current_sample_pos += seek_samples; - samples_to_do -= seek_samples; - render_vgmstream(buffer, seek_samples, vgmstream); - } - debugMessage("after render vgmstream"); - } +bool VgmStreamPlugin::init() { + debugMessage("init"); + vgmstream_cfg_load(); + debugMessage("after load cfg"); + return true; } -bool vgmstream_play(const char * filename, VFSFile * file) -{ - int current_sample_pos = 0; - int rate; - - debugMessage("start play"); - STREAMFILE* streamfile = open_vfs(filename); - - if (!streamfile) - { - printf("failed opening %s\n", filename); - return false; - } - - vgmstream = init_vgmstream_from_STREAMFILE(streamfile); - close_streamfile(streamfile); - - if (!vgmstream || vgmstream->channels <= 0) - { - printf("Error::Channels are zero or couldn't init plugin\n"); - if (vgmstream) - close_vgmstream(vgmstream); - vgmstream = NULL; - return false; - } - - short buffer[576 * vgmstream->channels]; - int max_buffer_samples = sizeof(buffer) / sizeof(buffer[0]) / vgmstream->channels; - - int stream_samples_amount = get_vgmstream_play_samples(vgmstream_cfg.loop_count, vgmstream_cfg.fade_length, vgmstream_cfg.fade_delay, vgmstream); - rate = get_vgmstream_average_bitrate(vgmstream); - - aud_input_set_bitrate(rate); - - if (!aud_input_open_audio(FMT_S16_LE, vgmstream->sample_rate, 2)) - return false; - - int fade_samples = vgmstream_cfg.fade_length * vgmstream->sample_rate; - while (!aud_input_check_stop()) - { - int toget = max_buffer_samples; - - int seek_value = aud_input_check_seek(); - if (seek_value > 0) - vgmstream_seek(seek_value, current_sample_pos); - - // If we haven't configured the plugin to play forever - // or the current song is not loopable. - if (!vgmstream_cfg.loop_forever || !vgmstream->loop_flag) - { - if (current_sample_pos >= stream_samples_amount) - break; - if (current_sample_pos + toget > stream_samples_amount) - toget = stream_samples_amount - current_sample_pos; - } - - render_vgmstream(buffer, toget, vgmstream); - - if (vgmstream->loop_flag && fade_samples > 0 && !vgmstream_cfg.loop_forever) - { - int samples_into_fade = current_sample_pos - (stream_samples_amount - fade_samples); - if (samples_into_fade + toget > 0) - { - for (int j = 0; j < toget; j++, samples_into_fade++) - { - if (samples_into_fade > 0) - { - double fadedness = (double)(fade_samples - samples_into_fade) / fade_samples; - for (int k = 0; k < vgmstream->channels; k++) - buffer[j * vgmstream->channels + k] *= fadedness; - } - } - } - } - - aud_input_write_audio(buffer, toget * sizeof(short) * vgmstream->channels); - current_sample_pos += toget; - } - - debugMessage("finished"); - if (vgmstream) - close_vgmstream(vgmstream); - vgmstream = NULL; - return true; +void VgmStreamPlugin::cleanup() { + debugMessage("cleanup"); + vgmstream_cfg_save(); } // called every time the user adds a new file to playlist -Tuple vgmstream_probe_for_tuple(const char *filename, VFSFile *file) -{ - debugMessage("probe for tuple"); - Tuple tuple; - int ms; - int rate; - VGMSTREAM* vgmstream = NULL; - STREAMFILE* streamfile = NULL; +Tuple VgmStreamPlugin::read_tuple(const char *filename, VFSFile &file) { + debugMessage("probe for tuple"); + Tuple tuple; + int ms; + int rate; + VGMSTREAM *vgmstream = NULL; + STREAMFILE *streamfile = NULL; - streamfile = open_vfs(filename); - vgmstream = init_vgmstream_from_STREAMFILE(streamfile); + streamfile = open_vfs(filename); + vgmstream = init_vgmstream_from_STREAMFILE(streamfile); - tuple.set_filename(filename); - rate = vgmstream->sample_rate * 2 * vgmstream->channels; - tuple.set_int(FIELD_BITRATE, rate); + tuple.set_filename(filename); + rate = vgmstream->sample_rate * 2 * vgmstream->channels; + tuple.set_int(Tuple::Bitrate, rate); - ms = get_vgmstream_play_samples(vgmstream_cfg.loop_count, vgmstream_cfg.fade_length, vgmstream_cfg.fade_delay, vgmstream) * 1000LL / vgmstream->sample_rate; - tuple.set_int(FIELD_LENGTH, ms); + ms = get_vgmstream_play_samples(vgmstream_cfg.loop_count, + vgmstream_cfg.fade_length, + vgmstream_cfg.fade_delay, vgmstream) * + 1000LL / vgmstream->sample_rate; + tuple.set_int(Tuple::Length, ms); - close_streamfile(streamfile); + close_streamfile(streamfile); + close_vgmstream(vgmstream); + + return tuple; +} + +bool VgmStreamPlugin::play(const char *filename, VFSFile &file) { + int current_sample_pos = 0; + int rate; + + debugMessage("start play"); + STREAMFILE *streamfile = open_vfs(filename); + + if (!streamfile) { + printf("failed opening %s\n", filename); + return false; + } + + vgmstream = init_vgmstream_from_STREAMFILE(streamfile); + close_streamfile(streamfile); + + if (!vgmstream || vgmstream->channels <= 0) { + printf("Error::Channels are zero or couldn't init plugin\n"); + if (vgmstream) + close_vgmstream(vgmstream); + vgmstream = NULL; + return false; + } + + short buffer[576 * vgmstream->channels]; + int max_buffer_samples = + sizeof(buffer) / sizeof(buffer[0]) / vgmstream->channels; + + int stream_samples_amount = get_vgmstream_play_samples( + vgmstream_cfg.loop_count, vgmstream_cfg.fade_length, + vgmstream_cfg.fade_delay, vgmstream); + rate = get_vgmstream_average_bitrate(vgmstream); + + set_stream_bitrate(rate); + open_audio(FMT_S16_LE, vgmstream->sample_rate, 2); + + int fade_samples = vgmstream_cfg.fade_length * vgmstream->sample_rate; + while (!check_stop()) { + int toget = max_buffer_samples; + + int seek_value = check_seek(); + if (seek_value > 0) + seek(seek_value, current_sample_pos); + + // If we haven't configured the plugin to play forever + // or the current song is not loopable. + if (!vgmstream_cfg.loop_forever || !vgmstream->loop_flag) { + if (current_sample_pos >= stream_samples_amount) + break; + if (current_sample_pos + toget > stream_samples_amount) + toget = stream_samples_amount - current_sample_pos; + } + + render_vgmstream(buffer, toget, vgmstream); + + if (vgmstream->loop_flag && fade_samples > 0 && + !vgmstream_cfg.loop_forever) { + int samples_into_fade = + current_sample_pos - (stream_samples_amount - fade_samples); + if (samples_into_fade + toget > 0) { + for (int j = 0; j < toget; j++, samples_into_fade++) { + if (samples_into_fade > 0) { + double fadedness = + (double)(fade_samples - samples_into_fade) / fade_samples; + for (int k = 0; k < vgmstream->channels; k++) + buffer[j * vgmstream->channels + k] *= fadedness; + } + } + } + } + + write_audio(buffer, toget * sizeof(short) * vgmstream->channels); + current_sample_pos += toget; + } + + debugMessage("finished"); + if (vgmstream) close_vgmstream(vgmstream); - - return tuple; + vgmstream = NULL; + return true; } -void vgmstream_cfg_load() -{ - debugMessage("cfg_load called"); - aud_config_set_defaults(CFG_ID, defaults); - vgmstream_cfg.loop_forever = aud_get_bool(CFG_ID, "loop_forever"); - vgmstream_cfg.loop_count = aud_get_int(CFG_ID, "loop_count"); - vgmstream_cfg.fade_length = aud_get_double(CFG_ID, "fade_length"); - vgmstream_cfg.fade_delay = aud_get_double(CFG_ID, "fade_delay"); +void VgmStreamPlugin::seek(int seek_value, int ¤t_sample_pos) { + debugMessage("seeking"); + // compute from ms to samples + int seek_needed_samples = + (long long)seek_value * vgmstream->sample_rate / 1000L; + short buffer[576 * vgmstream->channels]; + int max_buffer_samples = + sizeof(buffer) / sizeof(buffer[0]) / vgmstream->channels; + + int samples_to_do = 0; + if (seek_needed_samples < current_sample_pos) { + // go back in time, reopen file + debugMessage("reopen file to seek backward"); + reset_vgmstream(vgmstream); + current_sample_pos = 0; + samples_to_do = seek_needed_samples; + } else if (current_sample_pos < seek_needed_samples) { + // go forward in time + samples_to_do = seek_needed_samples - current_sample_pos; + } + + // do the actual seeking + if (samples_to_do >= 0) { + debugMessage("render forward"); + + // render till seeked sample + while (samples_to_do > 0) { + int seek_samples = std::min(max_buffer_samples, samples_to_do); + current_sample_pos += seek_samples; + samples_to_do -= seek_samples; + render_vgmstream(buffer, seek_samples, vgmstream); + } + debugMessage("after render vgmstream"); + } } -void vgmstream_cfg_save() -{ - debugMessage("cfg_save called"); - aud_set_bool(CFG_ID, "loop_forever", vgmstream_cfg.loop_forever); - aud_set_int(CFG_ID, "loop_count", vgmstream_cfg.loop_count); - aud_set_double(CFG_ID, "fade_length", vgmstream_cfg.fade_length); - aud_set_double(CFG_ID, "fade_delay", vgmstream_cfg.fade_delay); +void debugMessage(const char *str) { +#ifdef DEBUG + timeval curTime; + gettimeofday(&curTime, NULL); + int milli = curTime.tv_usec / 1000; + + char buffer[80]; + strftime(buffer, 80, "%H:%M:%S", localtime(&curTime.tv_sec)); + + char currentTime[84] = ""; + sprintf(currentTime, "%s:%d", buffer, milli); + printf("[%s] Debug: %s\n", currentTime, str); +#endif } diff --git a/unix/plugin.h b/unix/plugin.h new file mode 100644 index 00000000..fad1b595 --- /dev/null +++ b/unix/plugin.h @@ -0,0 +1,51 @@ +#ifndef __PLUGIN__ +#define __PLUGIN__ + +#include +#include +#include +#include +#include + +class VgmStreamPlugin : public InputPlugin { +public: + static const char *const exts[]; + static const char *const defaults[]; + static const char about[]; + static const PreferencesWidget widgets[]; + static const PluginPreferences prefs; + + static constexpr PluginInfo info = { + N_("VGMStream Decoder"), N_("vgmstream"), about, &prefs, + }; + + static constexpr auto iinfo = InputInfo().with_exts(exts); + + constexpr VgmStreamPlugin() : InputPlugin(info, iinfo) {} + + bool init(); + void cleanup(); + bool is_our_file(const char *filename, VFSFile &file) { return false; } + Tuple read_tuple(const char *filename, VFSFile &file); + bool play(const char *filename, VFSFile &file); + +private: + void seek(int seek_value, int ¤t_sample_pos); +}; + +typedef struct { + bool loop_forever; + int loop_count; + double fade_length; + double fade_delay; +} Settings; + +extern Settings vgmstream_cfg; + +void debugMessage(const char *str); +void vgmstream_cfg_load(); +void vgmstream_cfg_save(); + +#define AUDACIOUSVGMSTREAM_VERSION "1.2.0" + +#endif diff --git a/unix/settings.cc b/unix/settings.cc deleted file mode 100644 index 1f9ebe61..00000000 --- a/unix/settings.cc +++ /dev/null @@ -1,75 +0,0 @@ -#include "settings.h" - -#include -#include - -#include -#include -#include - -#include "../src/vgmstream.h" -#include "exts.h" -#include "version.h" -#include "vfs.h" - -void debugMessage(const char *str) -{ -#ifdef DEBUG - timeval curTime; - gettimeofday(&curTime, NULL); - int milli = curTime.tv_usec / 1000; - - char buffer [80]; - strftime(buffer, 80, "%H:%M:%S", localtime(&curTime.tv_sec)); - - char currentTime[84] = ""; - sprintf(currentTime, "%s:%d", buffer, milli); - printf("[%s] Debug: %s\n", currentTime, str); -#endif -} - -Settings vgmstream_cfg; - -bool vgmstream_init(); -void vgmstream_cleanup(); - -Tuple vgmstream_probe_for_tuple(const char *uri, VFSFile *fd); -void vgmstream_play(const char *filename, VFSFile * file); - -const char vgmstream_about[] = -{ - "audacious-vgmstream version: " AUDACIOUSVGMSTREAM_VERSION "\n\n" - "ported to audacious 3.5.1 by Brandon Whitehead\n" - "adopted from audacious 3 port by Thomas Eppers\n" - "originally written by Todd Jeffreys (http://voidpointer.org/)\n" - "vgmstream written by hcs, FastElbja, manakoAT, and bxaimc (http://www.sf.net/projects/vgmstream)" -}; - -static const PreferencesWidget vgmstream_widgets[] = { - WidgetLabel(N_("VGMStream Config")), - WidgetCheck(N_("Loop Forever:"), WidgetBool(vgmstream_cfg.loop_forever)), - WidgetSpin(N_("Loop Count:"), WidgetInt(vgmstream_cfg.loop_count), {1, 20, 1}), - WidgetSpin(N_("Fade Length:"), WidgetFloat(vgmstream_cfg.fade_length), {0, 60, 0.1}), - WidgetSpin(N_("Fade Delay:"), WidgetFloat(vgmstream_cfg.fade_delay), {0, 60, 0.1}), -}; - -static const PluginPreferences vgmstream_prefs = { - {vgmstream_widgets}, - vgmstream_cfg_load, - vgmstream_cfg_save -}; - - -#define AUD_PLUGIN_NAME N_("VGMStream Decoder") -#define AUD_PLUGIN_DOMAIN "vgmstream" -#define AUD_PLUGIN_ABOUT vgmstream_about -#define AUD_PLUGIN_INIT vgmstream_init -#define AUD_PLUGIN_CLEANUP vgmstream_cleanup -#define AUD_PLUGIN_PREFS & vgmstream_prefs -#define AUD_INPUT_IS_OUR_FILE nullptr -#define AUD_INPUT_PLAY vgmstream_play -#define AUD_INPUT_READ_TUPLE vgmstream_probe_for_tuple -#define AUD_INPUT_EXTS vgmstream_exts - -#define AUD_DECLARE_INPUT -#include diff --git a/unix/settings.h b/unix/settings.h deleted file mode 100644 index 3c386cde..00000000 --- a/unix/settings.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __SETTINGS__ -#define __SETTINGS__ - -typedef struct -{ - bool loop_forever; - int loop_count; - double fade_length; - double fade_delay; -} Settings; - -extern Settings vgmstream_cfg; - -void debugMessage(const char *str); -void vgmstream_cfg_load(); -void vgmstream_cfg_save(); - -#endif diff --git a/unix/version.h b/unix/version.h deleted file mode 100644 index e5b22127..00000000 --- a/unix/version.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __VERSION_H__ -#define __VERSION_H__ - -#define AUDACIOUSVGMSTREAM_VERSION "1.1.0" - -#endif diff --git a/unix/vfs.cc b/unix/vfs.cc index fb7cc545..729322ae 100644 --- a/unix/vfs.cc +++ b/unix/vfs.cc @@ -4,12 +4,10 @@ #include #include "../src/vgmstream.h" -#include "version.h" +#include "plugin.h" #include "vfs.h" -#include "settings.h" -typedef struct _VFSSTREAMFILE -{ +typedef struct _VFSSTREAMFILE { STREAMFILE sf; VFSFile *vfsFile; off_t offset; @@ -19,94 +17,89 @@ typedef struct _VFSSTREAMFILE static STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path); -static size_t read_vfs(VFSSTREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) -{ +static size_t read_vfs(VFSSTREAMFILE *streamfile, uint8_t *dest, off_t offset, + size_t length) { size_t sz; // if the offsets don't match, then we need to perform a seek - if (streamfile->offset != offset) - { - vfs_fseek(streamfile->vfsFile, offset, SEEK_SET); + if (streamfile->offset != offset) { + streamfile->vfsFile->fseek(offset, VFS_SEEK_SET); streamfile->offset = offset; } - sz = vfs_fread(dest, 1, length, streamfile->vfsFile); + sz = streamfile->vfsFile->fread(dest, 1, length); // increment our current offset streamfile->offset += sz; return sz; } -static void close_vfs(VFSSTREAMFILE *streamfile) -{ +static void close_vfs(VFSSTREAMFILE *streamfile) { debugMessage("close_vfs"); - vfs_fclose(streamfile->vfsFile); + free(streamfile->vfsFile); free(streamfile); } -static size_t get_size_vfs(VFSSTREAMFILE *streamfile) -{ - return vfs_fsize(streamfile->vfsFile); +static size_t get_size_vfs(VFSSTREAMFILE *streamfile) { + return streamfile->vfsFile->fsize(); } -static size_t get_offset_vfs(VFSSTREAMFILE *streamfile) -{ +static size_t get_offset_vfs(VFSSTREAMFILE *streamfile) { return streamfile->offset; } -static void get_name_vfs(VFSSTREAMFILE *streamfile, char *buffer, size_t length) -{ +static void get_name_vfs(VFSSTREAMFILE *streamfile, char *buffer, + size_t length) { strncpy(buffer, streamfile->name, length); - buffer[length-1] = '\0'; + buffer[length - 1] = '\0'; } -static void get_realname_vfs(VFSSTREAMFILE *streamfile, char *buffer, size_t length) -{ - strncpy(buffer, streamfile->realname, length); - buffer[length-1]='\0'; +static void get_realname_vfs(VFSSTREAMFILE *streamfile, char *buffer, + size_t length) { + strncpy(buffer, streamfile->realname, length); + buffer[length - 1] = '\0'; } -static STREAMFILE *open_vfs_impl(VFSSTREAMFILE *streamfile, const char * const filename, size_t buffersize) -{ +static STREAMFILE *open_vfs_impl(VFSSTREAMFILE *streamfile, + const char *const filename, + size_t buffersize) { if (!filename) return NULL; return open_vfs(filename); } -static STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path) -{ - VFSSTREAMFILE *streamfile = (VFSSTREAMFILE*)malloc(sizeof(VFSSTREAMFILE)); +STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path) { + VFSSTREAMFILE *streamfile = (VFSSTREAMFILE *)malloc(sizeof(VFSSTREAMFILE)); if (!streamfile) return NULL; // success, set our pointers memset(streamfile, 0, sizeof(VFSSTREAMFILE)); - streamfile->sf.read = read_vfs; - streamfile->sf.get_size = get_size_vfs; - streamfile->sf.get_offset = get_offset_vfs; - streamfile->sf.get_name = get_name_vfs; + streamfile->sf.read = read_vfs; + streamfile->sf.get_size = get_size_vfs; + streamfile->sf.get_offset = get_offset_vfs; + streamfile->sf.get_name = get_name_vfs; streamfile->sf.get_realname = get_realname_vfs; - streamfile->sf.open = open_vfs_impl; - streamfile->sf.close = close_vfs; + streamfile->sf.open = open_vfs_impl; + streamfile->sf.close = close_vfs; streamfile->vfsFile = file; - streamfile->offset = 0; + streamfile->offset = 0; strncpy(streamfile->name, path, sizeof(streamfile->name)); - streamfile->name[sizeof(streamfile->name)-1] = '\0'; + streamfile->name[sizeof(streamfile->name) - 1] = '\0'; { - gchar* realname = g_filename_from_uri(path, NULL, NULL); - strncpy(streamfile->realname, realname, sizeof(streamfile->realname)); - streamfile->realname[sizeof(streamfile->realname)-1] = '\0'; - g_free(realname); + gchar *realname = g_filename_from_uri(path, NULL, NULL); + strncpy(streamfile->realname, realname, sizeof(streamfile->realname)); + streamfile->realname[sizeof(streamfile->realname) - 1] = '\0'; + g_free(realname); } return &streamfile->sf; } -STREAMFILE *open_vfs(const char *path) -{ - VFSFile *vfsFile = vfs_fopen(path, "rb"); +STREAMFILE *open_vfs(const char *path) { + VFSFile *vfsFile = new VFSFile(path, "rb"); if (!vfsFile) return NULL;