view src/adplug/core/rix.cxx @ 952:87666f9bf6d0 trunk

[svn] Upstream commit "Vastly enhanced generic Protracker player and modified loaders accordingly. Copl now supports a getchip() method. A2M loader enhanced for OPL3 features." manually applied by decoding the actual changes from an ocean of whitespace damage. It compiles, but do test it.
author chainsaw
date Fri, 13 Apr 2007 09:09:50 -0700
parents cae46214b8bf
children ae7e96e44f22
line wrap: on
line source

/*
 * Adplug - Replayer for many OPL2/OPL3 audio file formats.
 * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * rix.cpp - Softstar RIX OPL Format Player by palxex <palxex.ys168.com>
 *                                             BSPAL <BSPAL.ys168.com>
 */

#include "rix.h"
#include "debug.h"

const unsigned char CrixPlayer::adflag[] = {0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1};
const unsigned char CrixPlayer::reg_data[] = {0,1,2,3,4,5,8,9,10,11,12,13,16,17,18,19,20,21};
const unsigned char CrixPlayer::ad_C0_offs[] = {0,1,2,0,1,2,3,4,5,3,4,5,6,7,8,6,7,8};
const unsigned char CrixPlayer::modify[] = {0,3,1,4,2,5,6,9,7,10,8,11,12,15,13,16,14,17,12,\
					    15,16,0,14,0,17,0,13,0};
const unsigned char CrixPlayer::bd_reg_data[] = {
  0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x08,0x04,0x02,0x01,
  0x00,0x01,0x01,0x03,0x0F,0x05,0x00,0x01,0x03,0x0F,0x00,
  0x00,0x00,0x01,0x00,0x00,0x01,0x01,0x0F,0x07,0x00,0x02,
  0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x0A,
  0x04,0x00,0x08,0x0C,0x0B,0x00,0x00,0x00,0x01,0x00,0x00,
  0x00,0x00,0x0D,0x04,0x00,0x06,0x0F,0x00,0x00,0x00,0x00,
  0x01,0x00,0x00,0x0C,0x00,0x0F,0x0B,0x00,0x08,0x05,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x0F,0x0B,0x00,
  0x07,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
  0x0F,0x0B,0x00,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x01,0x00,0x0F,0x0B,0x00,0x07,0x05,0x00,0x00,0x00,
  0x00,0x00,0x00};
unsigned char CrixPlayer::for40reg[] = {0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
					0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F};
unsigned short CrixPlayer::mus_time = 0x4268;

/*** public methods *************************************/

CPlayer *CrixPlayer::factory(Copl *newopl)
{
  return new CrixPlayer(newopl);
}

CrixPlayer::CrixPlayer(Copl *newopl)
  : CPlayer(newopl), flag_mkf(0), file_buffer(0), buf_addr(0)
{
}

CrixPlayer::~CrixPlayer()
{
  if(file_buffer)
    delete [] file_buffer;
}

bool CrixPlayer::load(VFSFile *fd, const CFileProvider &fp)
{
  binistream *f = fp.open(fd); if(!f) return false;
  unsigned long i=0;
  std::string filename(fd->uri);

  if(stricmp(filename.substr(filename.length()-4,4).c_str(),".mkf")==0)
  {
	  flag_mkf=1;
	  f->seek(0);
	  int offset=f->readInt(4);
	  f->seek(offset);
  }
  if(f->readInt(2)!=0x55aa){ fp.close(f);return false; }
  file_buffer = new unsigned char [fp.filesize(f) + 1];
  f->seek(0);
  while(!f->eof())
	file_buffer[i++]=f->readInt(1);
  length=i;
  fp.close(f);
  if(!flag_mkf)
	  buf_addr=file_buffer;
  rewind(0);
  return true;
}

bool CrixPlayer::update()
{
	int_08h_entry();
	return !play_end;
}

