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;
+  }
+}