view src/vtx/vtx.c @ 2284:d19b53359b24

cleaned up the sndfile wav plugin, currently limiting it ONLY TO WAV PLAYBACK. if somebody is more experienced with it and wants to restore the other formats, go ahead (maybe change the name of the plugin too?).
author mf0102 <0102@gmx.at>
date Wed, 09 Jan 2008 15:41:22 +0100
parents 33ffac81f4c7
children 42a5c9d5830b
line wrap: on
line source

/*  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/configdb.h>
#include <audacious/vfs.h>
#include <audacious/main.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 };

void
vtx_init(void)
{
        ConfigDb *db;
        db = aud_cfg_db_open();

        aud_cfg_db_get_int(db, NULL, "src_rate", &freq);
        if (freq < 4000 || freq > 192000)
                freq = 44100;

        aud_cfg_db_close(db);
}

int
vtx_is_our_fd (char *filename, VFSFile *fp)
{
  char buf[2];
    
  aud_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 = aud_vfs_fopen(filename, "rb");    
  ret = vtx_is_our_fd(filename, fp);
  aud_vfs_fclose(fp);

  return ret;
}

Tuple *
vtx_get_song_tuple_from_vtx(const gchar *filename, ayemu_vtx_t *in)
{
  Tuple *out = aud_tuple_new_from_filename(filename);

  aud_tuple_associate_string(out, FIELD_ARTIST, NULL, in->hdr.author);
  aud_tuple_associate_string(out, FIELD_TITLE, NULL, in->hdr.title);

  aud_tuple_associate_int(out, FIELD_LENGTH, NULL, in->hdr.regdata_size / 14 * 1000 / 50);

  aud_tuple_associate_string(out, FIELD_GENRE, NULL, (in->hdr.chiptype == AYEMU_AY)? "AY chiptunes" : "YM chiptunes");
  aud_tuple_associate_string(out, FIELD_ALBUM, NULL, in->hdr.from);
  aud_tuple_associate_string(out, -1, "game", in->hdr.from);

  aud_tuple_associate_string(out, FIELD_QUALITY, NULL, "sequenced");
  aud_tuple_associate_string(out, FIELD_CODEC, NULL, in->hdr.tracker);
  aud_tuple_associate_string(out, -1, "tracker", in->hdr.tracker);

  aud_tuple_associate_int(out, FIELD_YEAR, NULL, in->hdr.year);

  return out;
}

Tuple *
vtx_get_song_tuple(gchar *filename)
{
  ayemu_vtx_t tmp;

  if (ayemu_vtx_open (&tmp, filename))
    {
      Tuple *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, (char *)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)
        playback->pass_audio(playback, FMT_S16_NE,
  			  chans , SNDBUFSIZE, sndbuf, &playback->playing);
    
      if (playback->eof)
	{
	  playback->output->buffer_free ();
	  playback->output->buffer_free ();
	  while (playback->output->buffer_playing())
	    g_usleep(10000);
	  playback->playing = 0;
	}
    
      /* 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;
	}
    }
  ayemu_vtx_free (&vtx);
  return NULL;
}

void vtx_play_file (InputPlayback *playback)
{
  gchar *filename = playback->filename;
  gchar *buf;
  Tuple *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 = aud_tuple_formatter_make_title_string(ti, aud_get_gentitle_format());

      playback->set_params (playback, buf, vtx.hdr.regdata_size / 14 * 1000 / 50,
 	  	       14 * 50 * 8, freq, bits / 8);

      g_free (buf);

      aud_tuple_free(ti);

      playback->playing = TRUE;
      play_thread = g_thread_self();
      playback->set_pb_ready(playback);
      play_loop(playback);
    }
}

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)) {
    Tuple *ti = vtx_get_song_tuple_from_vtx(filename, &tmp);

    *title = aud_tuple_formatter_process_string(ti, aud_get_gentitle_format());
    *length = aud_tuple_get_int(ti, FIELD_LENGTH, NULL);

    ayemu_vtx_free (&tmp);
    aud_tuple_free(ti);
  }
}

InputPlugin vtx_ip = {
	.description = "VTX Audio Plugin",	/* Plugin description */
	.init = vtx_init,		/* Initialization */
	.about = vtx_about,		/* Show aboutbox */
	.configure = vtx_config,		/* Show/edit configuration */
	.is_our_file = vtx_is_our_file,	/* Check file, return 1 if the plugin can handle this file */
	.play_file = vtx_play_file,		/* Play given file */
	.stop = vtx_stop,		/* Stop playing */
	.pause = vtx_pause,		/* Pause playing */
	.seek = vtx_seek,		/* Seek time */
	.get_song_info = vtx_get_song_info,	/* Get song title and length */
	.file_info_box = vtx_file_info,		/* Show file-information dialog */
	.get_song_tuple = vtx_get_song_tuple,	/* Tuple */
	.is_our_file_from_vfs = vtx_is_our_fd,		/* VFS */
	.vfs_extensions = vtx_fmts		/* ext assist */
};

InputPlugin *vtx_iplist[] = { &vtx_ip, NULL };

SIMPLE_INPUT_PLUGIN(vtx, vtx_iplist);