void CrixPlayer::rewind(int subsong)
{
  I = 0; T = 0;
  mus_block = 0;
  ins_block = 0;
  rhythm = 0;
  music_on = 0;
  pause_flag = 0;
  band = 0;
  band_low = 0;
  e0_reg_flag = 0;
  bd_modify = 0;
  sustain = 0;
  play_end = 0;
  pos = index = 0; 

  memset(f_buffer, 0, sizeof(unsigned short) * 300);
  memset(a0b0_data2, 0, sizeof(unsigned short) * 11);
  memset(a0b0_data3, 0, 18);
  memset(a0b0_data4, 0, 18);
  memset(a0b0_data5, 0, 96);
  memset(addrs_head, 0, 96);
  memset(insbuf, 0, 28 * sizeof(unsigned short));
  memset(displace, 0, 11 * sizeof(unsigned short));
  memset(reg_bufs, 0, 18 * sizeof(ADDT));

  if(flag_mkf)
  {
	  unsigned int *buf_index=(unsigned int *)file_buffer;
	  int offset1=buf_index[subsong],offset2;
	  while((offset2=buf_index[++subsong])==offset1);
	  length=offset2-offset1+1;
	  buf_addr=file_buffer+offset1;
  }
  opl->init(); 
  opl->write(1,32);	// go to OPL2 mode
  set_new_int();
  data_initial();
}
unsigned int CrixPlayer::getsubsongs()
{
	if(flag_mkf)
	{
		unsigned int *buf_index=(unsigned int *)file_buffer;
		int songs=buf_index[0]/4,i=0;
		for(i=0;i<songs;i++)
			if(buf_index[i+1]==buf_index[i])
				songs--;
		return songs;
	}
	else
		return 1;
}

float CrixPlayer::getrefresh()
{
	return 70.0f;
}

