changeset 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 df18b664f4e0
children ae7e96e44f22
files ChangeLog src/adplug/core/a2m.cxx src/adplug/core/a2m.h src/adplug/core/amd.cxx src/adplug/core/fmc.cxx src/adplug/core/opl.h src/adplug/core/player.cxx src/adplug/core/player.h src/adplug/core/protrack.cxx src/adplug/core/protrack.h src/adplug/core/rad.cxx src/adplug/core/sa2.cxx
diffstat 12 files changed, 393 insertions(+), 235 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri Apr 13 08:07:32 2007 -0700
+++ b/ChangeLog	Fri Apr 13 09:09:50 2007 -0700
@@ -1,3 +1,11 @@
+2007-04-13 15:07:32 +0000  Tony Vroon <chainsaw@gentoo.org>
+  revision [2036]
+  D00 Vibrato and Slides fix by Dennis Lindroos (from upstream CVS).
+  trunk/src/adplug/core/d00.cxx |   11 +++++++++--
+  trunk/src/adplug/core/d00.h   |    4 ++--
+  2 files changed, 11 insertions(+), 4 deletions(-)
+
+
 2007-04-13 12:23:27 +0000  Giacomo Lozito <james@develia.org>
   revision [2034]
   - curl: do NOT use signals with the new multi-thread layout, and take care of the url string value
--- a/src/adplug/core/a2m.cxx	Fri Apr 13 08:07:32 2007 -0700
+++ b/src/adplug/core/a2m.cxx	Fri Apr 13 09:09:50 2007 -0700
@@ -1,6 +1,6 @@
 /*
  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2002 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
@@ -22,13 +22,11 @@
  * This loader detects and loads version 1, 4, 5 & 8 files.
  *
  * version 1-4 files:
- * Following commands are ignored:
- * FF1 - FF9, FAx - FEx
+ * Following commands are ignored: FF1 - FF9, FAx - FEx
  *
  * version 5-8 files:
  * Instrument panning is ignored. Flags byte is ignored.
- * Following commands are ignored:
- * Gxy, Hxy, Kxy - &xy
+ * Following commands are ignored: Gxy, Hxy, Kxy - &xy
  */
 
 #include "a2m.h"
@@ -63,15 +61,11 @@
 bool Ca2mLoader::load(VFSFile *fd, const CFileProvider &fp)
 {
   binistream *f = fp.open(fd); if(!f) return false;
-  struct {
-    char id[10];
-    unsigned long crc;
-    unsigned char version,numpats;
-  } ch;
+  char id[10];
   int i,j,k,t;
   unsigned int l;
-  unsigned char *org = 0, *orgptr;
-  unsigned long alength;
+  unsigned char *org, *orgptr, flags = 0, numpats, version;
+  unsigned long crc, alength;
   unsigned short len[9], *secdata, *secptr;
   const unsigned char convfx[16] = {0,1,2,23,24,3,5,4,6,9,17,13,11,19,7,14};
   const unsigned char convinf1[16] = {0,1,2,6,7,8,9,4,5,3,10,11,12,13,14,15};
@@ -80,29 +74,29 @@
 				     255,255,255,255,255,255,255,255,14,255};
 
   // read header
-  f->readString(ch.id, 10); ch.crc = f->readInt(4);
-  ch.version = f->readInt(1); ch.numpats = f->readInt(1);
+  f->readString(id, 10); crc = f->readInt(4);
+  version = f->readInt(1); numpats = f->readInt(1);
 
   // file validation section
