Mercurial > audlegacy-plugins
changeset 749:26ff35aa9b2b trunk
[svn] - vtx input plugin based on a submission from Pavel Vymetalek.
author | nenolod |
---|---|
date | Wed, 28 Feb 2007 04:38:53 -0800 |
parents | 559c68ce2e3d |
children | 5d8556c9949c |
files | ChangeLog configure.ac src/vtx/Makefile src/vtx/about.c src/vtx/ay8912.c src/vtx/ayemu.h src/vtx/ayemu_8912.h src/vtx/ayemu_vtxfile.h src/vtx/config.c src/vtx/info.c src/vtx/lh5dec.c src/vtx/vtx.c src/vtx/vtx.h src/vtx/vtxfile.c |
diffstat | 14 files changed, 1910 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Wed Feb 28 02:14:00 2007 -0800 +++ b/ChangeLog Wed Feb 28 04:38:53 2007 -0800 @@ -1,3 +1,13 @@ +2007-02-28 10:14:00 +0000 William Pitcock <nenolod@sacredspiral.co.uk> + revision [1594] + - using mad.pc makes us dependant on a nonstandard packaging of mad -- + which causes breakage on some operating systems + + trunk/configure.ac | 5 +++-- + trunk/src/madplug/Makefile | 4 ++-- + 2 files changed, 5 insertions(+), 4 deletions(-) + + 2007-02-28 07:32:10 +0000 William Pitcock <nenolod@sacredspiral.co.uk> revision [1592] - add czech translation file. Closes #818.
--- a/configure.ac Wed Feb 28 02:14:00 2007 -0800 +++ b/configure.ac Wed Feb 28 04:38:53 2007 -0800 @@ -93,7 +93,7 @@ dnl These plugins are always built. -INPUT_PLUGINS="tonegen console sexypsf wav cue alac metronom" +INPUT_PLUGINS="tonegen console sexypsf wav cue alac metronom vtx" OUTPUT_PLUGINS="disk_writer null" EFFECT_PLUGINS="audiocompress ladspa voice_removal stereo_plugin echo_plugin" GENERAL_PLUGINS="song_change alarm"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vtx/Makefile Wed Feb 28 04:38:53 2007 -0800 @@ -0,0 +1,16 @@ +include ../../mk/rules.mk +include ../../mk/init.mk + +OBJECTIVE_LIBS = libvtx$(SHARED_SUFFIX) + +LIBDIR = $(plugindir)/$(INPUT_PLUGIN_DIR) + +SOURCES = lh5dec.c ay8912.c about.c config.c info.c vtx.c vtxfile.c + +OBJECTS = ${SOURCES:.c=.o} + +LIBADD = $(VTX_LIBS) + +CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) -I. -I../../intl -I../.. -I../.. + +include ../../mk/objective.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vtx/about.c Wed Feb 28 04:38:53 2007 -0800 @@ -0,0 +1,35 @@ +#include <audacious/plugin.h> +#include <audacious/util.h> +#include <audacious/plugin.h> + +#include <audacious/output.h> +#include <audacious/util.h> +#include <audacious/titlestring.h> +#include <audacious/vfs.h> +#include <audacious/strings.h> +#include <audacious/i18n.h> + +#include <gtk/gtk.h> + +#include "vtx.h" +#include "ayemu.h" + +void +vtx_about (void) +{ + static GtkWidget *box; + if (box) gdk_window_raise(box->window); + else { + box = xmms_show_message (_("About Vortex Player"), + _ + ("Vortex file format player by Sashnov Alexander <sashnov@ngs.ru>\n" + "Founded on original source in_vtx.dll by Roman Sherbakov <v_soft@microfor.ru>\n" + "\n" + "Music in vtx format can be found at http://vtx.microfor.ru/music.htm\n" + "and other AY/YM music sites.\n" + "\n" + "Audacious implementation by Pavel Vymetalek <pvymetalek@seznam.cz>"), + _("Ok"), FALSE, NULL, NULL); + g_signal_connect (G_OBJECT (box), "destroy", G_CALLBACK(gtk_widget_destroyed), &box); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vtx/ay8912.c Wed Feb 28 04:38:53 2007 -0800 @@ -0,0 +1,494 @@ +/* AY/YM emulator implementation. */ + +#include "ayemu.h" + +#define debuglog stderr; + +char *ayemu_err; + +static const char VERSION[] = "libayemu 0.9"; + +const int MAGIC1 = 0xcdef; /* for check ayemu_t structure inited */ + +enum { +/* Max amplitude value for stereo signal for avoiding for possible + folowwing SSRC for clipping */ + AYEMU_MAX_AMP = 24575, + AYEMU_DEFAULT_CHIP_FREQ = 1773400 +}; + +/* sound chip volume envelops (will calculated by gen_env()) */ +static int bEnvGenInit = 0; +static int Envelope [16][128]; + + +/* AY volume table (c) by V_Soft and Lion 17 */ +static int Lion17_AY_table [16] = + { 0, 513, 828, 1239, 1923, 3238, 4926, 9110, + 10344, 17876, 24682, 30442, 38844, 47270, 56402, 65535}; + +/* YM volume table (c) by V_Soft and Lion 17 */ +static int Lion17_YM_table [32] = + { 0, 0, 190, 286, 375, 470, 560, 664, + 866, 1130, 1515, 1803, 2253, 2848, 3351, 3862, + 4844, 6058, 7290, 8559, 10474, 12878, 15297, 17787, + 21500, 26172, 30866, 35676, 42664, 50986, 58842, 65535}; + +/* AY volume table (c) by Hacker KAY */ +static int KAY_AY_table [16] = + { 0, 836, 1212, 1773, 2619, 3875, 5397, 8823, + 10392, 16706, 23339, 29292, 36969, 46421, 55195, 65535}; + +/* YM volume table (c) by Hacker KAY */ +static int KAY_YM_table [32] = + { 0, 0, 248, 450, 670, 826, 1010, 1239, + 1552, 1919, 2314, 2626, 3131, 3778, 4407, 5031, + 5968, 7161, 8415, 9622, 11421, 13689, 15957, 18280, + 21759, 26148, 30523, 34879, 41434, 49404, 57492, 65535}; + +/* default equlaizer (layout) settings for AY and YM, 7 stereo types */ +static const int default_layout [2][7][6] = { + { + /* A_l, A_r, B_l, B_r, C_l, C_r */ + + /* for AY */ + {100, 100, 100, 100, 100, 100}, // _MONO + {100, 33, 70, 70, 33, 100}, // _ABC + {100, 33, 33, 100, 70, 70}, // _ACB + {70, 70, 100, 33, 33, 100}, // _BAC + {33, 100, 100, 33, 70, 70}, // _BCA + {70, 70, 33, 100, 100, 33}, // _CAB + {33, 100, 70, 70, 100, 33}}, // _CBA + { + /* for YM */ + {100, 100, 100, 100, 100, 100}, // _MONO + {100, 5, 70, 70, 5, 100}, // _ABC + {100, 5, 5, 100, 70, 70}, // _ACB + {70, 70, 100, 5, 5, 100}, // _BAC + {5, 100, 100, 5, 70, 70}, // _BCA + {70, 70, 5, 100, 100, 5}, // _CAB + {5, 100, 70, 70, 100, 5}} // _CBA +}; + + +static int check_magic(ayemu_ay_t *ay) +{ + if (ay->magic == MAGIC1) + return 1; + fprintf(stderr, "libayemu: passed pointer %p to uninitialized ayemu_ay_t structure\n", ay); + return 0; +} + + +/* make chip hardware envelop tables. + Will execute once before first use. */ +static void gen_env() +{ + int env; + int pos; + int hold; + int dir; + int vol; + + for (env = 0; env < 16; env++) { + hold = 0; + dir = (env & 4)? 1 : -1; + vol = (env & 4)? -1 : 32; + for (pos = 0; pos < 128; pos++) { + if (!hold) { + vol += dir; + if (vol < 0 || vol >= 32) { + if ( env & 8 ) { + if ( env & 2 ) dir = -dir; + vol = (dir > 0 )? 0:31; + if ( env & 1 ) { + hold = 1; + vol = ( dir > 0 )? 31:0; + } + } else { + vol = 0; + hold = 1; + } + } + } + Envelope[env][pos] = vol; + } + } + bEnvGenInit = 1; +} + + +/** + * \retval ayemu_init none. + * +*/ +void ayemu_init(ayemu_ay_t *ay) +{ + ay->default_chip_flag = 1; + ay->ChipFreq = AYEMU_DEFAULT_CHIP_FREQ; + ay->default_stereo_flag = 1; + ay->default_sound_format_flag = 1; + ay->dirty = 1; + ay->magic = MAGIC1; + + ayemu_reset(ay); +} + +/** Reset AY/YM chip. + * + * \arg \c ay - pointer to ayemu_ay_t structure. + * \return none. + */ +void ayemu_reset(ayemu_ay_t *ay) +{ + if (!check_magic(ay)) return; + + ay->cnt_a = ay->cnt_b = ay->cnt_c = ay->cnt_n = ay->cnt_e = 0; + ay->bit_a = ay->bit_b = ay->bit_c = ay->bit_n = 0; + ay->env_pos = ay->EnvNum = 0; + ay->Cur_Seed = 0xffff; +} + + +static void set_table_ay (ayemu_ay_t *ay, int tbl[16]) +{ + int n; + for (n = 0; n < 32; n++) + ay->table[n] = tbl[n/2]; + ay->type = AYEMU_AY; +} + +static void set_table_ym (ayemu_ay_t *ay, int tbl[32]) +{ + int n; + for (n = 0; n < 32; n++) + ay->table[n] = tbl[n]; + ay->type = AYEMU_YM; +} + + +/** Set chip type. */ +int ayemu_set_chip_type(ayemu_ay_t *ay, ayemu_chip_t type, int *custom_table) +{ +if (!check_magic(ay)) + return 0; + + if (!(type == AYEMU_AY_CUSTOM || type == AYEMU_YM_CUSTOM) && custom_table != NULL) { + ayemu_err = "For non-custom chip type 'custom_table' param must be NULL"; + return 0; + } + + switch(type) { + case AYEMU_AY: + case AYEMU_AY_LION17: + set_table_ay(ay, Lion17_AY_table); + break; + case AYEMU_YM: + case AYEMU_YM_LION17: + set_table_ym(ay, Lion17_YM_table); + break; + case AYEMU_AY_KAY: + set_table_ay(ay, KAY_AY_table); + break; + case AYEMU_YM_KAY: + set_table_ym(ay, KAY_YM_table); + break; + case AYEMU_AY_CUSTOM: + set_table_ay(ay, custom_table); + break; + case AYEMU_YM_CUSTOM: + set_table_ym(ay, custom_table); + break; + default: + ayemu_err = "Incorrect chip type"; + return 0; + } + + ay->default_chip_flag = 0; + ay->dirty = 1; + return 1; +} + + +/** Set chip frequency. */ +void ayemu_set_chip_freq(ayemu_ay_t *ay, int chipfreq) +{ + if (!check_magic(ay)) return; + + ay->ChipFreq = chipfreq; + ay->dirty = 1; +} + +/*! Set output sound format + * \arg \c ay - pointer to ayemu_t structure + * \arg \c freq - sound freq (44100 for example) + * \arg \c chans - number of channels (1-mono, 2-stereo) + * \arg \c bits - now supported only 16 and 8. + * \retval \b 1 on success, \b 0 if error occure + */ +int ayemu_set_sound_format (ayemu_ay_t *ay, int freq, int chans, int bits) +{ + if (!check_magic(ay)) + return 0; + + if (!(bits == 16 || bits == 8)) { + ayemu_err = "Incorrect bits value"; + return 0; + } + else if (!(chans == 1 || chans == 2)) { + ayemu_err = "Incorrect number of channels"; + return 0; + } + else if (freq < 50) { + ayemu_err = "Incorrect output sound freq"; + return 0; + } + else { + ay->sndfmt.freq = freq; + ay->sndfmt.channels = chans; + ay->sndfmt.bpc = bits; + } + + ay->default_sound_format_flag = 0; + ay->dirty = 1; + return 1; +} + + +/*! Set amplitude factor for each of channels (A,B anc C, tone and noise). + * Factor's value must be from (-100) to 100. + * \arg ay - pointer to ayemu_t structure + * \arg stereo_type - type of stereo + * \arg custom_eq - NULL or pointer to custom table of mixer layout. + * \retval 1 if OK, 0 if error occures. + */ +int ayemu_set_stereo(ayemu_ay_t *ay, ayemu_stereo_t stereo_type, int *custom_eq) +{ + int i; + int chip; + + if (!check_magic(ay)) + return 0; + + if (stereo_type != AYEMU_STEREO_CUSTOM && custom_eq != NULL) { + ayemu_err = "Stereo type not custom, 'custom_eq' parametr must be NULL"; + return 0; + } + + chip = (ay->type == AYEMU_AY)? 0 : 1; + + switch(stereo_type) { + case AYEMU_MONO: + case AYEMU_ABC: + case AYEMU_ACB: + case AYEMU_BAC: + case AYEMU_BCA: + case AYEMU_CAB: + case AYEMU_CBA: + for (i = 0 ; i < 6 ; i++) + ay->eq[i] = default_layout[chip][stereo_type][i]; + break; + case AYEMU_STEREO_CUSTOM: + for (i = 0 ; i < 6 ; i++) + ay->eq[i] = custom_eq[i]; + break; + default: + ayemu_err = "Incorrect stereo type"; + return 0; + } + + ay->default_stereo_flag = 0; + ay->dirty = 1; + return 1; +} + + +#define WARN_IF_REGISTER_GREAT_THAN(r,m) \ +if (*(regs + r) > m) \ + fprintf(stderr, "ayemu_set_regs: warning: possible bad register data- R%d > %d\n", r, m) + + +/** Assign values for AY registers. + * + * You must pass array of char [14] to this function + */ +void ayemu_set_regs(ayemu_ay_t *ay, unsigned char *regs) +{ + if (!check_magic(ay)) return; + + WARN_IF_REGISTER_GREAT_THAN(1,15); + WARN_IF_REGISTER_GREAT_THAN(3,15); + WARN_IF_REGISTER_GREAT_THAN(5,15); + WARN_IF_REGISTER_GREAT_THAN(8,31); + WARN_IF_REGISTER_GREAT_THAN(9,31); + WARN_IF_REGISTER_GREAT_THAN(10,31); + + ay->regs.tone_a = regs[0] + ((regs[1]&0x0f) << 8); + ay->regs.tone_b = regs[2] + ((regs[3]&0x0f) << 8); + ay->regs.tone_c = regs[4] + ((regs[5]&0x0f) << 8); + + ay->regs.noise = regs[6] & 0x1f; + + ay->regs.R7_tone_a = ! (regs[7] & 0x01); + ay->regs.R7_tone_b = ! (regs[7] & 0x02); + ay->regs.R7_tone_c = ! (regs[7] & 0x04); + + ay->regs.R7_noise_a = ! (regs[7] & 0x08); + ay->regs.R7_noise_b = ! (regs[7] & 0x10); + ay->regs.R7_noise_c = ! (regs[7] & 0x20); + + ay->regs.vol_a = regs[8] & 0x0f; + ay->regs.vol_b = regs[9] & 0x0f; + ay->regs.vol_c = regs[10] & 0x0f; + ay->regs.env_a = regs[8] & 0x10; + ay->regs.env_b = regs[9] & 0x10; + ay->regs.env_c = regs[10] & 0x10; + ay->regs.env_freq = regs[11] + (regs[12] << 8); + + if (regs[13] != 0xff) { /* R13 = 255 means continue curent envelop */ + ay->regs.env_style = regs[13] & 0x0f; + ay->env_pos = ay->cnt_e = 0; + } +} + + +static void prepare_generation(ayemu_ay_t *ay) +{ + int vol, max_l, max_r; + + if (!ay->dirty) return; + + if (!bEnvGenInit) gen_env (); + + if (ay->default_chip_flag) ayemu_set_chip_type(ay, AYEMU_AY, NULL); + + if (ay->default_stereo_flag) ayemu_set_stereo(ay, AYEMU_ABC, NULL); + + if (ay->default_sound_format_flag) ayemu_set_sound_format(ay, 44100, 2, 16); + + ay->ChipTacts_per_outcount = ay->ChipFreq / ay->sndfmt.freq / 8; + + { /* GenVols */ + int n, m; + int vol; + for (n = 0; n < 32; n++) { + vol = ay->table[n]; + for (m=0; m < 6; m++) + ay->vols[m][n] = (int) (((double) vol * ay->eq[m]) / 100); + } + } + + /* динамическая настройка глобального коэффициента усиления + подразумевается, что в vols [x][31] лежит самая большая громкость + TODO: Сделать проверку на это ;-) + */ + max_l = ay->vols[0][31] + ay->vols[2][31] + ay->vols[3][31]; + max_r = ay->vols[1][31] + ay->vols[3][31] + ay->vols[5][31]; + vol = (max_l > max_r) ? max_l : max_r; // =157283 on all defaults + ay->Amp_Global = ay->ChipTacts_per_outcount *vol / AYEMU_MAX_AMP; + + ay->dirty = 0; +} + + +/*! Generate sound. + * Fill sound buffer with current register data + * Return value: pointer to next data in output sound buffer + * \retval \b 1 if OK, \b 0 if error occures. + */ +void *ayemu_gen_sound(ayemu_ay_t *ay, void *buff, size_t sound_bufsize) +{ + int mix_l, mix_r; + int tmpvol; + int m; + int snd_numcount; + unsigned char *sound_buf = buff; + + if (!check_magic(ay)) + return 0; + + prepare_generation(ay); + + snd_numcount = sound_bufsize / (ay->sndfmt.channels * (ay->sndfmt.bpc >> 3)); + while (snd_numcount-- > 0) { + mix_l = mix_r = 0; + + for (m = 0 ; m < ay->ChipTacts_per_outcount ; m++) { + if (++ay->cnt_a >= ay->regs.tone_a) { + ay->cnt_a = 0; + ay->bit_a = ! ay->bit_a; + } + if (++ay->cnt_b >= ay->regs.tone_b) { + ay->cnt_b = 0; + ay->bit_b = ! ay->bit_b; + } + if (++ay->cnt_c >= ay->regs.tone_c) { + ay->cnt_c = 0; + ay->bit_c = ! ay->bit_c; + } + + /* GenNoise (c) Hacker KAY & Sergey Bulba */ + if (++ay->cnt_n >= (ay->regs.noise * 2)) { + ay->cnt_n = 0; + ay->Cur_Seed = (ay->Cur_Seed * 2 + 1) ^ \ + (((ay->Cur_Seed >> 16) ^ (ay->Cur_Seed >> 13)) & 1); + ay->bit_n = ((ay->Cur_Seed >> 16) & 1); + } + + if (++ay->cnt_e >= ay->regs.env_freq) { + ay->cnt_e = 0; + if (++ay->env_pos > 127) + ay->env_pos = 64; + } + +#define ENVVOL Envelope [ay->regs.env_style][ay->env_pos] + + if ((ay->bit_a | !ay->regs.R7_tone_a) & (ay->bit_n | !ay->regs.R7_noise_a)) { + tmpvol = (ay->regs.env_a)? ENVVOL : ay->regs.vol_a * 2 + 1; + mix_l += ay->vols[0][tmpvol]; + mix_r += ay->vols[1][tmpvol]; + } + + if ((ay->bit_b | !ay->regs.R7_tone_b) & (ay->bit_n | !ay->regs.R7_noise_b)) { + tmpvol =(ay->regs.env_b)? ENVVOL : ay->regs.vol_b * 2 + 1; + mix_l += ay->vols[2][tmpvol]; + mix_r += ay->vols[3][tmpvol]; + } + + if ((ay->bit_c | !ay->regs.R7_tone_c) & (ay->bit_n | !ay->regs.R7_noise_c)) { + tmpvol = (ay->regs.env_c)? ENVVOL : ay->regs.vol_c * 2 + 1; + mix_l += ay->vols[4][tmpvol]; + mix_r += ay->vols[5][tmpvol]; + } + } /* end for (m=0; ...) */ + + mix_l /= ay->Amp_Global; + mix_r /= ay->Amp_Global; + + if (ay->sndfmt.bpc == 8) { + mix_l = (mix_l >> 8) | 128; /* 8 bit sound */ + mix_r = (mix_r >> 8) | 128; + *sound_buf++ = mix_l; + if (ay->sndfmt.channels != 1) + *sound_buf++ = mix_r; + } else { + *sound_buf++ = mix_l & 0x00FF; /* 16 bit sound */ + *sound_buf++ = (mix_l >> 8); + if (ay->sndfmt.channels != 1) { + *sound_buf++ = mix_r & 0x00FF; + *sound_buf++ = (mix_r >> 8); + } + } + } + return sound_buf; +} + +/** Free all data allocated by emulator + * + * For now it do nothing. + */ +void ayemu_free (ayemu_ay_t *ay) +{ + /* nothing to do here */ + return; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vtx/ayemu.h Wed Feb 28 04:38:53 2007 -0800 @@ -0,0 +1,77 @@ +/* + ayemu - AY/YM sound chip emulator and music file loader + Copyright (C) 2003-2004 Sashnov Alexander + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Alexander Sashnov + sashnov@ngs.ru +*/ +#ifndef _AYEMU_H +#define _AYEMU_H + +#ifdef __cplusplus +# define BEGIN_C_DECLS extern "C" { +# define END_C_DECLS } +#else /* !__cplusplus */ +# define BEGIN_C_DECLS +# define END_C_DECLS +#endif /* __cplusplus */ + +/* Make sure the correct platform symbols are defined */ +#if !defined(WIN32) && defined(_WIN32) +#define WIN32 +#endif /* Windows */ + +/* Some compilers use a special export keyword */ +#ifndef DECLSPEC +# ifdef __BEOS__ +# if defined(__GNUC__) +# define DECLSPEC __declspec(dllexport) +# else +# define DECLSPEC __declspec(export) +# endif +# else +# ifdef WIN32 +# ifdef __BORLANDC__ +# ifdef BUILD_SDL +# define DECLSPEC +# else +# define DECLSPEC __declspec(dllimport) +# endif +# else +# define DECLSPEC __declspec(dllexport) +# endif +# else +# define DECLSPEC +# endif +# endif +#endif + +#define EXTERN extern DECLSPEC + +/* typedefs for 32-bit architecture */ +typedef unsigned char Uint8; +typedef signed char Sint8; +typedef unsigned short Uint16; +typedef signed short Sint16; +typedef unsigned int Uint32; +typedef signed int Sint32; + +/* include other library headers */ +#include "ayemu_8912.h" +#include "ayemu_vtxfile.h" + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vtx/ayemu_8912.h Wed Feb 28 04:38:53 2007 -0800 @@ -0,0 +1,150 @@ +/** + * AY/YM emulator include file + */ + +#ifndef _AYEMU_ay8912_h +#define _AYEMU_ay8912_h + +#include <stddef.h> + +BEGIN_C_DECLS + +/** Types of stereo. + The codes of stereo types used for generage sound. */ +typedef enum +{ + AYEMU_MONO = 0, + AYEMU_ABC, + AYEMU_ACB, + AYEMU_BAC, + AYEMU_BCA, + AYEMU_CAB, + AYEMU_CBA, + AYEMU_STEREO_CUSTOM = 255 +} ayemu_stereo_t; + +/** Sound chip type. + Constant for identify used chip for emulation */ +typedef enum { + AYEMU_AY, /**< default AY chip (lion17 for now) */ + AYEMU_YM, /**< default YM chip (lion17 for now) */ + AYEMU_AY_LION17, /**< emulate AY with Lion17 table */ + AYEMU_YM_LION17, /**< emulate YM with Lion17 table */ + AYEMU_AY_KAY, /**< emulate AY with HACKER KAY table */ + AYEMU_YM_KAY, /**< emulate YM with HACKER KAY table */ + AYEMU_AY_LOG, /**< emulate AY with logariphmic table */ + AYEMU_YM_LOG, /**< emulate YM with logariphmic table */ + AYEMU_AY_CUSTOM, /**< use AY with custom table. */ + AYEMU_YM_CUSTOM /**< use YM with custom table. */ +} ayemu_chip_t; + +/** parsed by #ayemu_set_regs() AY registers data \internal */ +typedef struct +{ + int tone_a; /**< R0, R1 */ + int tone_b; /**< R2, R3 */ + int tone_c; /**< R4, R5 */ + int noise; /**< R6 */ + int R7_tone_a; /**< R7 bit 0 */ + int R7_tone_b; /**< R7 bit 1 */ + int R7_tone_c; /**< R7 bit 2 */ + int R7_noise_a; /**< R7 bit 3 */ + int R7_noise_b; /**< R7 bit 4 */ + int R7_noise_c; /**< R7 bit 5 */ + int vol_a; /**< R8 bits 3-0 */ + int vol_b; /**< R9 bits 3-0 */ + int vol_c; /**< R10 bits 3-0 */ + int env_a; /**< R8 bit 4 */ + int env_b; /**< R9 bit 4 */ + int env_c; /**< R10 bit 4 */ + int env_freq; /**< R11, R12 */ + int env_style; /**< R13 */ +} +ayemu_regdata_t; + + +/** Output sound format \internal */ +typedef struct +{ + int freq; /**< sound freq */ + int channels; /**< channels (1-mono, 2-stereo) */ + int bpc; /**< bits (8 or 16) */ +} +ayemu_sndfmt_t; + +/** + * \defgroup libayemu Functions for emulate AY/YM chip + */ +/*@{*/ + +/** Data structure for sound chip emulation \internal + * + */ +typedef struct +{ + /* emulator settings */ + int table[32]; /**< table of volumes for chip */ + ayemu_chip_t type; /**< general chip type (\b AYEMU_AY or \b AYEMU_YM) */ + int ChipFreq; /**< chip emulator frequency */ + int eq[6]; /**< volumes for channels. + Array contains 6 elements: + A left, A right, B left, B right, C left and C right; + range -100...100 */ + ayemu_regdata_t regs; /**< parsed registers data */ + ayemu_sndfmt_t sndfmt; /**< output sound format */ + + /* flags */ + int magic; /**< structure initialized flag */ + int default_chip_flag; /**< =1 after init, resets in #ayemu_set_chip_type() */ + int default_stereo_flag; /**< =1 after init, resets in #ayemu_set_stereo() */ + int default_sound_format_flag; /**< =1 after init, resets in #ayemu_set_sound_format() */ + int dirty; /**< dirty flag. Sets if any emulator properties changed */ + + int bit_a; /**< state of channel A generator */ + int bit_b; /**< state of channel B generator */ + int bit_c; /**< state of channel C generator */ + int bit_n; /**< current generator state */ + int cnt_a; /**< back counter of A */ + int cnt_b; /**< back counter of B */ + int cnt_c; /**< back counter of C */ + int cnt_n; /**< back counter of noise generator */ + int cnt_e; /**< back counter of envelop generator */ + int ChipTacts_per_outcount; /**< chip's counts per one sound signal count */ + int Amp_Global; /**< scale factor for amplitude */ + int vols[6][32]; /**< stereo type (channel volumes) and chip table. + This cache calculated by #table and #eq */ + int EnvNum; /**< number of current envilopment (0...15) */ + int env_pos; /**< current position in envelop (0...127) */ + int Cur_Seed; /**< random numbers counter */ +} +ayemu_ay_t; + +EXTERN void +ayemu_init(ayemu_ay_t *ay); + +EXTERN void +ayemu_reset(ayemu_ay_t *ay); + +EXTERN int +ayemu_set_chip_type(ayemu_ay_t *ay, ayemu_chip_t chip, int *custom_table); + +EXTERN void +ayemu_set_chip_freq(ayemu_ay_t *ay, int chipfreq); + +EXTERN int +ayemu_set_stereo(ayemu_ay_t *ay, ayemu_stereo_t stereo, int *custom_eq); + +EXTERN int +ayemu_set_sound_format (ayemu_ay_t *ay, int freq, int chans, int bits); + +EXTERN void +ayemu_set_regs (ayemu_ay_t *ay, unsigned char *regs); + +EXTERN void* +ayemu_gen_sound (ayemu_ay_t *ay, void *buf, size_t bufsize); + +/*@}*/ + +END_C_DECLS + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vtx/ayemu_vtxfile.h Wed Feb 28 04:38:53 2007 -0800 @@ -0,0 +1,92 @@ +#ifndef _AYEMU_vtxfile_h +#define _AYEMU_vtxfile_h + +#include <glib.h> +#include <audacious/vfs.h> +#include "ayemu_8912.h" + +/* The following constants and data structure comes from + VTX file format description. You can see them on + http:// +*/ + +#define AYEMU_VTX_NTSTRING_MAX 255 + +typedef char NTstring[AYEMU_VTX_NTSTRING_MAX+1]; + +BEGIN_C_DECLS + +/** VTX file format header and status of open file + * \internal + * + * This structure used for save VTX file format header's data + * (song author, title and so on). + */ +struct VTXFileHeader +{ + ayemu_chip_t chiptype; /**< Type of sound chip */ + int stereo; /**< Type of stereo: 0-ABC, 1-BCA... (see VTX format description) */ + int loop; /**< song loop */ + int chipFreq; /**< AY chip freq (1773400 for ZX) */ + int playerFreq; /**< 50 Hz for ZX, 60 Hz for yamaha */ + int year; /**< year song composed */ + NTstring title; /**< song title */ + NTstring author; /**< song author */ + NTstring from; /**< song from */ + NTstring tracker; /**< tracker */ + NTstring comment; /**< comment */ + size_t regdata_size; /**< size of unpacked data. Used in uncompression alhoritm. */ +}; + +/** + * \defgroup vtxfile Functions for extract data from vtx files + */ +/*@{*/ + + +/** structure for VTX file format handler + * \internal + * It stores VTX file header and current state + * (open file pointer, extracted register data, etc). + */ +typedef struct +{ + VFSFile *fp; /**< opening .vtx file pointer */ + struct VTXFileHeader hdr; /**< VTX header data */ + char *regdata; /**< unpacked song data */ + int pos; /**< current data frame offset */ +} ayemu_vtx_t; + + +/** Open vtx file and read vtx file header + \arg \c vtx - pointer to ayemu_vtx_t structure + \arg \c filename - filename for open vtx file + \return Return true if success, else false +*/ +EXTERN int ayemu_vtx_open (ayemu_vtx_t *vtx, const char *filename); + +/** Read and encode lha data from .vtx file. + * \return Return pointer to unpacked data or NULL. + */ +EXTERN char *ayemu_vtx_load_data (ayemu_vtx_t *vtx); + +/** Get next 14-bytes frame of AY register data. + * \return Return value: true if sucess, false if not enought data. + */ +EXTERN int ayemu_vtx_get_next_frame (ayemu_vtx_t *vtx, char *regs); + +/** Print formated file name. If fmt is NULL the default format %a - %t will used + * \return none. + */ +EXTERN void ayemu_vtx_sprintname (const ayemu_vtx_t *vtx, char *buf, const int sz, const char *fmt); + +/** Free all of allocaded resource for this file. + * You must call this function on end work with vtx file + */ +EXTERN void ayemu_vtx_free (ayemu_vtx_t *vtx); + +/*@}*/ + +END_C_DECLS + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vtx/config.c Wed Feb 28 04:38:53 2007 -0800 @@ -0,0 +1,10 @@ +/* + * Plugin configure dialog. + */ +#include "audacious/plugin.h" + +void vtx_config(void) +{ + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vtx/info.c Wed Feb 28 04:38:53 2007 -0800 @@ -0,0 +1,49 @@ +#include <audacious/plugin.h> +#include <audacious/util.h> +#include <audacious/output.h> +#include <audacious/titlestring.h> +#include <audacious/vfs.h> +#include <audacious/strings.h> +#include <audacious/i18n.h> + +#include "ayemu.h" +#include "vtx.h" + +void vtx_file_info(gchar *filename) +{ + static GtkWidget *box; + ayemu_vtx_t vtx; + + if (!ayemu_vtx_open(&vtx, filename)) + { + fprintf(stderr, "Can't open file %s\n", filename); + return; + } + else + { + gchar head[1024]; + gchar body[8192]; + + sprintf(head, "Details about %s", filename); + + ayemu_vtx_sprintname(&vtx, body, sizeof(body), + "Title: %t\n" + "Author: %a\n" + "From : %f\n" + "Tracker : %T\n" + "Comment : %C\n" + "Chip type: %c\n" + "Stereo: %s\n" + "Loop: %l\n" + "Chip freq: %F\n" + "Player Freq:%P\n" + "Year: %y"); + + box = xmms_show_message (head, + body, + _("Ok"), FALSE, NULL, NULL); + + + } + g_signal_connect (G_OBJECT (box), "destroy", G_CALLBACK(gtk_widget_destroyed), &box); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vtx/lh5dec.c Wed Feb 28 04:38:53 2007 -0800 @@ -0,0 +1,297 @@ +/* extractiong lh5 module + (c) Haruhiko Okumura + (m) Roman Scherbakov +*/ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> /* memmove */ +#include <limits.h> + +static unsigned short bitbuf; + +#define BITBUFSIZ (CHAR_BIT * sizeof bitbuf) + +#define DICBIT 13 /* 12(-lh4-) or 13(-lh5-) */ +#define DICSIZ (1L << DICBIT) +#define MATCHBIT 8 /* bits for MAXMATCH - THRESHOLD */ +#define MAXMATCH 256 /* formerly F (not more than unsigned char_MAX + 1) */ +#define THRESHOLD 3 /* choose optimal value */ +#define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD) +#define CBIT 9 /* $\lfloor \log_2 NC \rfloor + 1$ */ +#define CODE_BIT 16 /* codeword length */ +#define NP (DICBIT + 1) +#define NT (CODE_BIT + 3) +#define PBIT 4 /* smallest integer such that (1U << PBIT) > NP */ +#define TBIT 5 /* smallest integer such that (1U << TBIT) > NT */ +#if NT > NP +#define NPT NT +#else +#define NPT NP +#endif + +static unsigned long origsize, compsize; +static unsigned char *in_buf; +static unsigned char *out_buf; + +static unsigned short subbitbuf; +static int bitcount; + +static unsigned short left[2 * NC - 1], right[2 * NC - 1]; +static unsigned char c_len[NC], pt_len[NPT]; +static unsigned short blocksize; + +static unsigned short c_table[4096], pt_table[256]; + +static int j; /* remaining bytes to copy */ + + +static void error(char *msg) +{ + fprintf(stderr, "libayemu: lh5dec.c: %s\n", msg); + exit(EXIT_FAILURE); +} + +static void fillbuf(int n) /* Shift bitbuf n bits left, read n bits */ +{ + bitbuf <<= n; + while (n > bitcount) { + bitbuf |= subbitbuf << (n -= bitcount); + if (compsize != 0) { + compsize--; subbitbuf = *in_buf++; + } else subbitbuf = 0; + bitcount = CHAR_BIT; + } + bitbuf |= subbitbuf >> (bitcount -= n); +} + +static unsigned short getbits(int n) +{ + unsigned short x; + + x = bitbuf >> (BITBUFSIZ - n); fillbuf(n); + return x; +} + +// make table for decoding + +static void make_table(int nchar, unsigned char bitlen[], int tablebits, unsigned short table[]) +{ + unsigned short count[17], weight[17], start[18], *p; + unsigned short i, k, len, ch, jutbits, avail, nextcode, mask; + + for (i = 1; i <= 16; i++) count[i] = 0; + for (i = 0; i < nchar; i++) count[bitlen[i]]++; + + start[1] = 0; + for (i = 1; i <= 16; i++) + start[i + 1] = start[i] + (count[i] << (16 - i)); + if (start[17] != (unsigned short)(1U << 16)) error("Bad table"); + + jutbits = 16 - tablebits; + for (i = 1; i <= tablebits; i++) { + start[i] >>= jutbits; + weight[i] = 1U << (tablebits - i); + } + while (i <= 16) weight[i++] = 1U << (16 - i); + + i = start[tablebits + 1] >> jutbits; + if (i != (unsigned short)(1U << 16)) { + k = 1U << tablebits; + while (i != k) table[i++] = 0; + } + + avail = nchar; + mask = 1U << (15 - tablebits); + for (ch = 0; ch < nchar; ch++) { + if ((len = bitlen[ch]) == 0) continue; + nextcode = start[len] + weight[len]; + if (len <= tablebits) { + for (i = start[len]; i < nextcode; i++) table[i] = ch; + } else { + k = start[len]; + p = &table[k >> jutbits]; + i = len - tablebits; + while (i != 0) { + if (*p == 0) { + right[avail] = left[avail] = 0; + *p = avail++; + } + if (k & mask) p = &right[*p]; + else p = &left[*p]; + k <<= 1; i--; + } + *p = ch; + } + start[len] = nextcode; + } +} + +// static Huffman + +static void read_pt_len(int nn, int nbit, int i_special) +{ + int i, c, n; + unsigned short mask; + + n = getbits(nbit); + if (n == 0) { + c = getbits(nbit); + for (i = 0; i < nn; i++) pt_len[i] = 0; + for (i = 0; i < 256; i++) pt_table[i] = c; + } else { + i = 0; + while (i < n) { + c = bitbuf >> (BITBUFSIZ - 3); + if (c == 7) { + mask = 1U << (BITBUFSIZ - 1 - 3); + while (mask & bitbuf) { mask >>= 1; c++; } + } + fillbuf((c < 7) ? 3 : c - 3); + pt_len[i++] = c; + if (i == i_special) { + c = getbits(2); + while (--c >= 0) pt_len[i++] = 0; + } + } + while (i < nn) pt_len[i++] = 0; + make_table(nn, pt_len, 8, pt_table); + } +} + +static void read_c_len(void) +{ + int i, c, n; + unsigned short mask; + + n = getbits(CBIT); + if (n == 0) { + c = getbits(CBIT); + for (i = 0; i < NC; i++) c_len[i] = 0; + for (i = 0; i < 4096; i++) c_table[i] = c; + } else { + i = 0; + while (i < n) { + c = pt_table[bitbuf >> (BITBUFSIZ - 8)]; + if (c >= NT) { + mask = 1U << (BITBUFSIZ - 1 - 8); + do { + if (bitbuf & mask) c = right[c]; + else c = left [c]; + mask >>= 1; + } while (c >= NT); + } + fillbuf(pt_len[c]); + if (c <= 2) { + if (c == 0) c = 1; + else if (c == 1) c = getbits(4) + 3; + else c = getbits(CBIT) + 20; + while (--c >= 0) c_len[i++] = 0; + } else c_len[i++] = c - 2; + } + while (i < NC) c_len[i++] = 0; + make_table(NC, c_len, 12, c_table); + } +} + + +static unsigned short decode_c(void) +{ + unsigned short j, mask; + + if (blocksize == 0) { + blocksize = getbits(16); + read_pt_len(NT, TBIT, 3); + read_c_len(); + read_pt_len(NP, PBIT, -1); + } + blocksize--; + j = c_table[bitbuf >> (BITBUFSIZ - 12)]; + if (j >= NC) { + mask = 1U << (BITBUFSIZ - 1 - 12); + do { + if (bitbuf & mask) j = right[j]; + else j = left [j]; + mask >>= 1; + } while (j >= NC); + } + fillbuf(c_len[j]); + return j; +} + + +static unsigned short decode_p(void) +{ + unsigned short j, mask; + + j = pt_table[bitbuf >> (BITBUFSIZ - 8)]; + if (j >= NP) { + mask = 1U << (BITBUFSIZ - 1 - 8); + do { + if (bitbuf & mask) j = right[j]; + else j = left [j]; + mask >>= 1; + } while (j >= NP); + } + fillbuf(pt_len[j]); + if (j != 0) j = (1U << (j - 1)) + getbits(j - 1); + return j; +} + + +static void decode(unsigned short count, unsigned char buffer[]) +{ + static unsigned short i; + unsigned short r, c; + + r = 0; + while (--j >= 0) { + buffer[r] = buffer[i]; + i = (i + 1) & (DICSIZ - 1); + if (++r == count) return; + } + for ( ; ; ) { + c = decode_c(); + if (c <= UCHAR_MAX) { + buffer[r] = c & UCHAR_MAX; + if (++r == count) return; + } else { + j = c - (UCHAR_MAX + 1 - THRESHOLD); + i = (r - decode_p() - 1) & (DICSIZ - 1); + while (--j >= 0) { + buffer[r] = buffer[i]; + i = (i + 1) & (DICSIZ - 1); + if (++r == count) return; + } + } + } +} + +void lh5_decode(unsigned char *inp, unsigned char *outp, unsigned long original_size, unsigned long packed_size) +{ + unsigned short n; + unsigned char *buffer; + + compsize = packed_size; + origsize = original_size; + in_buf = inp; + out_buf = outp; + + buffer = (unsigned char *) malloc(DICSIZ); + if (!buffer) error ("Out of memory"); + + bitbuf = 0; subbitbuf = 0; bitcount = 0; + fillbuf(BITBUFSIZ); + blocksize = 0; + j = 0; + + while (origsize != 0) { + n = (origsize > DICSIZ) ? DICSIZ : (unsigned short)origsize; + decode(n, buffer); + memmove(out_buf, buffer, n); + out_buf += n; + origsize -= n; + } + + if (buffer) free (buffer); + buffer = NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vtx/vtx.c Wed Feb 28 04:38:53 2007 -0800 @@ -0,0 +1,330 @@ +/* VTXplugin - VTX player for XMMS + * + * Copyright (C) 2002-2004 Sashnov Alexander + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <audacious/plugin.h> + +#include <audacious/output.h> +#include <audacious/util.h> +#include <audacious/titlestring.h> +#include <audacious/vfs.h> +#include <audacious/strings.h> +#include <audacious/i18n.h> + +#include "vtx.h" +#include <ayemu.h> + +extern InputPlugin vtx_ip; + +#define SNDBUFSIZE 1024 +char sndbuf[SNDBUFSIZE]; + +static GThread *play_thread = NULL; + +int seek_to; + +int freq = 44100; +int chans = 2; + +/* NOTE: if you change 'bits' you also need change constant FMT_S16_NE + * to more appropriate in line + * + * (vtx_ip.output->open_audio (FMT_S16_NE, * freq, chans) == 0) + * in function vtx_play_file() + * + * and in produce_audio() function call. + */ +const int bits = 16; + +ayemu_ay_t ay; +ayemu_vtx_t vtx; + +static gchar *vtx_fmts[] = { "vtx", NULL }; + +int +vtx_is_our_fd (char *filename, VFSFile *fp) +{ + char buf[2]; + + vfs_fread (buf, 2, 1, fp); + return (!strncasecmp (buf, "ay", 2) || !strncasecmp (buf, "ym", 2)); +} + +int +vtx_is_our_file (char *filename) +{ + gboolean ret; + VFSFile *fp; + + fp = vfs_fopen(filename, "rb"); + ret = vtx_is_our_fd(filename, fp); + vfs_fclose(fp); + + return ret; +} + +TitleInput * +vtx_get_song_tuple_from_vtx(const gchar *filename, ayemu_vtx_t *in) +{ + TitleInput *out = bmp_title_input_new(); + gchar *string; + + out->performer = g_strdup(in->hdr.author); + out->track_name = g_strdup(in->hdr.title); + + out->file_name = g_strdup(g_basename(filename)); + out->file_path = g_path_get_dirname(filename); + if ((string = strrchr(out->file_name, '.'))) + { + out->file_ext = string + 1; + *string = '\0'; + } + + out->length = in->hdr.regdata_size / 14 * 1000 / 50; + + return out; +} + +TitleInput * +vtx_get_song_tuple(gchar *filename) +{ + ayemu_vtx_t tmp; + + if (ayemu_vtx_open (&tmp, filename)) + { + TitleInput *ti = vtx_get_song_tuple_from_vtx(filename, &tmp); + ayemu_vtx_free(&tmp); + return ti; + } + + return NULL; +} + +/* sound playing thread, runing by vtx_play_file() */ +static gpointer +play_loop (gpointer args) +{ + InputPlayback *playback = (InputPlayback *) args; + void *stream; /* pointer to current position in sound buffer */ + unsigned char regs[14]; + int need; + int left; /* how many sound frames can play with current AY register frame */ + int donow; + int rate; + + left = 0; + rate = chans * (bits / 8); + + while (playback->playing && !playback->eof) + { + /* fill sound buffer */ + stream = sndbuf; + for (need = SNDBUFSIZE / rate ; need > 0 ; need -= donow) + if (left > 0) + { /* use current AY register frame */ + donow = (need > left) ? left : need; + left -= donow; + stream = ayemu_gen_sound (&ay, (char *)stream, donow * rate); + } + else + { /* get next AY register frame */ + if (ayemu_vtx_get_next_frame (&vtx, regs) == 0) + { + playback->eof = TRUE; + donow = need; + memset (stream, 0, donow * rate); + } + else + { + left = freq / vtx.hdr.playerFreq; + ayemu_set_regs (&ay, regs); + donow = 0; + } + } + + while (playback->output->buffer_free () < SNDBUFSIZE && playback->playing + && seek_to == -1) + g_usleep(10000); + + if (playback->playing && seek_to == -1) + produce_audio(playback->output->written_time (), FMT_S16_NE, + chans , SNDBUFSIZE, sndbuf, &playback->playing); + + if (playback->eof) + { + playback->output->buffer_free (); + playback->output->buffer_free (); + } + + /* jump to time in seek_to (in seconds) */ + if (seek_to != -1) + { + vtx.pos = seek_to * 50; /* (time in sec) * 50 = offset in AY register data frames */ + playback->output->flush (seek_to * 1000); + seek_to = -1; + } + } + + /* close sound and release vtx file must be done in vtx_stop() */ + g_thread_exit (NULL); + + return NULL; +} + +void vtx_play_file (InputPlayback *playback) +{ + gchar *filename = playback->filename; + gchar *buf; + TitleInput *ti; + + memset (&ay, 0, sizeof(ay)); + + if (!ayemu_vtx_open (&vtx, filename)) + g_print ("libvtx: Error read vtx header from %s\n", filename); + else if (!ayemu_vtx_load_data (&vtx)) + g_print ("libvtx: Error read vtx data from %s\n", filename); + else + { + ayemu_init(&ay); + ayemu_set_chip_type(&ay, vtx.hdr.chiptype, NULL); + ayemu_set_chip_freq(&ay, vtx.hdr.chipFreq); + ayemu_set_stereo(&ay, vtx.hdr.stereo, NULL); + + playback->error = FALSE; + if (playback->output->open_audio (FMT_S16_NE, freq, chans) == 0) + { + g_print ("libvtx: output audio error!\n"); + playback->error = TRUE; + playback->playing = FALSE; + return; + } + + playback->eof = FALSE; + seek_to = -1; + + ti = vtx_get_song_tuple_from_vtx(playback->filename, &vtx); + + buf = xmms_get_titlestring(xmms_get_gentitle_format(), ti); + + vtx_ip.set_info (buf, vtx.hdr.regdata_size / 14 * 1000 / 50, + 14 * 50 * 8, freq, bits / 8); + + g_free (buf); + + bmp_title_input_free(ti); + + playback->playing = TRUE; + play_thread = g_thread_create (play_loop, playback, TRUE, NULL); + } +} + +void +vtx_stop (InputPlayback *playback) +{ + if (playback->playing && play_thread != NULL) + { + playback->playing = FALSE; + + g_thread_join (play_thread); + play_thread = NULL; + playback->output->close_audio (); + ayemu_vtx_free (&vtx); + } +} + +/* seek to specified number of seconds */ +void +vtx_seek (InputPlayback *playback, int time) +{ + if (time * 50 < vtx.hdr.regdata_size / 14) + { + playback->eof = FALSE; + seek_to = time; + + /* wait for affect changes in parallel thread */ + while (seek_to != -1) + g_usleep (10000); + } +} + +/* Pause or unpause */ +void +vtx_pause (InputPlayback *playback, short p) +{ + playback->output->pause (p); +} + +/* Function to grab the title string */ +void +vtx_get_song_info (char *filename, char **title, int *length) +{ + ayemu_vtx_t tmp; + + (*length) = -1; + (*title) = NULL; + + if (ayemu_vtx_open (&tmp, filename)) { + TitleInput *ti = vtx_get_song_tuple_from_vtx(filename, &tmp); + + *title = xmms_get_titlestring(xmms_get_gentitle_format(), ti); + *length = ti->length; + + ayemu_vtx_free (&tmp); + bmp_title_input_free(ti); + } +} + +InputPlugin vtx_ip = { + NULL, /* FILLED BY XMMS */ + NULL, /* FILLED BY XMMS */ + "VTX Plugin", /* Plugin description */ + NULL, /* Initialization */ + vtx_about, /* Show aboutbox */ + vtx_config, /* Show/edit configuration */ + vtx_is_our_file, /* Check file, return 1 if the plugin can handle this file */ + NULL, /* Scan directory */ + vtx_play_file, /* Play given file */ + vtx_stop, /* Stop playing */ + vtx_pause, /* Pause playing */ + vtx_seek, /* Seek time */ + NULL, /* Set equalizer */ + NULL, /* Get playing time (obsoleted by InputPlayback API) */ + NULL, /* Get volume */ + NULL, /* Set volume */ + NULL, /* Cleanup */ + NULL, /* OBSOLETE! */ + NULL, /* Send data to Visualization plugin */ + NULL, NULL, /* FILLED BY XMMS */ + vtx_get_song_info, /* Get song title and length */ + vtx_file_info, /* Show file-information dialog */ + NULL, /* FILLED BY XMMS */ + vtx_get_song_tuple, /* Tuple */ + NULL, /* Tuple */ + NULL, /* Buffer */ + vtx_is_our_fd, /* VFS */ + vtx_fmts /* ext assist */ +}; + +/* called from xmms for plug */ +InputPlugin * +get_iplugin_info (void) +{ + vtx_ip.description = g_strdup (_("VTX Plugin")); + return &vtx_ip; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vtx/vtx.h Wed Feb 28 04:38:53 2007 -0800 @@ -0,0 +1,28 @@ +#ifndef VTX_H +#define VTX_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pthread.h> + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "audacious/plugin.h" + +void vtx_about(void); +void vtx_config(void); +void vtx_file_info(char *filename); + +extern int vtx_is_our_file(char *filename); +extern void vtx_play_file (InputPlayback *playback); +extern void vtx_stop (InputPlayback *playback); +extern void vtx_seek (InputPlayback *playback, int time); +extern void vtx_pause (InputPlayback *playback, short p); +extern int vtx_get_time (InputPlayback *playback); +extern void vtx_get_song_info (char *filename, char **title, int *length); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vtx/vtxfile.c Wed Feb 28 04:38:53 2007 -0800 @@ -0,0 +1,321 @@ +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <ctype.h> + +#include "ayemu.h" + +/* defined in lh5dec.c */ +extern void lh5_decode(unsigned char *inp,unsigned char *outp,unsigned long original_size, unsigned long packed_size); + + +/* Read 8-bit integer from file. + * Return 1 if error occurs + */ +static int read_byte(VFSFile *fp, int *p) +{ + int c; + if ((c = vfs_getc(fp)) == EOF) { + perror("libayemu: read_byte()"); + return 1; + } + *p = c; + return 0; +} + +/* Read 16-bit integer from file. + * Return 1 if error occurs + */ +static int read_word16(VFSFile *fp, int *p) +{ + int c; + if ((c = vfs_getc(fp)) == EOF) { + perror("libayemu: read_word16()"); + return 1; + } + *p = c; + + if ((c = vfs_getc(fp)) == EOF) { + perror("libayemu: read_word16()"); + return 1; + } + *p += c << 8; + + return 0; +} + +/* read 32-bit integer from file. + * Returns 1 if error occurs + */ +static int read_word32(VFSFile *fp, int *p) +{ + int c; + + if ((c = vfs_getc(fp)) == EOF) { + perror("libayemu: read_word32()"); + return 1; + } + *p = c; + + if ((c = vfs_getc(fp)) == EOF) { + perror("libayemu: read_word32()"); + return 1; + } + *p += c << 8; + + if ((c = vfs_getc(fp)) == EOF) { + perror("libayemu: read_word32()"); + return 1; + } + *p += c << 16; + + if ((c = vfs_getc(fp)) == EOF) { + perror("libayemu: read_word32()"); + return 1; + } + *p += c << 24; + + return 0; +} + +/* read_NTstring: reads null-terminated string from file. + * Return 1 if error occures. + */ +static int read_NTstring(VFSFile *fp, char s[]) +{ + int i, c; + for (i = 0 ; i < AYEMU_VTX_NTSTRING_MAX && (c = vfs_getc(fp)) != EOF && c ; i++) + s[i] = c; + s[i] = '\0'; + if (c == EOF) { + fprintf(stderr, "libayemu: read_NTstring(): uninspected end of file!\n"); + return 1; + } + return 0; +} + +/** Open specified .vtx file and read vtx file header + * + * Open specified .vtx file and read vtx file header in struct vtx + * Return value: true if success, else false + */ +int ayemu_vtx_open (ayemu_vtx_t *vtx, const char *filename) +{ + char buf[2]; + int error = 0; + + vtx->regdata = NULL; + + if ((vtx->fp = vfs_fopen (filename, "rb")) == NULL) { + fprintf(stderr, "ayemu_vtx_open: Cannot open file %s: %s\n", filename, strerror(errno)); + return 0; + } + + if (vfs_fread(buf, 2, 1, vtx->fp) != 1) { + fprintf(stderr,"ayemu_vtx_open: Can't read from %s: %s\n", filename, strerror(errno)); + error = 1; + } + + buf[0] = tolower(buf[0]); + buf[1] = tolower(buf[1]); + + if (strncmp(buf, "ay", 2) == 0) + vtx->hdr.chiptype = AYEMU_AY; + else if (strncmp (buf, "ym", 2) == 0) + vtx->hdr.chiptype = AYEMU_YM; + else { + fprintf (stderr, "File %s is _not_ VORTEX format!\nIt not begins from AY or YM.\n", filename); + error = 1; + } + + /* read VTX header info in order format specified, see http:// ..... */ + if (!error) error = read_byte(vtx->fp, &vtx->hdr.stereo); + if (!error) error = read_word16(vtx->fp, &vtx->hdr.loop); + if (!error) error = read_word32(vtx->fp, &vtx->hdr.chipFreq); + if (!error) error = read_byte(vtx->fp, &vtx->hdr.playerFreq); + if (!error) error = read_word16(vtx->fp, &vtx->hdr.year); + if (!error) error = read_word32(vtx->fp, &vtx->hdr.regdata_size); + if (!error) error = read_NTstring(vtx->fp, vtx->hdr.title); + if (!error) error = read_NTstring(vtx->fp, vtx->hdr.author); + if (!error) error = read_NTstring(vtx->fp, vtx->hdr.from); + if (!error) error = read_NTstring(vtx->fp, vtx->hdr.tracker); + if (!error) error = read_NTstring (vtx->fp, vtx->hdr.comment); + + if (error) { + vfs_fclose(vtx->fp); + vtx->fp = NULL; + } + return !error; +} + +/** Read and encode lha data from .vtx file + * + * Return value: pointer to unpacked data or NULL + * Note: you must call ayemu_vtx_open() first. + */ +char *ayemu_vtx_load_data (ayemu_vtx_t *vtx) +{ + char *packed_data; + size_t packed_size; + size_t buf_alloc; + int c; + + if (vtx->fp == NULL) { + fprintf(stderr, "ayemu_vtx_load_data: tune file not open yet (do you call ayemu_vtx_open first?)\n"); + return NULL; + } + packed_size = 0; + buf_alloc = 4096; + packed_data = (char *) malloc (buf_alloc); + /* read packed AY register data to end of file. */ + while ((c = vfs_getc (vtx->fp)) != EOF) { + if (buf_alloc < packed_size) { + buf_alloc *= 2; + packed_data = (char *) realloc (packed_data, buf_alloc); + if (packed_data == NULL) { + fprintf (stderr, "ayemu_vtx_load_data: Packed data out of memory!\n"); + vfs_fclose (vtx->fp); + return NULL; + } + } + packed_data[packed_size++] = c; + } + vfs_fclose (vtx->fp); + vtx->fp = NULL; + if ((vtx->regdata = (char *) malloc (vtx->hdr.regdata_size)) == NULL) { + fprintf (stderr, "ayemu_vtx_load_data: Can allocate %d bytes for unpack register data\n", vtx->hdr.regdata_size); + free (packed_data); + return NULL; + } + lh5_decode (packed_data, vtx->regdata, vtx->hdr.regdata_size, packed_size); + free (packed_data); + vtx->pos = 0; + return vtx->regdata; +} + +/** Get next 14-bytes frame of AY register data. + * + * Return value: 1 if sucess, 0 if no enought data. + */ +int ayemu_vtx_get_next_frame (ayemu_vtx_t *vtx, char *regs) +{ + int numframes = vtx->hdr.regdata_size / 14; + if (vtx->pos++ >= numframes) + return 0; + else { + int n; + char *p = vtx->regdata + vtx->pos; + for(n = 0 ; n < 14 ; n++, p+=numframes) + regs[n] = *p; + return 1; + } +} + +static void append_string(char *buf, const int sz, const char *str) +{ + if (strlen(buf) + strlen(str) < sz - 1) + strcat(buf, str); +} + +static void append_number(char *buf, const int sz, const int num) +{ + char s[32]; + snprintf(s, sizeof(s), "%d", num); + return append_string(buf, sz, s); +} + +static void append_char(char *buf, const int sz, const char c) +{ + int pos = strlen(buf); + if (pos < sz - 1) + buf[pos++] = c; + buf[pos] = '\0'; +} + +/** Print formated file name. If fmt is NULL the default format %a - %t will used + * + * %% the % sign + * %a author of song + * %t song title + * %y year + * %f song from + * %T Tracker + * %C Comment + * %s stereo type (ABC, BCA, ...) + * %l 'looped' or 'non-looped' + * %c chip type: 'AY' or 'YM' + * %F chip Freq + * %P player freq + */ +void ayemu_vtx_sprintname (const ayemu_vtx_t *vtx, char *const buf, const int sz, const char *fmt) +{ + static char *stereo_types[] = { "MONO", "ABC", "ACB", "BAC", "BCA", "CAB", "CBA" }; + + if (fmt == NULL) + fmt = "%a - %t"; + + buf[0] = '\0'; + + while (*fmt != '\0') { + if (*fmt == '%') { + switch(*++fmt) { + case 'a': + append_string(buf, sz, vtx->hdr.author); + break; + case 't': + append_string(buf, sz, vtx->hdr.title); + break; + case 'y': + append_number(buf, sz, vtx->hdr.year); + break; + case 'f': + append_string(buf, sz, vtx->hdr.from); + break; + case 'T': + append_string(buf, sz, vtx->hdr.tracker); + break; + case 'C': + append_string(buf, sz, vtx->hdr.comment); + break; + case 's': + append_string(buf, sz, stereo_types[vtx->hdr.stereo]); + break; + case 'l': + append_string(buf, sz, (vtx->hdr.loop)? "looped" : "non-looped" ); + break; + case 'c': + append_string(buf, sz, (vtx->hdr.chiptype == AYEMU_AY)? "AY" : "YM" ); + break; + case 'F': + append_number(buf, sz, vtx->hdr.chipFreq); + break; + case 'P': + append_number(buf, sz, vtx->hdr.playerFreq); + break; + default: + append_char(buf, sz, *fmt); + } + fmt++; + } else { + append_char(buf, sz, *fmt++); + } + } +} + +/** Free all of allocaded resource for this file. + * + * Free the unpacket register data if is and close file. + */ +void ayemu_vtx_free (ayemu_vtx_t *vtx) +{ + if (vtx->fp) { + vfs_fclose(vtx->fp); + vtx->fp = NULL; + } + + if (vtx->regdata) { + free(vtx->regdata); + vtx->regdata = NULL; + } +}