/*------------------Implemention----------------------------*/
inline void CrixPlayer::set_new_int()
{
  if(!ad_initial()) exit(1);
}
/*----------------------------------------------------------*/
inline void CrixPlayer::Pause()
{
  register unsigned short i;
  pause_flag = 1;
  for(i=0;i<11;i++)
    switch_ad_bd(i);
}
/*----------------------------------------------------------*/
inline void CrixPlayer::ad_a0b0l_reg_(unsigned short index,unsigned short p2,unsigned short p3)
{
//   unsigned short i = p2+a0b0_data2[index];
  a0b0_data4[index] = p3;
  a0b0_data3[index] = p2;
}
inline void CrixPlayer::data_initial()
{
  rhythm = buf_addr[2];
  mus_block = (buf_addr[0x0D]<<8)+buf_addr[0x0C];
  ins_block = (buf_addr[0x09]<<8)+buf_addr[0x08];
  I = mus_block+1;
  if(rhythm != 0)
    {
      //		ad_a0b0_reg(6);
      //		ad_a0b0_reg(7);
      //		ad_a0b0_reg(8);
      ad_a0b0l_reg_(8,0x18,0);
      ad_a0b0l_reg_(7,0x1F,0);
    }
  bd_modify = 0;
  //	ad_bd_reg();
  band = 0; music_on = 1;
}
/*----------------------------------------------------------*/
inline unsigned short CrixPlayer::ad_initial()
{
  register unsigned short i,j,k = 0;
  for(i=0;i<25;i++) 
  {
  	f_buffer[i*12]=(unsigned int)((i*24+10000)*0.27461678223+4)>>3;
  	for(int t=1;t<12;t++)
  		f_buffer[i*12+t]=(unsigned int)((double)f_buffer[i*12+t-1]*1.06);
  }
  for(i=0;i<8;i++)
    for(j=0;j<12;j++)
      {
	a0b0_data5[k] = i;
	addrs_head[k] = j;
	k++;
      }
  //ad_bd_reg();
  //ad_08_reg();
  //for(i=0;i<9;i++) ad_a0b0_reg(i);
  e0_reg_flag = 0x20;
  //for(i=0;i<18;i++) ad_bop(0xE0+reg_data[i],0);
  //ad_bop(1,e0_reg_flag);
  return 1;//ad_test();
}
/*----------------------------------------------------------*/
inline void CrixPlayer::ad_bop(unsigned short reg,unsigned short value)
{
  if(reg == 2 || reg == 3)
    AdPlug_LogWrite("switch OPL2/3 mode!\n");
  opl->write(reg & 0xff, value & 0xff);
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::int_08h_entry()
  {   
  unsigned short band_sus = 1;
  while(band_sus)
    {
        if(sustain <= 0)   
	{
	  band_sus = rix_proc();
	  if(band_sus) sustain += band_sus;
            else
	    {
                play_end=1;   
	      break;
	    }
	}
      else
	{
            if(band_sus) sustain -= 14; /* aging */   
	  break;
	}
    }
  }   
/*--------------------------------------------------------------*/
inline unsigned short CrixPlayer::rix_proc()
{
  unsigned char ctrl = 0;
  if(music_on == 0||pause_flag == 1) return 0;
  band = 0;
  while(buf_addr[I] != 0x80 && I<length-1)
    {
      band_low = buf_addr[I-1];
      ctrl = buf_addr[I]; I+=2;
      switch(ctrl&0xF0)
	{
	case 0x90:  rix_get_ins(); rix_90_pro(ctrl&0x0F); break;
	case 0xA0:  rix_A0_pro(ctrl&0x0F,((unsigned short)band_low)<<6); break;
	case 0xB0:  rix_B0_pro(ctrl&0x0F,band_low); break;
	case 0xC0:  switch_ad_bd(ctrl&0x0F);
	  if(band_low != 0) rix_C0_pro(ctrl&0x0F,band_low);
	  break;
	default:    band = (ctrl<<8)+band_low; break;
	}
      if(band != 0) return band;
    }
  music_ctrl();
  I = mus_block+1;
  band = 0; music_on = 1;
  return 0;
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::rix_get_ins()
{
  int		i;
  unsigned char	*baddr = (&buf_addr[ins_block])+(band_low<<6);

  for(i = 0; i < 28; i++)
    insbuf[i] = (baddr[i * 2 + 1] << 8) + baddr[i * 2];
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::rix_90_pro(unsigned short ctrl_l)
{
  if(rhythm == 0 || ctrl_l < 6)
    {
      ins_to_reg(modify[ctrl_l*2],insbuf,insbuf[26]);
      ins_to_reg(modify[ctrl_l*2+1],insbuf+13,insbuf[27]);
      return;
    }
  else if(ctrl_l > 6)
	{
	  ins_to_reg(modify[ctrl_l*2+6],insbuf,insbuf[26]);
	  return;
	}
      else
	{
	  ins_to_reg(12,insbuf,insbuf[26]);
	  ins_to_reg(15,insbuf+13,insbuf[27]);
	  return;
	}
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::rix_A0_pro(unsigned short ctrl_l,unsigned short index)
{
  if(rhythm == 0 || ctrl_l <= 6)
    {
      prepare_a0b0(ctrl_l,index>0x3FFF?0x3FFF:index);
      ad_a0b0l_reg(ctrl_l,a0b0_data3[ctrl_l],a0b0_data4[ctrl_l]);
    }
  else return;
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::prepare_a0b0(unsigned short index,unsigned short v)  /* important !*/
{
  short high = 0,low = 0; unsigned int res;
  int res1 = (v-0x2000)*0x19;
  if(res1 == (int)0xff) return;
  low = res1/0x2000;
  if(low < 0)
    {
      low = 0x18-low; high = (signed short)low<0?0xFFFF:0;
      res = high; res<<=16; res+=low;
      low = ((signed short)res)/(signed short)0xFFE7;
      a0b0_data2[index] = low;
      low = res;
      res = low - 0x18;
      high = (signed short)res%0x19;
      low = (signed short)res/0x19;
      if(high != 0) {low = 0x19; low = low-high;}
    }
  else
    {
      res = high = low;
      low = (signed short)res/(signed short)0x19;
      a0b0_data2[index] = low;
      res = high;
      low = (signed short)res%(signed short)0x19;
    }
  low = (signed short)low*(signed short)0x18;
  displace[index] = low;
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::ad_a0b0l_reg(unsigned short index,unsigned short p2,unsigned short p3)
{
  unsigned short data; unsigned short i = p2+a0b0_data2[index];
  a0b0_data4[index] = p3;
  a0b0_data3[index] = p2;
  i = ((signed short)i<=0x5F?i:0x5F);
  i = ((signed short)i>=0?i:0);
  data = f_buffer[addrs_head[i]+displace[index]/2];
  ad_bop(0xA0+index,data);
  data = a0b0_data5[i]*4+(p3<1?0:0x20)+((data>>8)&3);
  ad_bop(0xB0+index,data);
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::rix_B0_pro(unsigned short ctrl_l,unsigned short index)
{
  register int temp = 0;
  if(rhythm == 0 || ctrl_l < 6) temp = modify[ctrl_l*2+1];
  else
    {
      temp = ctrl_l > 6?ctrl_l*2:ctrl_l*2+1;
      temp = modify[temp+6];
    }
  for40reg[temp] = index>0x7F?0x7F:index;
  ad_40_reg(temp);
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::rix_C0_pro(unsigned short ctrl_l,unsigned short index)
{
  register unsigned short i = index>=12?index-12:0;
  if(ctrl_l < 6 || rhythm == 0)
    {
      ad_a0b0l_reg(ctrl_l,i,1);
      return;
    }
  else
    {
      if(ctrl_l != 6)
	{
	  if(ctrl_l == 8)
	    {
	      ad_a0b0l_reg(ctrl_l,i,0);
	      ad_a0b0l_reg(7,i+7,0);
	    }
	}
      else ad_a0b0l_reg(ctrl_l,i,0);
      bd_modify |= bd_reg_data[ctrl_l];
      ad_bd_reg();
      return;
    }
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::switch_ad_bd(unsigned short index)
{

  if(rhythm == 0 || index < 6) ad_a0b0l_reg(index,a0b0_data3[index],0);
  else
    {
      bd_modify &= (~bd_reg_data[index]),
	ad_bd_reg();
    }
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::ins_to_reg(unsigned short index,unsigned short* insb,unsigned short value)
{
  register unsigned short i;
  for(i=0;i<13;i++) reg_bufs[index].v[i] = insb[i];
  reg_bufs[index].v[13] = value&3;
  ad_bd_reg(),ad_08_reg(),
    ad_40_reg(index),ad_C0_reg(index),ad_60_reg(index),
    ad_80_reg(index),ad_20_reg(index),ad_E0_reg(index);
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::ad_E0_reg(unsigned short index)
{
  unsigned short data = e0_reg_flag == 0?0:(reg_bufs[index].v[13]&3);
  ad_bop(0xE0+reg_data[index],data);
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::ad_20_reg(unsigned short index)
{
  unsigned short data = (reg_bufs[index].v[9] < 1?0:0x80);
  data += (reg_bufs[index].v[10] < 1?0:0x40);
  data += (reg_bufs[index].v[5] < 1?0:0x20);
  data += (reg_bufs[index].v[11] < 1?0:0x10);
  data += (reg_bufs[index].v[1]&0x0F);
  ad_bop(0x20+reg_data[index],data);
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::ad_80_reg(unsigned short index)
{
  unsigned short data = (reg_bufs[index].v[7]&0x0F),temp = reg_bufs[index].v[4];
  data |= (temp << 4);
  ad_bop(0x80+reg_data[index],data);
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::ad_60_reg(unsigned short index)
{
  unsigned short data = reg_bufs[index].v[6]&0x0F,temp = reg_bufs[index].v[3];
  data |= (temp << 4);
  ad_bop(0x60+reg_data[index],data);
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::ad_C0_reg(unsigned short index)
{
  unsigned short data = reg_bufs[index].v[2];
  if(adflag[index] == 1) return;
  data *= 2,
    data |= (reg_bufs[index].v[12] < 1?1:0);
  ad_bop(0xC0+ad_C0_offs[index],data);
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::ad_40_reg(unsigned short index)
{
  unsigned int res = 0;
  unsigned short data = 0,temp = reg_bufs[index].v[0];
  data = 0x3F - (0x3F & reg_bufs[index].v[8]),
    data *= for40reg[index],
    data *= 2,
    data += 0x7F,
    res = data;
  data = res/0xFE,
    data -= 0x3F,
    data = -data,
    data |= temp<<6;
  ad_bop(0x40+reg_data[index],data);
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::ad_bd_reg()
{
  unsigned short data = rhythm < 1? 0:0x20;
  data |= bd_modify;
  ad_bop(0xBD,data);
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::ad_a0b0_reg(unsigned short index)
{
  ad_bop(0xA0+index,0);
  ad_bop(0xB0+index,0);
}
/*--------------------------------------------------------------*/
inline void CrixPlayer::music_ctrl()
{
  register int i;
  for(i=0;i<11;i++)
    switch_ad_bd(i);
}