-  if(strncmp(ch.id,"_A2module_",10) || (ch.version != 1 && ch.version != 5 &&
-					ch.version != 4 && ch.version != 8)) {
+  if(strncmp(id,"_A2module_",10) || (version != 1 && version != 5 &&
+				     version != 4 && version != 8)) {
     fp.close(f);
     return false;
   }
 
   // load, depack & convert section
-  nop = ch.numpats; length = 128; restartpos = 0; activechan = 0xffff;
-  if(ch.version == 1 || ch.version == 4) {
+  nop = numpats; length = 128; restartpos = 0;
+  if(version < 5) {
     for(i=0;i<5;i++) len[i] = f->readInt(2);
     t = 9;
-  } else {
+  } else {	// version >= 5
     for(i=0;i<9;i++) len[i] = f->readInt(2);
     t = 18;
   }
 
   // block 0
   secdata = new unsigned short [len[0] / 2];
-  if(ch.version == 1 || ch.version == 5) {
+  if(version == 1 || version == 5) {
     for(i=0;i<len[0]/2;i++) secdata[i] = f->readInt(2);
     org = new unsigned char [MAXBUF]; orgptr = org;
     sixdepak(secdata,org,len[0]);
@@ -113,6 +107,7 @@
   memcpy(songname,orgptr,43); orgptr += 43;
   memcpy(author,orgptr,43); orgptr += 43;
   memcpy(instname,orgptr,250*33); orgptr += 250*33;
+
   for(i=0;i<250;i++) {	// instruments
     inst[i].data[0] = *(orgptr+i*13+10);
     inst[i].data[1] = *(orgptr+i*13);
@@ -125,56 +120,60 @@
     inst[i].data[8] = *(orgptr+i*13+9);
     inst[i].data[9] = *(orgptr+i*13+2);
     inst[i].data[10] = *(orgptr+i*13+3);
-    if(ch.version == 1 || ch.version == 4)
+    if(version < 5)
       inst[i].misc = *(orgptr+i*13+11);
-    else
-      inst[i].misc = 0;
+    else {    // version >= 5 -> OPL3 format
+      int pan = *(orgptr+i*13+11);
+
+      if(pan)
+      inst[i].data[0] |= (pan & 3) << 4;      // set pan
+      else
+      inst[i].data[0] |= 48;                  // enable both speakers
+    }
+
     inst[i].slide = *(orgptr+i*13+12);
   }
 
   orgptr += 250*13;
   memcpy(order,orgptr,128); orgptr += 128;
-  bpm = *orgptr; orgptr += 1;
-  initspeed = *orgptr;
-  // v5-8 files have an additional flag byte here
-  if(ch.version == 1 || ch.version == 5)
-    {
-      delete [] org; org = 0;
-    }
-  delete [] secdata; secdata = 0;
+  bpm = *orgptr; orgptr++;
+  initspeed = *orgptr; orgptr++;
+  if(version >= 5) flags = *orgptr;
+  if(version == 1 || version == 5) delete [] org;
+  delete [] secdata;
 
   // blocks 1-4 or 1-8
   alength = len[1];
-  for(i=0;i<(ch.version == 1 || ch.version == 4 ? ch.numpats/16 : ch.numpats/8);i++)
+  for(i = 0; i < (version < 5 ? numpats / 16 : numpats / 8); i++)
     alength += len[i+2];
 
   secdata = new unsigned short [alength / 2];
-  if(ch.version == 1 || ch.version == 5) {
+  if(version == 1 || version == 5) {
     for(l=0;l<alength/2;l++) secdata[l] = f->readInt(2);
-    org = new unsigned char [MAXBUF * (ch.numpats / (ch.version == 1 ? 16 : 8) + 1)];
+    org = new unsigned char [MAXBUF * (numpats / (version == 1 ? 16 : 8) + 1)];
     orgptr = org; secptr = secdata;
     orgptr += sixdepak(secptr,orgptr,len[1]); secptr += len[1] / 2;
-    if(ch.version == 1) {
-      if(ch.numpats > 16)
+    if(version == 1) {
+      if(numpats > 16)
 	orgptr += sixdepak(secptr,orgptr,len[2]); secptr += len[2] / 2;
-      if(ch.numpats > 32)
+      if(numpats > 32)
 	orgptr += sixdepak(secptr,orgptr,len[3]); secptr += len[3] / 2;
-      if(ch.numpats > 48)
+      if(numpats > 48)
 	sixdepak(secptr,orgptr,len[4]);
     } else {
-      if(ch.numpats > 8)
+      if(numpats > 8)
 	orgptr += sixdepak(secptr,orgptr,len[2]); secptr += len[2] / 2;
-      if(ch.numpats > 16)
+      if(numpats > 16)
 	orgptr += sixdepak(secptr,orgptr,len[3]); secptr += len[3] / 2;
-      if(ch.numpats > 24)
+      if(numpats > 24)
 	orgptr += sixdepak(secptr,orgptr,len[4]); secptr += len[4] / 2;
-      if(ch.numpats > 32)
+      if(numpats > 32)
 	orgptr += sixdepak(secptr,orgptr,len[5]); secptr += len[5] / 2;
-      if(ch.numpats > 40)
+      if(numpats > 40)
 	orgptr += sixdepak(secptr,orgptr,len[6]); secptr += len[6] / 2;
-      if(ch.numpats > 48)
+      if(numpats > 48)
 	orgptr += sixdepak(secptr,orgptr,len[7]); secptr += len[7] / 2;
-      if(ch.numpats > 56)
+      if(numpats > 56)
 	sixdepak(secptr,orgptr,len[8]);
     }
     delete [] secdata; secdata = 0;
@@ -183,69 +182,100 @@
     for(l=0;l<alength;l++) org[l] = f->readInt(1);
   }
 
-  for(i=0;i<64*9;i++)		// patterns
-    trackord[i/9][i%9] = i+1;
-
-  if(ch.version == 1 || ch.version == 4) {
-    for(i=0;i<ch.numpats;i++)
+  if(version < 5) {
+    for(i=0;i<numpats;i++)
       for(j=0;j<64;j++)
 	for(k=0;k<9;k++) {
-	  tracks[i*9+k][j].note = org[i*64*t*4+j*t*4+k*4] == 255 ? 127 : org[i*64*t*4+j*t*4+k*4];
-	  tracks[i*9+k][j].inst = org[i*64*t*4+j*t*4+k*4+1];
-	  tracks[i*9+k][j].command = convfx[org[i*64*t*4+j*t*4+k*4+2]];
-	  tracks[i*9+k][j].param2 = org[i*64*t*4+j*t*4+k*4+3] & 0x0f;
-	  if(tracks[i*9+k][j].command != 14)
-	    tracks[i*9+k][j].param1 = org[i*64*t*4+j*t*4+k*4+3] >> 4;
+        struct Tracks *track = &tracks[i * 9 + k][j];
+        unsigned char *o = &org[i*64*t*4+j*t*4+k*4];
+
+        track->note = o[0] == 255 ? 127 : o[0];
+        track->inst = o[1];
+        track->command = convfx[o[2]];
+        track->param2 = o[3] & 0x0f;
+        if(track->command != 14)
+          track->param1 = o[3] >> 4;
 	  else {
-	    tracks[i*9+k][j].param1 = convinf1[org[i*64*t*4+j*t*4+k*4+3] >> 4];
-	    if(tracks[i*9+k][j].param1 == 15 && !tracks[i*9+k][j].param2) {	// convert key-off
-	      tracks[i*9+k][j].command = 8;
-	      tracks[i*9+k][j].param1 = 0;
-	      tracks[i*9+k][j].param2 = 0;
+          track->param1 = convinf1[o[3] >> 4];
+          if(track->param1 == 15 && !track->param2) { // convert key-off
+            track->command = 8;
+            track->param1 = 0;
+            track->param2 = 0;
 	    }
 	  }
-	  if(tracks[i*9+k][j].command == 14) {
-	    switch(tracks[i*9+k][j].param1) {
+        if(track->command == 14) {
+          switch(track->param1) {
 	    case 2: // convert define waveform
-	      tracks[i*9+k][j].command = 25;
-	      tracks[i*9+k][j].param1 = tracks[i*9+k][j].param2;
-	      tracks[i*9+k][j].param2 = 0xf;
+              track->command = 25;
+              track->param1 = track->param2;
+              track->param2 = 0xf;
 	      break;
 	    case 8: // convert volume slide up
-	      tracks[i*9+k][j].command = 26;
-	      tracks[i*9+k][j].param1 = tracks[i*9+k][j].param2;
-	      tracks[i*9+k][j].param2 = 0;
+              track->command = 26;
+              track->param1 = track->param2;
+              track->param2 = 0;
 	      break;
 	    case 9: // convert volume slide down
-	      tracks[i*9+k][j].command = 26;
-	      tracks[i*9+k][j].param1 = 0;
+              track->command = 26;
+              track->param1 = 0;
 	      break;
 	    }
 	  }
 	}
-  } else {
-    for(i=0;i<ch.numpats;i++)
-      for(j=0;j<9;j++)
+  } else {    // version >= 5
+    realloc_patterns(64, 64, 18);
+
+    for(i=0;i<numpats;i++)
+      for(j=0;j<18;j++)
 	for(k=0;k<64;k++) {
-	  tracks[i*9+j][k].note = org[i*64*t*4+j*64*4+k*4] == 255 ? 127 : org[i*64*t*4+j*64*4+k*4];
-	  tracks[i*9+j][k].inst = org[i*64*t*4+j*64*4+k*4+1];
-	  tracks[i*9+j][k].command = newconvfx[org[i*64*t*4+j*64*4+k*4+2]];
-	  tracks[i*9+j][k].param1 = org[i*64*t*4+j*64*4+k*4+3] >> 4;
-	  tracks[i*9+j][k].param2 = org[i*64*t*4+j*64*4+k*4+3] & 0x0f;
+          struct Tracks *track = &tracks[i * 18 + j][k];
+          unsigned char *o = &org[i*64*t*4+j*64*4+k*4];
+
+          track->note = o[0] == 255 ? 127 : o[0];
+          track->inst = o[1];
+          track->command = newconvfx[o[2]];
+          track->param1 = o[3] >> 4;
+          track->param2 = o[3] & 0x0f;
+ 
+          // Convert '&' command
+          if(o[2] == 36)
+            switch(track->param1) {
+            case 0:     // pattern delay (frames)
+              track->command = 29;
+              track->param1 = 0;
+              // param2 already set correctly
+              break;
+ 
+            case 1:     // pattern delay (rows)
+              track->command = 14;
+              track->param1 = 8;
+              // param2 already set correctly
+              break;
+            }
 	}
   }
 
-  if(ch.version == 1 || ch.version == 5)
+  init_trackord();
+
+  if(version == 1 || version == 5)
     {
-      delete [] org; org = 0; 
+      delete [] org;
     }
   else
     {
-      delete [] secdata; secdata = 0;
+      delete [] secdata;
     }
-  fp.close(f);
-  rewind(0);
-  return true;
+
+    // Process flags
+    if(version >= 5) {
+      CmodPlayer::flags |= Opl3;                                // All versions >= 5 are OPL3
+      if(flags & 8) CmodPlayer::flags |= Tremolo;               // Tremolo depth
+      if(flags & 16) CmodPlayer::flags |= Vibrato;      // Vibrato depth
+    }
+ 
+    fp.close(f);
+    rewind(0);
+    return true;
 }
 
 float Ca2mLoader::getrefresh()
--- a/src/adplug/core/a2m.h	Fri Apr 13 08:07:32 2007 -0700
+++ b/src/adplug/core/a2m.h	Fri Apr 13 09:09:50 2007 -0700
@@ -1,6 +1,6 @@
 /*
  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2002 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
@@ -19,6 +19,9 @@
  * a2m.h - A2M Loader by Simon Peter <dn.tlp@gmx.net>
  */
 
+#ifndef H_ADPLUG_A2MLOADER
+#define H_ADPLUG_A2MLOADER
+
 #include "protrack.h"
 
 class Ca2mLoader: public CmodPlayer
@@ -78,3 +81,5 @@
 		dad[ADPLUG_A2M_TWICEMAX+1], freq[ADPLUG_A2M_TWICEMAX+1], *wdbuf;
 	unsigned char *obuf, *buf;
 };
+#endif
+
--- a/src/adplug/core/amd.cxx	Fri Apr 13 08:07:32 2007 -0700
+++ b/src/adplug/core/amd.cxx	Fri Apr 13 09:09:50 2007 -0700
@@ -1,6 +1,6 @@
 /*
  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
@@ -130,7 +130,7 @@
 	fp.close(f);
 
 	// convert to protracker replay data
-	bpm = 50; restartpos = 0; activechan = 0xffff; flags = Decimal;
+	bpm = 50; restartpos = 0; flags = Decimal;
 	for(i=0;i<26;i++) {	// convert instruments
 		buf = inst[i].data[0];
 		buf2 = inst[i].data[1];
--- a/src/adplug/core/fmc.cxx	Fri Apr 13 08:07:32 2007 -0700
+++ b/src/adplug/core/fmc.cxx	Fri Apr 13 09:09:50 2007 -0700
@@ -1,6 +1,6 @@
 /*
   Adplug - Replayer for many OPL2/OPL3 audio file formats.
-  Copyright (C) 1999 - 2003 Simon Peter <dn.tlp@gmx.net>, et al.
+  Copyright (C) 1999 - 2007 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
@@ -149,7 +149,7 @@
 	}
 
 	// data for Protracker
-	activechan = (0xffff >> (16 - header.numchan)) << (16 - header.numchan);
+	activechan = (0xffffffff >> (32 - header.numchan)) << (32 - header.numchan);
 	nop = t / header.numchan;
 	restartpos = 0;
 
--- a/src/adplug/core/opl.h	Fri Apr 13 08:07:32 2007 -0700
+++ b/src/adplug/core/opl.h	Fri Apr 13 09:09:50 2007 -0700
@@ -1,6 +1,6 @@
 /*
  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
@@ -45,6 +45,11 @@
 	currChip = n;
     }
 
+  virtual int getchip()                         // returns current OPL chip
+    {
+      return currChip;
+    }
+
   virtual void init(void) = 0;			// reinitialize OPL chip(s)
 
   // return this OPL chip's type
--- a/src/adplug/core/player.cxx	Fri Apr 13 08:07:32 2007 -0700
+++ b/src/adplug/core/player.cxx	Fri Apr 13 09:09:50 2007 -0700
@@ -1,6 +1,6 @@
 /*
  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
--- a/src/adplug/core/player.h	Fri Apr 13 08:07:32 2007 -0700
+++ b/src/adplug/core/player.h	Fri Apr 13 09:09:50 2007 -0700
@@ -1,6 +1,6 @@
 /*
  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2004 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
@@ -77,7 +77,7 @@
 	CAdPlugDatabase	*db;	// AdPlug Database
 
 	static const unsigned short	note_table[12];	// standard adlib note table
-	static const unsigned char	op_table[9];	// the 9 operators as expected by the OPL2
+	static const unsigned char	op_table[9];	// the 9 operators as expected by the OPL
 };
 
 #endif
--- a/src/adplug/core/protrack.cxx	Fri Apr 13 08:07:32 2007 -0700
+++ b/src/adplug/core/protrack.cxx	Fri Apr 13 09:09:50 2007 -0700
@@ -1,6 +1,6 @@
 /*
  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
@@ -21,7 +21,7 @@
  * NOTES:
  * This is a generic Protracker-based formats player. It offers all Protracker
  * features, plus a good set of extensions to be compatible to other Protracker
- * derivatives. It is derived from the original SA2 player by me. If you got a
+ * derivatives. It is derived from the former SA2 player. If you got a
  * Protracker-like format, this is most certainly the player you want to use.
  */
 
@@ -43,7 +43,8 @@
 
 CmodPlayer::CmodPlayer(Copl *newopl)
   : CPlayer(newopl), inst(0), order(0), arplist(0), arpcmd(0), initspeed(6),
-    activechan(0xffff), flags(Standard), nop(0), nrows(0), npats(0), nchans(0)
+    nop(0), activechan(0xffffffff), flags(Standard), curchip(opl->getchip()),
+    nrows(0), npats(0), nchans(0)
 {
   realloc_order(128);
   realloc_patterns(64, 64, 9);
@@ -58,16 +59,18 @@
 
 bool CmodPlayer::update()
 {
-	unsigned char pattbreak=0,donote;		// remember vars
-	unsigned char pattnr,chan,info1,info2,info;	// cache vars
-	unsigned short track;
-	unsigned long row;
+  unsigned char		pattbreak=0, donote, pattnr, chan, oplchan, info1,
+    info2, info, pattern_delay;
+  unsigned short	track;
+  unsigned long		row;
 
 	if(!speed)		// song full stop
 		return !songend;
 
 	// effect handling (timer dependant)
-	for(chan=0;chan<nchans;chan++) {
+  for(chan = 0; chan < nchans; chan++) {
+    oplchan = set_opl_chip(chan);
+
 		if(arplist && arpcmd && inst[channel[chan].inst].arpstart)	// special arpeggio
 			if(channel[chan].arpspdcnt)
 				channel[chan].arpspdcnt--;
@@ -84,11 +87,11 @@
 					case 254: channel[chan].arppos = arplist[channel[chan].arppos]; break; // arpeggio loop
 					default: if(arpcmd[channel[chan].arppos]) {
 								if(arpcmd[channel[chan].arppos] / 10)
-									opl->write(0xe3 + op_table[chan], arpcmd[channel[chan].arppos] / 10 - 1);
+	      opl->write(0xe3 + op_table[oplchan], arpcmd[channel[chan].arppos] / 10 - 1);
 								if(arpcmd[channel[chan].arppos] % 10)
-									opl->write(0xe0 + op_table[chan], (arpcmd[channel[chan].arppos] % 10) - 1);
+	      opl->write(0xe0 + op_table[oplchan], (arpcmd[channel[chan].arppos] % 10) - 1);
 								if(arpcmd[channel[chan].arppos] < 10)	// ?????
-									opl->write(0xe0 + op_table[chan], arpcmd[channel[chan].arppos] - 1);
+	      opl->write(0xe0 + op_table[oplchan], arpcmd[channel[chan].arppos] - 1);
 							 }
 					}
 					if(arpcmd[channel[chan].arppos] != 252) {
@@ -185,19 +188,19 @@
 	}
 
 	// arrangement handling
-	if(ord >= length) {
-		songend = 1;				// set end-flag
-		ord = restartpos;
-	}
+  if(!resolve_order()) return !songend;
 	pattnr = order[ord];
 
         if(!rw) AdPlug_LogWrite("\nCmodPlayer::update(): Pattern: %d, Order: %d\n", pattnr, ord);
         AdPlug_LogWrite("CmodPlayer::update():%3d|", rw);
 
 	// play row
+  pattern_delay = 0;
 	row = rw;
-	for(chan=0;chan<nchans;chan++) {
-		if(!(activechan >> (15 - chan)) & 1) {	// channel active?
+  for(chan = 0; chan < nchans; chan++) {
+    oplchan = set_opl_chip(chan);
+
+    if(!(activechan >> (31 - chan)) & 1) {	// channel active?
                   AdPlug_LogWrite("N/A|");
 		  continue;
 		}
@@ -246,30 +249,36 @@
 		else
 			info = (channel[chan].info1 << 4) + channel[chan].info2;
 		switch(channel[chan].fx) {
-		case 3:	if(tracks[track][row].note) {					// tone portamento
-					if(tracks[track][row].note < 13)
-						channel[chan].nextfreq = notetable[tracks[track][row].note - 1];
-					else
-						if(tracks[track][row].note % 12 > 0)
-							channel[chan].nextfreq = notetable[(tracks[track][row].note % 12) - 1];
+    case 3: // tone portamento
+      if(tracks[track][row].note) {
+	if(tracks[track][row].note < 13)
+	  channel[chan].nextfreq = notetable[tracks[track][row].note - 1];
 						else
-							channel[chan].nextfreq = notetable[11];
-					channel[chan].nextoct = (tracks[track][row].note - 1) / 12;
-					if(tracks[track][row].note == 127) {	// handle key off
-						channel[chan].nextfreq = channel[chan].freq;
-						channel[chan].nextoct = channel[chan].oct;
+	  if(tracks[track][row].note % 12 > 0)
+	    channel[chan].nextfreq = notetable[(tracks[track][row].note % 12) - 1];
+	  else
+	    channel[chan].nextfreq = notetable[11];
+	channel[chan].nextoct = (tracks[track][row].note - 1) / 12;
+	if(tracks[track][row].note == 127) {	// handle key off
+	  channel[chan].nextfreq = channel[chan].freq;
+	  channel[chan].nextoct = channel[chan].oct;
+	}
 					}
-				}
 				if(info)		// remember vars
 					channel[chan].portainfo = info;
 				break;
-		case 4: if(info) {										// vibrato (remember vars)
-					channel[chan].vibinfo1 = info1;
-					channel[chan].vibinfo2 = info2;
-				}
+
+    case 4: // vibrato (remember vars)
+      if(info) {
+	channel[chan].vibinfo1 = info1;
+	channel[chan].vibinfo2 = info2;
+      }
 				break;
+
 		case 7: tempo = info; break;							// set tempo
+
 		case 8: channel[chan].key = 0; setfreq(chan); break;	// release sustaining note
+
 		case 9: // set carrier/modulator volume
 				if(info1)
 					channel[chan].vol1 = info1 * 7;
@@ -277,7 +286,10 @@
 					channel[chan].vol2 = info2 * 7;
 				setvolume(chan);
 				break;
-		case 11: pattbreak = 1; rw = 0; if(info < ord) songend = 1; ord = info; break; // position jump
+
+    case 11: // position jump
+      pattbreak = 1; rw = 0; if(info < ord) songend = 1; ord = info; break;
+
 		case 12: // set volume
 				channel[chan].vol1 = info;
 				channel[chan].vol2 = info;
@@ -287,35 +299,54 @@
 					channel[chan].vol2 = 63;
 				setvolume(chan);
 				break;
-		case 13: if(!pattbreak) { pattbreak = 1; rw = info; ord++; } break;	// pattern break
+
+    case 13: // pattern break
+      if(!pattbreak) { pattbreak = 1; rw = info; ord++; } break;
+
 		case 14: // extended command
 				switch(info1) {
-				case 0: if(info2)								// define cell-tremolo
-							regbd |= 128;
-						else
-							regbd &= 127;
+      case 0: // define cell-tremolo
+	if(info2)
+	  regbd |= 128;
+	else
+	  regbd &= 127;
 						opl->write(0xbd,regbd);
 						break;
-				case 1: if(info2)								// define cell-vibrato
-							regbd |= 64;
-						else
-							regbd &= 191;
+
+      case 1: // define cell-vibrato
+	if(info2)
+	  regbd |= 64;
+	else
+	  regbd &= 191;
 						opl->write(0xbd,regbd);
 						break;
-				case 4: vol_up_alt(chan,info2);					// increase volume fine
+
+      case 4: // increase volume fine
+	vol_up_alt(chan,info2);
 						setvolume(chan);
 						break;
-				case 5: vol_down_alt(chan,info2);				// decrease volume fine
+
+      case 5: // decrease volume fine
+	vol_down_alt(chan,info2);
 						setvolume(chan);
 						break;
-				case 6: slide_up(chan,info2);					// manual slide up
+
+      case 6: // manual slide up
+	slide_up(chan,info2);
 						setfreq(chan);
 						break;
-				case 7: slide_down(chan,info2);					// manual slide down
+
+      case 7: // manual slide down
+	slide_down(chan,info2);
 						setfreq(chan);
 						break;
+
+      case 8: // pattern delay (rows)
+	pattern_delay = info2 * speed;
+	break;
 				}
 				break;
+
 		case 15: // SA2 set speed
 			if(info <= 0x1f)
 				speed = info;
@@ -324,6 +355,7 @@
 			if(!info)
 				songend = 1;
 			break;
+
 		case 17: // alternate set volume
 			channel[chan].vol1 = info;
 			if(channel[chan].vol1 > 63)
@@ -336,15 +368,18 @@
 
 			setvolume(chan);
 			break;
+
 		case 18: // AMD set speed
 			if(info <= 31 && info > 0)
 				speed = info;
 			if(info > 31 || !info)
 				tempo = info;
 			break;
+
 		case 19: // RAD/A2M set speed
 			speed = (info ? info : info + 1);
 			break;
+
 		case 21: // set modulator volume
 			if(info <= 63)
 				channel[chan].vol2 = info;
@@ -352,6 +387,7 @@
 				channel[chan].vol2 = 63;
 			setvolume(chan);
 			break;
+
 		case 22: // set carrier volume
 			if(info <= 63)
 				channel[chan].vol1 = info;
@@ -359,35 +395,45 @@
 				channel[chan].vol1 = 63;
 			setvolume(chan);
 			break;
+
 		case 23: // fine frequency slide up
 			slide_up(chan,info);
 			setfreq(chan);
 			break;
+
 		case 24: // fine frequency slide down
 			slide_down(chan,info);
 			setfreq(chan);
 			break;
+
 		case 25: // set carrier/modulator waveform
 			if(info1 != 0x0f)
-				opl->write(0xe3 + op_table[chan],info1);
+	opl->write(0xe3 + op_table[oplchan],info1);
 			if(info2 != 0x0f)
-				opl->write(0xe0 + op_table[chan],info2);
+	opl->write(0xe0 + op_table[oplchan],info2);
 			break;
+
 		case 27: // set chip tremolo/vibrato
-			if (info1)
+      if(info1)
 				regbd |= 128;
 			else
 				regbd &= 127;
-			if (info2)
+      if(info2)
 				regbd |= 64;
 			else
 				regbd &= 191;
 			opl->write(0xbd,regbd);
 			break;
+
+    case 29: // pattern delay (frames)
+      pattern_delay = info;
+      break;
 		}
 	}
 
-	del = speed - 1;	// speed compensation
+  // speed compensation
+  del = speed - 1 + pattern_delay;
+
 	if(!pattbreak) {	// next row (only if no manual advance)
 		rw++;
 		if(rw >= nrows) {
@@ -395,16 +441,51 @@
 			ord++;
 		}
 	}
+
+  resolve_order();	// so we can report songend right away
+  AdPlug_LogWrite("\n");
+  return !songend;
+}
+
+unsigned char CmodPlayer::set_opl_chip(unsigned char chan)
+  /*
+   * Sets OPL chip according to channel number. Channels 0-8 are on first chip,
+   * channels 9-17 are on second chip. Returns corresponding OPL channel
+   * number.
+   */
+{
+  int newchip = chan < 9 ? 0 : 1;
+
+  if(newchip != curchip) {
+    opl->setchip(newchip);
+    curchip = newchip;
+  }
+
+  return chan % 9;
+}
+
+bool CmodPlayer::resolve_order()
+  /*
+   * Resolves current orderlist entry, checking for jumps and loops.
+   *
+   * Returns true on correct processing, false if immediate recursive loop
+   * has been detected.
+   */
+{
 	if(ord < length) {
-	  if(order[ord] >= JUMPMARKER) {	// jump to order
-	    ord = order[ord] - JUMPMARKER;
-	    songend = 1;
+    while(order[ord] >= JUMPMARKER) {	// jump to order
+      unsigned long neword = order[ord] - JUMPMARKER;
+
+      if(neword <= ord) songend = 1;
+      if(neword == ord) return false;
+      ord = neword;
 	  }
-	} else
+  } else {
 	  songend = 1;
+    ord = restartpos;
+  }
 
-        AdPlug_LogWrite("\n");
-	return !songend;
+  return true;
 }
 
 void CmodPlayer::rewind(int subsong)
@@ -424,7 +505,20 @@
       nop = (order[i] > nop ? order[i] : nop);
 
   opl->init();				// Reset OPL chip
-  opl->write(1,32);			// Go to ym3812 mode
+  opl->write(1, 32);	// Go to ym3812 mode
+
+  // Enable OPL3 extensions if flagged
+  if(flags & Opl3) {
+    opl->setchip(1);
+    opl->write(1, 32);
+    opl->write(5, 1);
+    opl->setchip(0);
+  }
+
+  // Enable tremolo/vibrato depth if flagged
+  if(flags & Tremolo) regbd |= 128;
+  if(flags & Vibrato) regbd |= 64;
+  if(regbd) opl->write(0xbd, regbd);
 }
 
 float CmodPlayer::getrefresh()
@@ -520,38 +614,44 @@
 
 void CmodPlayer::setvolume(unsigned char chan)
 {
-	if (flags & Faust)
+  unsigned char oplchan = set_opl_chip(chan);
+
+  if(flags & Faust)
         	setvolume_alt(chan);
 	else {
-		opl->write(0x40 + op_table[chan], 63-channel[chan].vol2 + (inst[channel[chan].inst].data[9] & 192));
-		opl->write(0x43 + op_table[chan], 63-channel[chan].vol1 + (inst[channel[chan].inst].data[10] & 192));
+    opl->write(0x40 + op_table[oplchan], 63-channel[chan].vol2 + (inst[channel[chan].inst].data[9] & 192));
+    opl->write(0x43 + op_table[oplchan], 63-channel[chan].vol1 + (inst[channel[chan].inst].data[10] & 192));
 	}
 }
 
 void CmodPlayer::setvolume_alt(unsigned char chan)
 {
+  unsigned char oplchan = set_opl_chip(chan);
     unsigned char ivol2 = inst[channel[chan].inst].data[9] & 63;
     unsigned char ivol1 = inst[channel[chan].inst].data[10] & 63;
 
-    opl->write(0x40 + op_table[chan], (((63 - channel[chan].vol2 & 63) + ivol2) >> 1) + (inst[channel[chan].inst].data[9] & 192));
-    opl->write(0x43 + op_table[chan], (((63 - channel[chan].vol1 & 63) + ivol1) >> 1) + (inst[channel[chan].inst].data[10] & 192));
+  opl->write(0x40 + op_table[oplchan], (((63 - channel[chan].vol2 & 63) + ivol2) >> 1) + (inst[channel[chan].inst].data[9] & 192));
+  opl->write(0x43 + op_table[oplchan], (((63 - channel[chan].vol1 & 63) + ivol1) >> 1) + (inst[channel[chan].inst].data[10] & 192));
 }
 
 void CmodPlayer::setfreq(unsigned char chan)
 {
-	opl->write(0xa0 + chan, channel[chan].freq & 255);
+  unsigned char oplchan = set_opl_chip(chan);
+
+  opl->write(0xa0 + oplchan, channel[chan].freq & 255);
 	if(channel[chan].key)
-		opl->write(0xb0 + chan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2) | 32);
+    opl->write(0xb0 + oplchan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2) | 32);
 	else
-		opl->write(0xb0 + chan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2));
+    opl->write(0xb0 + oplchan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2));
 }
 
 void CmodPlayer::playnote(unsigned char chan)
 {
-	unsigned char op = op_table[chan], insnr = channel[chan].inst;
+  unsigned char oplchan = set_opl_chip(chan);
+  unsigned char op = op_table[oplchan], insnr = channel[chan].inst;
 
 	if(!(flags & NoKeyOn))
-	  opl->write(0xb0 + chan, 0);	// stop old note
+    opl->write(0xb0 + oplchan, 0);	// stop old note
 
 	// set instrument data
 	opl->write(0x20 + op, inst[insnr].data[1]);
@@ -562,7 +662,7 @@
 	opl->write(0x83 + op, inst[insnr].data[6]);
 	opl->write(0xe0 + op, inst[insnr].data[7]);
 	opl->write(0xe3 + op, inst[insnr].data[8]);
-	opl->write(0xc0 + chan, inst[insnr].data[0]);
+  opl->write(0xc0 + oplchan, inst[insnr].data[0]);
 	opl->write(0xbd, inst[insnr].misc);	// set misc. register
 
 	// set frequency, volume & play
--- a/src/adplug/core/protrack.h	Fri Apr 13 08:07:32 2007 -0700
+++ b/src/adplug/core/protrack.h	Fri Apr 13 09:09:50 2007 -0700
@@ -1,6 +1,6 @@
 /*
  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2002 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
@@ -27,81 +27,93 @@
 class CmodPlayer: public CPlayer
 {
 public:
-	CmodPlayer(Copl *newopl);
-	virtual ~CmodPlayer();
+  CmodPlayer(Copl *newopl);
+  virtual ~CmodPlayer();
+
+  bool update();
+  void rewind(int subsong);
+  float getrefresh();
 
-	bool update();
-	void rewind(int subsong);
-	float getrefresh();
+  unsigned int getpatterns()
+    { return nop; }
+  unsigned int getpattern()
+    { return order[ord]; }
+  unsigned int getorders()
+    { return length; }
+  unsigned int getorder()
+    { return ord; }
+  unsigned int getrow()
+    { return rw; }
+  unsigned int getspeed()
+    { return speed; }
 
-	unsigned int getpatterns()
-	{ return nop; };
-	unsigned int getpattern()
-	{ return order[ord]; };
-	unsigned int getorders()
-	{ return length; };
-	unsigned int getorder()
-	{ return ord; };
-	unsigned int getrow()
-	{ return rw; };
-	unsigned int getspeed()
-	{ return speed; };
+ protected:
+  enum Flags {
+    Standard = 0,
+    Decimal = 1 << 0,
+    Faust = 1 << 1,
+    NoKeyOn = 1 << 2,
+    Opl3 = 1 << 3,
+    Tremolo = 1 << 4,
+    Vibrato = 1 << 5,
+    Percussion = 1 << 6
+  };
 
-protected:
-	enum Flags {Standard = 0, Decimal = 1, Faust = 2, NoKeyOn = 4};
-
-	struct Instrument {
-		unsigned char data[11],arpstart,arpspeed,arppos,arpspdcnt,misc;
-		signed char slide;
-	} *inst;
+  struct Instrument {
+    unsigned char data[11],arpstart,arpspeed,arppos,arpspdcnt,misc;
+    signed char slide;
+  } *inst;
 
-	struct Tracks {
-		unsigned char note,command,inst,param2,param1;
-	} **tracks;
-
-	unsigned char *order, *arplist, *arpcmd, initspeed;
-	unsigned short tempo, activechan, **trackord, bpm, flags, nop;
-	unsigned long length, restartpos;
+  struct Tracks {
+    unsigned char note,command,inst,param2,param1;
+  } **tracks;
 
-	struct Channel {
-	  unsigned short freq,nextfreq;
-	  unsigned char oct,vol1,vol2,inst,fx,info1,info2,key,nextoct,
-	    note,portainfo,vibinfo1,vibinfo2,arppos,arpspdcnt;
-	  signed char trigger;
-	} *channel;
+  unsigned char *order, *arplist, *arpcmd, initspeed;
+  unsigned short tempo, **trackord, bpm, nop;
+  unsigned long length, restartpos, activechan;
+  int flags, curchip;
 
-	void init_trackord();
-	bool init_specialarp();
-	void init_notetable(const unsigned short *newnotetable);
-	bool realloc_order(unsigned long len);
-	bool realloc_patterns(unsigned long pats, unsigned long rows, unsigned long chans);
-	bool realloc_instruments(unsigned long len);
+  struct Channel {
+    unsigned short freq,nextfreq;
+    unsigned char oct,vol1,vol2,inst,fx,info1,info2,key,nextoct,
+      note,portainfo,vibinfo1,vibinfo2,arppos,arpspdcnt;
+    signed char trigger;
+  } *channel;
 
-	void dealloc();
+  void init_trackord();
+  bool init_specialarp();
+  void init_notetable(const unsigned short *newnotetable);
+  bool realloc_order(unsigned long len);
+  bool realloc_patterns(unsigned long pats, unsigned long rows, unsigned long chans);
+  bool realloc_instruments(unsigned long len);
 
-private:
-	static const unsigned short sa2_notetable[12];
-	static const unsigned char vibratotab[32];
+  void dealloc();
 
-	unsigned char speed, del, songend, regbd;
-	unsigned short rows, notetable[12];
-	unsigned long rw, ord, nrows, npats, nchans;
+ private:
+  static const unsigned short sa2_notetable[12];
+  static const unsigned char vibratotab[32];
+
+  unsigned char speed, del, songend, regbd;
+  unsigned short rows, notetable[12];
+  unsigned long rw, ord, nrows, npats, nchans;
 
-	void setvolume(unsigned char chan);
-	void setvolume_alt(unsigned char chan);
-	void setfreq(unsigned char chan);
-	void playnote(unsigned char chan);
-	void setnote(unsigned char chan, int note);
-	void slide_down(unsigned char chan, int amount);
-	void slide_up(unsigned char chan, int amount);
-	void tone_portamento(unsigned char chan, unsigned char info);
-	void vibrato(unsigned char chan, unsigned char speed, unsigned char depth);
-	void vol_up(unsigned char chan, int amount);
-	void vol_down(unsigned char chan, int amount);
-	void vol_up_alt(unsigned char chan, int amount);
-	void vol_down_alt(unsigned char chan, int amount);
+  void setvolume(unsigned char chan);
+  void setvolume_alt(unsigned char chan);
+  void setfreq(unsigned char chan);
+  void playnote(unsigned char chan);
+  void setnote(unsigned char chan, int note);
+  void slide_down(unsigned char chan, int amount);
+  void slide_up(unsigned char chan, int amount);
+  void tone_portamento(unsigned char chan, unsigned char info);
+  void vibrato(unsigned char chan, unsigned char speed, unsigned char depth);
+  void vol_up(unsigned char chan, int amount);
+  void vol_down(unsigned char chan, int amount);
+  void vol_up_alt(unsigned char chan, int amount);
+  void vol_down_alt(unsigned char chan, int amount);
 
-	void dealloc_patterns();
+  void dealloc_patterns();
+  bool resolve_order();
+  unsigned char set_opl_chip(unsigned char chan);
 };
 
 #endif
--- a/src/adplug/core/rad.cxx	Fri Apr 13 08:07:32 2007 -0700
+++ b/src/adplug/core/rad.cxx	Fri Apr 13 09:09:50 2007 -0700
@@ -1,6 +1,6 @@
 /*
  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
@@ -108,7 +108,7 @@
 				tracks[i][j].note++;
 			tracks[i][j].command = convfx[tracks[i][j].command];
 		}
-	restartpos = 0; activechan = 0xffff; initspeed = radflags & 31;
+	restartpos = 0; initspeed = radflags & 31;
 	bpm = radflags & 64 ? 0 : 50; flags = Decimal;
 
 	rewind(0);
--- a/src/adplug/core/sa2.cxx	Fri Apr 13 08:07:32 2007 -0700
+++ b/src/adplug/core/sa2.cxx	Fri Apr 13 09:09:50 2007 -0700
@@ -1,6 +1,6 @@
 /*
  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
@@ -155,9 +155,7 @@
 	}
 
 	if(sat_type & HAS_ACTIVECHANNELS)
-	        activechan = f->readInt(2);		// active channels
-	else
-		activechan = 0xffff;
+	        activechan = f->readInt(2) << 16;	// active channels
 
         AdPlug_LogWrite("Csa2Loader::load(\"%s\"): sat_type = %x, nop = %d, "
 		 "length = %d, restartpos = %d, activechan = %x, bpm = %d\n",