[Scummvm-cvs-logs] scummvm master -> f4cc45d367442f850baecd814003ec09790a43b0

bluegr bluegr at gmail.com
Sat Mar 2 13:12:18 CET 2013


This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
8884c240fc MT32: Update copyright year
f4cc45d367 MT32: Sync with the latest changes in munt


Commit: 8884c240fc4683975cd76802b600c019ebcb260c
    https://github.com/scummvm/scummvm/commit/8884c240fc4683975cd76802b600c019ebcb260c
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2013-03-02T03:46:11-08:00

Commit Message:
MT32: Update copyright year

Changed paths:
    audio/softsynth/mt32/AReverbModel.cpp
    audio/softsynth/mt32/AReverbModel.h
    audio/softsynth/mt32/BReverbModel.cpp
    audio/softsynth/mt32/BReverbModel.h
    audio/softsynth/mt32/DelayReverb.cpp
    audio/softsynth/mt32/DelayReverb.h
    audio/softsynth/mt32/FreeverbModel.cpp
    audio/softsynth/mt32/FreeverbModel.h
    audio/softsynth/mt32/LA32Ramp.cpp
    audio/softsynth/mt32/LA32Ramp.h
    audio/softsynth/mt32/Part.cpp
    audio/softsynth/mt32/Part.h
    audio/softsynth/mt32/Partial.cpp
    audio/softsynth/mt32/Partial.h
    audio/softsynth/mt32/PartialManager.cpp
    audio/softsynth/mt32/PartialManager.h
    audio/softsynth/mt32/Poly.cpp
    audio/softsynth/mt32/Poly.h
    audio/softsynth/mt32/ROMInfo.cpp
    audio/softsynth/mt32/ROMInfo.h
    audio/softsynth/mt32/Structures.h
    audio/softsynth/mt32/Synth.cpp
    audio/softsynth/mt32/Synth.h
    audio/softsynth/mt32/TVA.cpp
    audio/softsynth/mt32/TVA.h
    audio/softsynth/mt32/TVF.cpp
    audio/softsynth/mt32/TVF.h
    audio/softsynth/mt32/TVP.cpp
    audio/softsynth/mt32/TVP.h
    audio/softsynth/mt32/Tables.cpp
    audio/softsynth/mt32/Tables.h
    audio/softsynth/mt32/mmath.h
    audio/softsynth/mt32/mt32emu.h



diff --git a/audio/softsynth/mt32/AReverbModel.cpp b/audio/softsynth/mt32/AReverbModel.cpp
index 595b286..1d63832 100644
--- a/audio/softsynth/mt32/AReverbModel.cpp
+++ b/audio/softsynth/mt32/AReverbModel.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/AReverbModel.h b/audio/softsynth/mt32/AReverbModel.h
index be1ca49..c992478 100644
--- a/audio/softsynth/mt32/AReverbModel.h
+++ b/audio/softsynth/mt32/AReverbModel.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp
index 2570424..cc0219b 100644
--- a/audio/softsynth/mt32/BReverbModel.cpp
+++ b/audio/softsynth/mt32/BReverbModel.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h
index f728d1a..d6fcb73 100644
--- a/audio/softsynth/mt32/BReverbModel.h
+++ b/audio/softsynth/mt32/BReverbModel.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/DelayReverb.cpp b/audio/softsynth/mt32/DelayReverb.cpp
index bf925f8..d80c98a 100644
--- a/audio/softsynth/mt32/DelayReverb.cpp
+++ b/audio/softsynth/mt32/DelayReverb.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/DelayReverb.h b/audio/softsynth/mt32/DelayReverb.h
index 1abb49f..c800383 100644
--- a/audio/softsynth/mt32/DelayReverb.h
+++ b/audio/softsynth/mt32/DelayReverb.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/FreeverbModel.cpp b/audio/softsynth/mt32/FreeverbModel.cpp
index 2ed302f..bd9c70b 100644
--- a/audio/softsynth/mt32/FreeverbModel.cpp
+++ b/audio/softsynth/mt32/FreeverbModel.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/FreeverbModel.h b/audio/softsynth/mt32/FreeverbModel.h
index bf1102b..5ea11f1 100644
--- a/audio/softsynth/mt32/FreeverbModel.h
+++ b/audio/softsynth/mt32/FreeverbModel.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/LA32Ramp.cpp b/audio/softsynth/mt32/LA32Ramp.cpp
index 4e4d6b4..c2b4596 100644
--- a/audio/softsynth/mt32/LA32Ramp.cpp
+++ b/audio/softsynth/mt32/LA32Ramp.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/LA32Ramp.h b/audio/softsynth/mt32/LA32Ramp.h
index ae937eb..8f55941 100644
--- a/audio/softsynth/mt32/LA32Ramp.h
+++ b/audio/softsynth/mt32/LA32Ramp.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp
index cd38589..62ba346 100644
--- a/audio/softsynth/mt32/Part.cpp
+++ b/audio/softsynth/mt32/Part.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/Part.h b/audio/softsynth/mt32/Part.h
index e5be41f..b658588 100644
--- a/audio/softsynth/mt32/Part.h
+++ b/audio/softsynth/mt32/Part.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp
index a4d1ab0..0ef2f62 100644
--- a/audio/softsynth/mt32/Partial.cpp
+++ b/audio/softsynth/mt32/Partial.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/Partial.h b/audio/softsynth/mt32/Partial.h
index 5e25076..9166beb 100644
--- a/audio/softsynth/mt32/Partial.h
+++ b/audio/softsynth/mt32/Partial.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp
index f8c2dbc..436e7a3 100644
--- a/audio/softsynth/mt32/PartialManager.cpp
+++ b/audio/softsynth/mt32/PartialManager.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/PartialManager.h b/audio/softsynth/mt32/PartialManager.h
index bb78672..a1c9266 100644
--- a/audio/softsynth/mt32/PartialManager.h
+++ b/audio/softsynth/mt32/PartialManager.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp
index 46e30c0..46574f8 100644
--- a/audio/softsynth/mt32/Poly.cpp
+++ b/audio/softsynth/mt32/Poly.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/Poly.h b/audio/softsynth/mt32/Poly.h
index e25b6d8..068cf73 100644
--- a/audio/softsynth/mt32/Poly.h
+++ b/audio/softsynth/mt32/Poly.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/ROMInfo.cpp b/audio/softsynth/mt32/ROMInfo.cpp
index 514bc23..9d05420 100644
--- a/audio/softsynth/mt32/ROMInfo.cpp
+++ b/audio/softsynth/mt32/ROMInfo.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/ROMInfo.h b/audio/softsynth/mt32/ROMInfo.h
index 2ffd2b7..1d837dc 100644
--- a/audio/softsynth/mt32/ROMInfo.h
+++ b/audio/softsynth/mt32/ROMInfo.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/Structures.h b/audio/softsynth/mt32/Structures.h
index cbce89a..43d2d1f 100644
--- a/audio/softsynth/mt32/Structures.h
+++ b/audio/softsynth/mt32/Structures.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp
index 7f4ba44..6099b98 100644
--- a/audio/softsynth/mt32/Synth.cpp
+++ b/audio/softsynth/mt32/Synth.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h
index 91375c0..70e0064 100644
--- a/audio/softsynth/mt32/Synth.h
+++ b/audio/softsynth/mt32/Synth.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp
index 19a01cf..65e5256 100644
--- a/audio/softsynth/mt32/TVA.cpp
+++ b/audio/softsynth/mt32/TVA.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/TVA.h b/audio/softsynth/mt32/TVA.h
index a104fe4..e6e5cc4 100644
--- a/audio/softsynth/mt32/TVA.h
+++ b/audio/softsynth/mt32/TVA.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/TVF.cpp b/audio/softsynth/mt32/TVF.cpp
index 47c4917..49a0d85 100644
--- a/audio/softsynth/mt32/TVF.cpp
+++ b/audio/softsynth/mt32/TVF.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/TVF.h b/audio/softsynth/mt32/TVF.h
index 490d8de..22d81da 100644
--- a/audio/softsynth/mt32/TVF.h
+++ b/audio/softsynth/mt32/TVF.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp
index 5dc4ca6..c3e64c1 100644
--- a/audio/softsynth/mt32/TVP.cpp
+++ b/audio/softsynth/mt32/TVP.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/TVP.h b/audio/softsynth/mt32/TVP.h
index f6f62f8..cd5fb4c 100644
--- a/audio/softsynth/mt32/TVP.h
+++ b/audio/softsynth/mt32/TVP.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/Tables.cpp b/audio/softsynth/mt32/Tables.cpp
index 5353a74..c7e9437 100644
--- a/audio/softsynth/mt32/Tables.cpp
+++ b/audio/softsynth/mt32/Tables.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h
index c3e80e7..728d7ec 100644
--- a/audio/softsynth/mt32/Tables.h
+++ b/audio/softsynth/mt32/Tables.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/mmath.h b/audio/softsynth/mt32/mmath.h
index 25c79d5..f67433b 100644
--- a/audio/softsynth/mt32/mmath.h
+++ b/audio/softsynth/mt32/mmath.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by
diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h
index ae5f495..6812f7b 100644
--- a/audio/softsynth/mt32/mt32emu.h
+++ b/audio/softsynth/mt32/mt32emu.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License as published by


Commit: f4cc45d367442f850baecd814003ec09790a43b0
    https://github.com/scummvm/scummvm/commit/f4cc45d367442f850baecd814003ec09790a43b0
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2013-03-02T04:09:39-08:00

Commit Message:
MT32: Sync with the latest changes in munt

The major change is the addition of a refined wave generator based on
logarithmic fixed-point computations and LUTs

Changed paths:
  A audio/softsynth/mt32/LA32WaveGenerator.cpp
  A audio/softsynth/mt32/LA32WaveGenerator.h
  A audio/softsynth/mt32/LegacyWaveGenerator.cpp
  A audio/softsynth/mt32/LegacyWaveGenerator.h
    audio/softsynth/mt32/LA32Ramp.cpp
    audio/softsynth/mt32/Partial.cpp
    audio/softsynth/mt32/Partial.h
    audio/softsynth/mt32/Synth.cpp
    audio/softsynth/mt32/Synth.h
    audio/softsynth/mt32/Tables.cpp
    audio/softsynth/mt32/Tables.h
    audio/softsynth/mt32/mmath.h
    audio/softsynth/mt32/module.mk
    audio/softsynth/mt32/mt32emu.h



diff --git a/audio/softsynth/mt32/LA32Ramp.cpp b/audio/softsynth/mt32/LA32Ramp.cpp
index c2b4596..b4ac6f1 100644
--- a/audio/softsynth/mt32/LA32Ramp.cpp
+++ b/audio/softsynth/mt32/LA32Ramp.cpp
@@ -82,9 +82,13 @@ void LA32Ramp::startRamp(Bit8u target, Bit8u increment) {
 	if (increment == 0) {
 		largeIncrement = 0;
 	} else {
-		// Using integer argument here, no precision loss:
+		// Three bits in the fractional part, no need to interpolate
 		// (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f)
-		largeIncrement = (unsigned int)(EXP2I(((increment & 0x7F) + 24) << 9) + 0.125f);
+		Bit32u expArg = increment & 0x7F;
+		largeIncrement = 8191 - Tables::getInstance().exp9[~(expArg << 6) & 511];
+		largeIncrement <<= expArg >> 3;
+		largeIncrement += 64;
+		largeIncrement >>= 9;
 	}
 	descending = (increment & 0x80) != 0;
 	if (descending) {
diff --git a/audio/softsynth/mt32/LA32WaveGenerator.cpp b/audio/softsynth/mt32/LA32WaveGenerator.cpp
new file mode 100644
index 0000000..74dba78
--- /dev/null
+++ b/audio/softsynth/mt32/LA32WaveGenerator.cpp
@@ -0,0 +1,417 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ *  This program 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 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 Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#include <cmath>
+#include "mt32emu.h"
+#include "mmath.h"
+#include "LA32WaveGenerator.h"
+
+#if MT32EMU_ACCURATE_WG == 0
+
+namespace MT32Emu {
+
+static const Bit32u SINE_SEGMENT_RELATIVE_LENGTH = 1 << 18;
+static const Bit32u MIDDLE_CUTOFF_VALUE = 128 << 18;
+static const Bit32u RESONANCE_DECAY_THRESHOLD_CUTOFF_VALUE = 144 << 18;
+static const Bit32u MAX_CUTOFF_VALUE = 240 << 18;
+static const LogSample SILENCE = {65535, LogSample::POSITIVE};
+
+Bit16u LA32Utilites::interpolateExp(const Bit16u fract) {
+	Bit16u expTabIndex = fract >> 3;
+	Bit16u extraBits = fract & 7;
+	Bit16u expTabEntry2 = 8191 - Tables::getInstance().exp9[expTabIndex];
+	Bit16u expTabEntry1 = expTabIndex == 0 ? 8191 : (8191 - Tables::getInstance().exp9[expTabIndex - 1]);
+	return expTabEntry1 + (((expTabEntry2 - expTabEntry1) * extraBits) >> 3);
+}
+
+Bit16s LA32Utilites::unlog(const LogSample &logSample) {
+	//Bit16s sample = (Bit16s)EXP2F(13.0f - logSample.logValue / 1024.0f);
+	Bit32u intLogValue = logSample.logValue >> 12;
+	Bit16u fracLogValue = logSample.logValue & 4095;
+	Bit16s sample = interpolateExp(fracLogValue) >> intLogValue;
+	return logSample.sign == LogSample::POSITIVE ? sample : -sample;
+}
+
+void LA32Utilites::addLogSamples(LogSample &logSample1, const LogSample &logSample2) {
+	Bit32u logSampleValue = logSample1.logValue + logSample2.logValue;
+	logSample1.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535;
+	logSample1.sign = logSample1.sign == logSample2.sign ? LogSample::POSITIVE : LogSample::NEGATIVE;
+}
+
+Bit32u LA32WaveGenerator::getSampleStep() {
+	// sampleStep = EXP2F(pitch / 4096.0f + 4.0f)
+	Bit32u sampleStep = LA32Utilites::interpolateExp(~pitch & 4095);
+	sampleStep <<= pitch >> 12;
+	sampleStep >>= 8;
+	return sampleStep;
+}
+
+Bit32u LA32WaveGenerator::getResonanceWaveLengthFactor(Bit32u effectiveCutoffValue) {
+	// resonanceWaveLengthFactor = (Bit32u)EXP2F(12.0f + effectiveCutoffValue / 4096.0f);
+	Bit32u resonanceWaveLengthFactor = LA32Utilites::interpolateExp(~effectiveCutoffValue & 4095);
+	resonanceWaveLengthFactor <<= effectiveCutoffValue >> 12;
+	return resonanceWaveLengthFactor;
+}
+
+Bit32u LA32WaveGenerator::getHighLinearLength(Bit32u effectiveCutoffValue) {
+	// Ratio of positive segment to wave length
+	Bit32u effectivePulseWidthValue = 0;
+	if (pulseWidth > 128) {
+		effectivePulseWidthValue = (pulseWidth - 128) << 6;
+	}
+
+	Bit32u highLinearLength = 0;
+	// highLinearLength = EXP2F(19.0f - effectivePulseWidthValue / 4096.0f + effectiveCutoffValue / 4096.0f) - 2 * SINE_SEGMENT_RELATIVE_LENGTH;
+	if (effectivePulseWidthValue < effectiveCutoffValue) {
+		Bit32u expArg = effectiveCutoffValue - effectivePulseWidthValue;
+		highLinearLength = LA32Utilites::interpolateExp(~expArg & 4095);
+		highLinearLength <<= 7 + (expArg >> 12);
+		highLinearLength -= 2 * SINE_SEGMENT_RELATIVE_LENGTH;
+	}
+	return highLinearLength;
+}
+
+void LA32WaveGenerator::computePositions(Bit32u highLinearLength, Bit32u lowLinearLength, Bit32u resonanceWaveLengthFactor) {
+	// Assuming 12-bit multiplication used here
+	squareWavePosition = resonanceSinePosition = (wavePosition >> 8) * (resonanceWaveLengthFactor >> 4);
+	if (squareWavePosition < SINE_SEGMENT_RELATIVE_LENGTH) {
+		phase = POSITIVE_RISING_SINE_SEGMENT;
+		return;
+	}
+	squareWavePosition -= SINE_SEGMENT_RELATIVE_LENGTH;
+	if (squareWavePosition < highLinearLength) {
+		phase = POSITIVE_LINEAR_SEGMENT;
+		return;
+	}
+	squareWavePosition -= highLinearLength;
+	if (squareWavePosition < SINE_SEGMENT_RELATIVE_LENGTH) {
+		phase = POSITIVE_FALLING_SINE_SEGMENT;
+		return;
+	}
+	squareWavePosition -= SINE_SEGMENT_RELATIVE_LENGTH;
+	resonanceSinePosition = squareWavePosition;
+	if (squareWavePosition < SINE_SEGMENT_RELATIVE_LENGTH) {
+		phase = NEGATIVE_FALLING_SINE_SEGMENT;
+		return;
+	}
+	squareWavePosition -= SINE_SEGMENT_RELATIVE_LENGTH;
+	if (squareWavePosition < lowLinearLength) {
+		phase = NEGATIVE_LINEAR_SEGMENT;
+		return;
+	}
+	squareWavePosition -= lowLinearLength;
+	phase = NEGATIVE_RISING_SINE_SEGMENT;
+}
+
+void LA32WaveGenerator::advancePosition() {
+	wavePosition += getSampleStep();
+	wavePosition %= 4 * SINE_SEGMENT_RELATIVE_LENGTH;
+
+	Bit32u effectiveCutoffValue = (cutoffVal > MIDDLE_CUTOFF_VALUE) ? (cutoffVal - MIDDLE_CUTOFF_VALUE) >> 10 : 0;
+	Bit32u resonanceWaveLengthFactor = getResonanceWaveLengthFactor(effectiveCutoffValue);
+	Bit32u highLinearLength = getHighLinearLength(effectiveCutoffValue);
+	Bit32u lowLinearLength = (resonanceWaveLengthFactor << 8) - 4 * SINE_SEGMENT_RELATIVE_LENGTH - highLinearLength;
+	computePositions(highLinearLength, lowLinearLength, resonanceWaveLengthFactor);
+
+	// resonancePhase computation hack
+	*(int*)&resonancePhase = ((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3;
+}
+
+void LA32WaveGenerator::generateNextSquareWaveLogSample() {
+	Bit32u logSampleValue;
+	switch (phase) {
+		case POSITIVE_RISING_SINE_SEGMENT:
+		case NEGATIVE_FALLING_SINE_SEGMENT:
+			logSampleValue = Tables::getInstance().logsin9[(squareWavePosition >> 9) & 511];
+			break;
+		case POSITIVE_FALLING_SINE_SEGMENT:
+		case NEGATIVE_RISING_SINE_SEGMENT:
+			logSampleValue = Tables::getInstance().logsin9[~(squareWavePosition >> 9) & 511];
+			break;
+		case POSITIVE_LINEAR_SEGMENT:
+		case NEGATIVE_LINEAR_SEGMENT:
+		default:
+			logSampleValue = 0;
+			break;
+	}
+	logSampleValue <<= 2;
+	logSampleValue += amp >> 10;
+	if (cutoffVal < MIDDLE_CUTOFF_VALUE) {
+		logSampleValue += (MIDDLE_CUTOFF_VALUE - cutoffVal) >> 9;
+	}
+
+	squareLogSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535;
+	squareLogSample.sign = phase < NEGATIVE_FALLING_SINE_SEGMENT ? LogSample::POSITIVE : LogSample::NEGATIVE;
+}
+
+void LA32WaveGenerator::generateNextResonanceWaveLogSample() {
+	Bit32u logSampleValue;
+	if (resonancePhase == POSITIVE_FALLING_RESONANCE_SINE_SEGMENT || resonancePhase == NEGATIVE_RISING_RESONANCE_SINE_SEGMENT) {
+		logSampleValue = Tables::getInstance().logsin9[~(resonanceSinePosition >> 9) & 511];
+	} else {
+		logSampleValue = Tables::getInstance().logsin9[(resonanceSinePosition >> 9) & 511];
+	}
+	logSampleValue <<= 2;
+	logSampleValue += amp >> 10;
+
+	// From the digital captures, the decaying speed of the resonance sine is found a bit different for the positive and the negative segments
+	Bit32u decayFactor = phase < NEGATIVE_FALLING_SINE_SEGMENT ? resAmpDecayFactor : resAmpDecayFactor + 1;
+	// Unsure about resonanceSinePosition here. It's possible that dedicated counter & decrement are used. Although, cutoff is finely ramped, so maybe not.
+	logSampleValue += resonanceAmpSubtraction + (((resonanceSinePosition >> 4) * decayFactor) >> 8);
+
+	// To ensure the output wave has no breaks, two different windows are appied to the beginning and the ending of the resonance sine segment
+	if (phase == POSITIVE_RISING_SINE_SEGMENT || phase == NEGATIVE_FALLING_SINE_SEGMENT) {
+		// The window is synchronous sine here
+		logSampleValue += Tables::getInstance().logsin9[(squareWavePosition >> 9) & 511] << 2;
+	} else if (phase == POSITIVE_FALLING_SINE_SEGMENT || phase == NEGATIVE_RISING_SINE_SEGMENT) {
+		// The window is synchronous square sine here
+		logSampleValue += Tables::getInstance().logsin9[~(squareWavePosition >> 9) & 511] << 3;
+	}
+
+	if (cutoffVal < MIDDLE_CUTOFF_VALUE) {
+		// For the cutoff values below the cutoff middle point, it seems the amp of the resonance wave is expotentially decayed
+		logSampleValue += 31743 + ((MIDDLE_CUTOFF_VALUE - cutoffVal) >> 9);
+	} else if (cutoffVal < RESONANCE_DECAY_THRESHOLD_CUTOFF_VALUE) {
+		// For the cutoff values below this point, the amp of the resonance wave is sinusoidally decayed
+		Bit32u sineIx = (cutoffVal - MIDDLE_CUTOFF_VALUE) >> 13;
+		logSampleValue += Tables::getInstance().logsin9[sineIx] << 2;
+	}
+
+	// After all the amp decrements are added, it should be safe now to adjust the amp of the resonance wave to what we see on captures
+	logSampleValue -= 1 << 12;
+
+	resonanceLogSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535;
+	resonanceLogSample.sign = resonancePhase < NEGATIVE_FALLING_RESONANCE_SINE_SEGMENT ? LogSample::POSITIVE : LogSample::NEGATIVE;
+}
+
+void LA32WaveGenerator::generateNextSawtoothCosineLogSample(LogSample &logSample) const {
+	Bit32u sawtoothCosinePosition = wavePosition + (1 << 18);
+	if ((sawtoothCosinePosition & (1 << 18)) > 0) {
+		logSample.logValue = Tables::getInstance().logsin9[~(sawtoothCosinePosition >> 9) & 511];
+	} else {
+		logSample.logValue = Tables::getInstance().logsin9[(sawtoothCosinePosition >> 9) & 511];
+	}
+	logSample.logValue <<= 2;
+	logSample.sign = ((sawtoothCosinePosition & (1 << 19)) == 0) ? LogSample::POSITIVE : LogSample::NEGATIVE;
+}
+
+void LA32WaveGenerator::pcmSampleToLogSample(LogSample &logSample, const Bit16s pcmSample) const {
+	Bit32u logSampleValue = (32787 - (pcmSample & 32767)) << 1;
+	logSampleValue += amp >> 10;
+	logSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535;
+	logSample.sign = pcmSample < 0 ? LogSample::NEGATIVE : LogSample::POSITIVE;
+}
+
+void LA32WaveGenerator::generateNextPCMWaveLogSamples() {
+	// This should emulate the ladder we see in the PCM captures for pitches 01, 02, 07, etc.
+	// The most probable cause is the factor in the interpolation formula is one bit less
+	// accurate than the sample position counter
+	pcmInterpolationFactor = (wavePosition & 255) >> 1;
+	Bit32u pcmWaveTableIx = wavePosition >> 8;
+	pcmSampleToLogSample(firstPCMLogSample, pcmWaveAddress[pcmWaveTableIx]);
+	if (pcmWaveInterpolated) {
+		pcmWaveTableIx++;
+		if (pcmWaveTableIx < pcmWaveLength) {
+			pcmSampleToLogSample(secondPCMLogSample, pcmWaveAddress[pcmWaveTableIx]);
+		} else {
+			if (pcmWaveLooped) {
+				pcmWaveTableIx -= pcmWaveLength;
+				pcmSampleToLogSample(secondPCMLogSample, pcmWaveAddress[pcmWaveTableIx]);
+			} else {
+				secondPCMLogSample = SILENCE;
+			}
+		}
+	} else {
+		secondPCMLogSample = SILENCE;
+	}
+	// pcmSampleStep = EXP2F(pitch / 4096. - 5.);
+	Bit32u pcmSampleStep = LA32Utilites::interpolateExp(~pitch & 4095);
+	pcmSampleStep <<= pitch >> 12;
+	// Seeing the actual lengths of the PCM wave for pitches 00..12,
+	// the pcmPosition counter can be assumed to have 8-bit fractions
+	pcmSampleStep >>= 9;
+	wavePosition += pcmSampleStep;
+	if (wavePosition >= (pcmWaveLength << 8)) {
+		if (pcmWaveLooped) {
+			wavePosition -= pcmWaveLength << 8;
+		} else {
+			deactivate();
+		}
+	}
+}
+
+void LA32WaveGenerator::initSynth(const bool useSawtoothWaveform, const Bit8u usePulseWidth, const Bit8u useResonance) {
+	sawtoothWaveform = useSawtoothWaveform;
+	pulseWidth = usePulseWidth;
+	resonance = useResonance;
+
+	wavePosition = 0;
+
+	squareWavePosition = 0;
+	phase = POSITIVE_RISING_SINE_SEGMENT;
+
+	resonanceSinePosition = 0;
+	resonancePhase = POSITIVE_RISING_RESONANCE_SINE_SEGMENT;
+	resonanceAmpSubtraction = (32 - resonance) << 10;
+	resAmpDecayFactor = Tables::getInstance().resAmpDecayFactor[resonance >> 2] << 2;
+
+	pcmWaveAddress = NULL;
+	active = true;
+}
+
+void LA32WaveGenerator::initPCM(const Bit16s * const usePCMWaveAddress, const Bit32u usePCMWaveLength, const bool usePCMWaveLooped, const bool usePCMWaveInterpolated) {
+	pcmWaveAddress = usePCMWaveAddress;
+	pcmWaveLength = usePCMWaveLength;
+	pcmWaveLooped = usePCMWaveLooped;
+	pcmWaveInterpolated = usePCMWaveInterpolated;
+
+	wavePosition = 0;
+	active = true;
+}
+
+void LA32WaveGenerator::generateNextSample(const Bit32u useAmp, const Bit16u usePitch, const Bit32u useCutoffVal) {
+	if (!active) {
+		return;
+	}
+
+	amp = useAmp;
+	pitch = usePitch;
+
+	if (isPCMWave()) {
+		generateNextPCMWaveLogSamples();
+		return;
+	}
+
+	// The 240 cutoffVal limit was determined via sample analysis (internal Munt capture IDs: glop3, glop4).
+	// More research is needed to be sure that this is correct, however.
+	cutoffVal = (useCutoffVal > MAX_CUTOFF_VALUE) ? MAX_CUTOFF_VALUE : useCutoffVal;
+
+	generateNextSquareWaveLogSample();
+	generateNextResonanceWaveLogSample();
+	if (sawtoothWaveform) {
+		LogSample cosineLogSample;
+		generateNextSawtoothCosineLogSample(cosineLogSample);
+		LA32Utilites::addLogSamples(squareLogSample, cosineLogSample);
+		LA32Utilites::addLogSamples(resonanceLogSample, cosineLogSample);
+	}
+	advancePosition();
+}
+
+LogSample LA32WaveGenerator::getOutputLogSample(const bool first) const {
+	if (!isActive()) {
+		return SILENCE;
+	}
+	if (isPCMWave()) {
+		return first ? firstPCMLogSample : secondPCMLogSample;
+	}
+	return first ? squareLogSample : resonanceLogSample;
+}
+
+void LA32WaveGenerator::deactivate() {
+	active = false;
+}
+
+bool LA32WaveGenerator::isActive() const {
+	return active;
+}
+
+bool LA32WaveGenerator::isPCMWave() const {
+	return pcmWaveAddress != NULL;
+}
+
+Bit32u LA32WaveGenerator::getPCMInterpolationFactor() const {
+	return pcmInterpolationFactor;
+}
+
+void LA32PartialPair::init(const bool useRingModulated, const bool useMixed) {
+	ringModulated = useRingModulated;
+	mixed = useMixed;
+}
+
+void LA32PartialPair::initSynth(const PairType useMaster, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) {
+	if (useMaster == MASTER) {
+		master.initSynth(sawtoothWaveform, pulseWidth, resonance);
+	} else {
+		slave.initSynth(sawtoothWaveform, pulseWidth, resonance);
+	}
+}
+
+void LA32PartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped) {
+	if (useMaster == MASTER) {
+		master.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, true);
+	} else {
+		slave.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, !ringModulated);
+	}
+}
+
+void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff) {
+	if (useMaster == MASTER) {
+		master.generateNextSample(amp, pitch, cutoff);
+	} else {
+		slave.generateNextSample(amp, pitch, cutoff);
+	}
+}
+
+Bit16s LA32PartialPair::unlogAndMixWGOutput(const LA32WaveGenerator &wg, const LogSample * const ringModulatingLogSample) {
+	if (!wg.isActive() || ((ringModulatingLogSample != NULL) && (ringModulatingLogSample->logValue == SILENCE.logValue))) {
+		return 0;
+	}
+	LogSample firstLogSample = wg.getOutputLogSample(true);
+	LogSample secondLogSample = wg.getOutputLogSample(false);
+	if (ringModulatingLogSample != NULL) {
+		LA32Utilites::addLogSamples(firstLogSample, *ringModulatingLogSample);
+		LA32Utilites::addLogSamples(secondLogSample, *ringModulatingLogSample);
+	}
+	Bit16s firstSample = LA32Utilites::unlog(firstLogSample);
+	Bit16s secondSample = LA32Utilites::unlog(secondLogSample);
+	if (wg.isPCMWave()) {
+		return Bit16s(firstSample + ((Bit32s(secondSample - firstSample) * wg.getPCMInterpolationFactor()) >> 7));
+	}
+	return firstSample + secondSample;
+}
+
+Bit16s LA32PartialPair::nextOutSample() {
+	if (ringModulated) {
+		LogSample slaveFirstLogSample = slave.getOutputLogSample(true);
+		LogSample slaveSecondLogSample = slave.getOutputLogSample(false);
+		Bit16s sample = unlogAndMixWGOutput(master, &slaveFirstLogSample);
+		if (!slave.isPCMWave()) {
+			sample += unlogAndMixWGOutput(master, &slaveSecondLogSample);
+		}
+		if (mixed) {
+			sample += unlogAndMixWGOutput(master, NULL);
+		}
+		return sample;
+	}
+	return unlogAndMixWGOutput(master, NULL) + unlogAndMixWGOutput(slave, NULL);
+}
+
+void LA32PartialPair::deactivate(const PairType useMaster) {
+	if (useMaster == MASTER) {
+		master.deactivate();
+	} else {
+		slave.deactivate();
+	}
+}
+
+bool LA32PartialPair::isActive(const PairType useMaster) const {
+	return useMaster == MASTER ? master.isActive() : slave.isActive();
+}
+
+}
+
+#endif // #if MT32EMU_ACCURATE_WG == 0
diff --git a/audio/softsynth/mt32/LA32WaveGenerator.h b/audio/softsynth/mt32/LA32WaveGenerator.h
new file mode 100644
index 0000000..37a4aea
--- /dev/null
+++ b/audio/softsynth/mt32/LA32WaveGenerator.h
@@ -0,0 +1,246 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ *  This program 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 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 Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if MT32EMU_ACCURATE_WG == 0
+
+#ifndef MT32EMU_LA32_WAVE_GENERATOR_H
+#define MT32EMU_LA32_WAVE_GENERATOR_H
+
+namespace MT32Emu {
+
+/**
+ * LA32 performs wave generation in the log-space that allows replacing multiplications by cheap additions
+ * It's assumed that only low-bit multiplications occur in a few places which are unavoidable like these:
+ * - interpolation of exponent table (obvious, a delta value has 4 bits)
+ * - computation of resonance amp decay envelope (the table contains values with 1-2 "1" bits except the very first value 31 but this case can be found using inversion)
+ * - interpolation of PCM samples (obvious, the wave position counter is in the linear space, there is no log() table in the chip)
+ * and it seems to be implemented in the same way as in the Boss chip, i.e. right shifted additions which involved noticeable precision loss
+ * Subtraction is supposed to be replaced by simple inversion
+ * As the logarithmic sine is always negative, all the logarithmic values are treated as decrements
+ */
+struct LogSample {
+	// 16-bit fixed point value, includes 12-bit fractional part
+	// 4-bit integer part allows to present any 16-bit sample in the log-space
+	// Obviously, the log value doesn't contain the sign of the resulting sample
+	Bit16u logValue;
+	enum {
+		POSITIVE,
+		NEGATIVE
+	} sign;
+};
+
+class LA32Utilites {
+public:
+	static Bit16u interpolateExp(const Bit16u fract);
+	static Bit16s unlog(const LogSample &logSample);
+	static void addLogSamples(LogSample &logSample1, const LogSample &logSample2);
+};
+
+/**
+ * LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator.
+ * The output square wave is created by adding high / low linear segments in-between
+ * the rising and falling cosine segments. Basically, it’s very similar to the phase distortion synthesis.
+ * Behaviour of a true resonance filter is emulated by adding decaying sine wave.
+ * The beginning and the ending of the resonant sine is multiplied by a cosine window.
+ * To synthesise sawtooth waves, the resulting square wave is multiplied by synchronous cosine wave.
+ */
+class LA32WaveGenerator {
+	//***************************************************************************
+	//  The local copy of partial parameters below
+	//***************************************************************************
+
+	bool active;
+
+	// True means the resulting square wave is to be multiplied by the synchronous cosine
+	bool sawtoothWaveform;
+
+	// Logarithmic amp of the wave generator
+	Bit32u amp;
+
+	// Logarithmic frequency of the resulting wave
+	Bit16u pitch;
+
+	// Values in range [1..31]
+	// Value 1 correspong to the minimum resonance
+	Bit8u resonance;
+
+	// Processed value in range [0..255]
+	// Values in range [0..128] have no effect and the resulting wave remains symmetrical
+	// Value 255 corresponds to the maximum possible asymmetric of the resulting wave
+	Bit8u pulseWidth;
+
+	// Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier
+	Bit32u cutoffVal;
+
+	// Logarithmic PCM sample start address
+	const Bit16s *pcmWaveAddress;
+
+	// Logarithmic PCM sample length
+	Bit32u pcmWaveLength;
+
+	// true for looped logarithmic PCM samples
+	bool pcmWaveLooped;
+
+	// false for slave PCM partials in the structures with the ring modulation
+	bool pcmWaveInterpolated;
+
+	//***************************************************************************
+	// Internal variables below
+	//***************************************************************************
+
+	// Relative position within either the synth wave or the PCM sampled wave
+	// 0 - start of the positive rising sine segment of the square wave or start of the PCM sample
+	// 1048576 (2^20) - end of the negative rising sine segment of the square wave
+	// For PCM waves, the address of the currently playing sample equals (wavePosition / 256)
+	Bit32u wavePosition;
+
+	// Relative position within a square wave phase:
+	// 0             - start of the phase
+	// 262144 (2^18) - end of a sine phase in the square wave
+	Bit32u squareWavePosition;
+
+	// Relative position within the positive or negative wave segment:
+	// 0 - start of the corresponding positive or negative segment of the square wave
+	// 262144 (2^18) - corresponds to end of the first sine phase in the square wave
+	// The same increment sampleStep is used to indicate the current position
+	// since the length of the resonance wave is always equal to four square wave sine segments.
+	Bit32u resonanceSinePosition;
+
+	// The amp of the resonance sine wave grows with the resonance value
+	// As the resonance value cannot change while the partial is active, it is initialised once
+	Bit32u resonanceAmpSubtraction;
+
+	// The decay speed of resonance sine wave, depends on the resonance value
+	Bit32u resAmpDecayFactor;
+
+	// Fractional part of the pcmPosition
+	Bit32u pcmInterpolationFactor;
+
+	// Current phase of the square wave
+	enum {
+		POSITIVE_RISING_SINE_SEGMENT,
+		POSITIVE_LINEAR_SEGMENT,
+		POSITIVE_FALLING_SINE_SEGMENT,
+		NEGATIVE_FALLING_SINE_SEGMENT,
+		NEGATIVE_LINEAR_SEGMENT,
+		NEGATIVE_RISING_SINE_SEGMENT
+	} phase;
+
+	// Current phase of the resonance wave
+	enum {
+		POSITIVE_RISING_RESONANCE_SINE_SEGMENT,
+		POSITIVE_FALLING_RESONANCE_SINE_SEGMENT,
+		NEGATIVE_FALLING_RESONANCE_SINE_SEGMENT,
+		NEGATIVE_RISING_RESONANCE_SINE_SEGMENT
+	} resonancePhase;
+
+	// Resulting log-space samples of the square and resonance waves
+	LogSample squareLogSample;
+	LogSample resonanceLogSample;
+
+	// Processed neighbour log-space samples of the PCM wave
+	LogSample firstPCMLogSample;
+	LogSample secondPCMLogSample;
+
+	//***************************************************************************
+	// Internal methods below
+	//***************************************************************************
+
+	Bit32u getSampleStep();
+	Bit32u getResonanceWaveLengthFactor(Bit32u effectiveCutoffValue);
+	Bit32u getHighLinearLength(Bit32u effectiveCutoffValue);
+
+	void computePositions(Bit32u highLinearLength, Bit32u lowLinearLength, Bit32u resonanceWaveLengthFactor);
+	void advancePosition();
+
+	void generateNextSquareWaveLogSample();
+	void generateNextResonanceWaveLogSample();
+	void generateNextSawtoothCosineLogSample(LogSample &logSample) const;
+
+	void pcmSampleToLogSample(LogSample &logSample, const Bit16s pcmSample) const;
+	void generateNextPCMWaveLogSamples();
+
+public:
+	// Initialise the WG engine for generation of synth partial samples and set up the invariant parameters
+	void initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance);
+
+	// Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters
+	void initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated);
+
+	// Update parameters with respect to TVP, TVA and TVF, and generate next sample
+	void generateNextSample(const Bit32u amp, const Bit16u pitch, const Bit32u cutoff);
+
+	// WG output in the log-space consists of two components which are to be added (or ring modulated) in the linear-space afterwards
+	LogSample getOutputLogSample(const bool first) const;
+
+	// Deactivate the WG engine
+	void deactivate();
+
+	// Return active state of the WG engine
+	bool isActive() const;
+
+	// Return true if the WG engine generates PCM wave samples
+	bool isPCMWave() const;
+
+	// Return current PCM interpolation factor
+	Bit32u getPCMInterpolationFactor() const;
+};
+
+// LA32PartialPair contains a structure of two partials being mixed / ring modulated
+class LA32PartialPair {
+	LA32WaveGenerator master;
+	LA32WaveGenerator slave;
+	bool ringModulated;
+	bool mixed;
+
+	static Bit16s unlogAndMixWGOutput(const LA32WaveGenerator &wg, const LogSample * const ringModulatingLogSample);
+
+public:
+	enum PairType {
+		MASTER,
+		SLAVE
+	};
+
+	// ringModulated should be set to false for the structures with mixing or stereo output
+	// ringModulated should be set to true for the structures with ring modulation
+	// mixed is used for the structures with ring modulation and indicates whether the master partial output is mixed to the ring modulator output
+	void init(const bool ringModulated, const bool mixed);
+
+	// Initialise the WG engine for generation of synth partial samples and set up the invariant parameters
+	void initSynth(const PairType master, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance);
+
+	// Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters
+	void initPCM(const PairType master, const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped);
+
+	// Update parameters with respect to TVP, TVA and TVF, and generate next sample
+	void generateNextSample(const PairType master, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff);
+
+	// Perform mixing / ring modulation and return the result
+	Bit16s nextOutSample();
+
+	// Deactivate the WG engine
+	void deactivate(const PairType master);
+
+	// Return active state of the WG engine
+	bool isActive(const PairType master) const;
+};
+
+} // namespace MT32Emu
+
+#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
+
+#endif // #if MT32EMU_ACCURATE_WG == 0
diff --git a/audio/softsynth/mt32/LegacyWaveGenerator.cpp b/audio/softsynth/mt32/LegacyWaveGenerator.cpp
new file mode 100644
index 0000000..139c892
--- /dev/null
+++ b/audio/softsynth/mt32/LegacyWaveGenerator.cpp
@@ -0,0 +1,347 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ *  This program 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 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 Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#include <cmath>
+#include "mt32emu.h"
+#include "mmath.h"
+#include "LegacyWaveGenerator.h"
+
+#if MT32EMU_ACCURATE_WG == 1
+
+namespace MT32Emu {
+
+static const float MIDDLE_CUTOFF_VALUE = 128.0f;
+static const float RESONANCE_DECAY_THRESHOLD_CUTOFF_VALUE = 144.0f;
+static const float MAX_CUTOFF_VALUE = 240.0f;
+
+float LA32WaveGenerator::getPCMSample(unsigned int position) {
+	if (position >= pcmWaveLength) {
+		if (!pcmWaveLooped) {
+			return 0;
+		}
+		position = position % pcmWaveLength;
+	}
+	Bit16s pcmSample = pcmWaveAddress[position];
+	float sampleValue = EXP2F(((pcmSample & 32767) - 32787.0f) / 2048.0f);
+	return ((pcmSample & 32768) == 0) ? sampleValue : -sampleValue;
+}
+
+void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) {
+	this->sawtoothWaveform = sawtoothWaveform;
+	this->pulseWidth = pulseWidth;
+	this->resonance = resonance;
+
+	wavePos = 0.0f;
+	lastFreq = 0.0f;
+
+	pcmWaveAddress = NULL;
+	active = true;
+}
+
+void LA32WaveGenerator::initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated) {
+	this->pcmWaveAddress = pcmWaveAddress;
+	this->pcmWaveLength = pcmWaveLength;
+	this->pcmWaveLooped = pcmWaveLooped;
+	this->pcmWaveInterpolated = pcmWaveInterpolated;
+
+	pcmPosition = 0.0f;
+	active = true;
+}
+
+float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pitch, const Bit32u cutoffRampVal) {
+	if (!active) {
+		return 0.0f;
+	}
+
+	this->amp = amp;
+	this->pitch = pitch;
+
+	float sample = 0.0f;
+
+	// SEMI-CONFIRMED: From sample analysis:
+	// (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc.
+	// This gives results within +/- 2 at the output (before any DAC bitshifting)
+	// when sustaining at levels 156 - 255 with no modifiers.
+	// (2) Tested with a special square wave partial (internal capture ID tva5) at TVA envelope levels 155-255.
+	// This gives deltas between -1 and 0 compared to the real output. Note that this special partial only produces
+	// positive amps, so negative still needs to be explored, as well as lower levels.
+	//
+	// Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing.
+
+	float amp = EXP2F(ampVal / -1024.0f / 4096.0f);
+	float freq = EXP2F(pitch / 4096.0f - 16.0f) * SAMPLE_RATE;
+
+	if (isPCMWave()) {
+		// Render PCM waveform
+		int len = pcmWaveLength;
+		int intPCMPosition = (int)pcmPosition;
+		if (intPCMPosition >= len && !pcmWaveLooped) {
+			// We're now past the end of a non-looping PCM waveform so it's time to die.
+			deactivate();
+			return 0.0f;
+		}
+		float positionDelta = freq * 2048.0f / SAMPLE_RATE;
+
+		// Linear interpolation
+		float firstSample = getPCMSample(intPCMPosition);
+		// We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial.
+		// It's assumed that the multiplication circuitry intended to perform the interpolation on the slave PCM partial
+		// is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair).
+		if (pcmWaveInterpolated) {
+			sample = firstSample + (getPCMSample(intPCMPosition + 1) - firstSample) * (pcmPosition - intPCMPosition);
+		} else {
+			sample = firstSample;
+		}
+
+		float newPCMPosition = pcmPosition + positionDelta;
+		if (pcmWaveLooped) {
+			newPCMPosition = fmod(newPCMPosition, (float)pcmWaveLength);
+		}
+		pcmPosition = newPCMPosition;
+	} else {
+		// Render synthesised waveform
+		wavePos *= lastFreq / freq;
+		lastFreq = freq;
+
+		float resAmp = EXP2F(1.0f - (32 - resonance) / 4.0f);
+		{
+			//static const float resAmpFactor = EXP2F(-7);
+			//resAmp = EXP2I(resonance << 10) * resAmpFactor;
+		}
+
+		// The cutoffModifier may not be supposed to be directly added to the cutoff -
+		// it may for example need to be multiplied in some way.
+		// The 240 cutoffVal limit was determined via sample analysis (internal Munt capture IDs: glop3, glop4).
+		// More research is needed to be sure that this is correct, however.
+		float cutoffVal = cutoffRampVal / 262144.0f;
+		if (cutoffVal > MAX_CUTOFF_VALUE) {
+			cutoffVal = MAX_CUTOFF_VALUE;
+		}
+
+		// Wave length in samples
+		float waveLen = SAMPLE_RATE / freq;
+
+		// Init cosineLen
+		float cosineLen = 0.5f * waveLen;
+		if (cutoffVal > MIDDLE_CUTOFF_VALUE) {
+			cosineLen *= EXP2F((cutoffVal - MIDDLE_CUTOFF_VALUE) / -16.0f); // found from sample analysis
+		}
+
+		// Start playing in center of first cosine segment
+		// relWavePos is shifted by a half of cosineLen
+		float relWavePos = wavePos + 0.5f * cosineLen;
+		if (relWavePos > waveLen) {
+			relWavePos -= waveLen;
+		}
+
+		// Ratio of positive segment to wave length
+		float pulseLen = 0.5f;
+		if (pulseWidth > 128) {
+			pulseLen = EXP2F((64 - pulseWidth) / 64.0f);
+			//static const float pulseLenFactor = EXP2F(-192 / 64);
+			//pulseLen = EXP2I((256 - pulseWidthVal) << 6) * pulseLenFactor;
+		}
+		pulseLen *= waveLen;
+
+		float hLen = pulseLen - cosineLen;
+
+		// Ignore pulsewidths too high for given freq
+		if (hLen < 0.0f) {
+			hLen = 0.0f;
+		}
+
+		// Ignore pulsewidths too high for given freq and cutoff
+		float lLen = waveLen - hLen - 2 * cosineLen;
+		if (lLen < 0.0f) {
+			lLen = 0.0f;
+		}
+
+		// Correct resAmp for cutoff in range 50..66
+		if ((cutoffVal >= 128.0f) && (cutoffVal < 144.0f)) {
+			resAmp *= sinf(FLOAT_PI * (cutoffVal - 128.0f) / 32.0f);
+		}
+
+		// Produce filtered square wave with 2 cosine waves on slopes
+
+		// 1st cosine segment
+		if (relWavePos < cosineLen) {
+			sample = -cosf(FLOAT_PI * relWavePos / cosineLen);
+		} else
+
+		// high linear segment
+		if (relWavePos < (cosineLen + hLen)) {
+			sample = 1.f;
+		} else
+
+		// 2nd cosine segment
+		if (relWavePos < (2 * cosineLen + hLen)) {
+			sample = cosf(FLOAT_PI * (relWavePos - (cosineLen + hLen)) / cosineLen);
+		} else {
+
+		// low linear segment
+			sample = -1.f;
+		}
+
+		if (cutoffVal < 128.0f) {
+
+			// Attenuate samples below cutoff 50
+			// Found by sample analysis
+			sample *= EXP2F(-0.125f * (128.0f - cutoffVal));
+		} else {
+
+			// Add resonance sine. Effective for cutoff > 50 only
+			float resSample = 1.0f;
+
+			// Resonance decay speed factor
+			float resAmpDecayFactor = Tables::getInstance().resAmpDecayFactor[resonance >> 2];
+
+			// Now relWavePos counts from the middle of first cosine
+			relWavePos = wavePos;
+
+			// negative segments
+			if (!(relWavePos < (cosineLen + hLen))) {
+				resSample = -resSample;
+				relWavePos -= cosineLen + hLen;
+
+				// From the digital captures, the decaying speed of the resonance sine is found a bit different for the positive and the negative segments
+				resAmpDecayFactor += 0.25f;
+			}
+
+			// Resonance sine WG
+			resSample *= sinf(FLOAT_PI * relWavePos / cosineLen);
+
+			// Resonance sine amp
+			float resAmpFadeLog2 = -0.125f * resAmpDecayFactor * (relWavePos / cosineLen); // seems to be exact
+			float resAmpFade = EXP2F(resAmpFadeLog2);
+
+			// Now relWavePos set negative to the left from center of any cosine
+			relWavePos = wavePos;
+
+			// negative segment
+			if (!(wavePos < (waveLen - 0.5f * cosineLen))) {
+				relWavePos -= waveLen;
+			} else
+
+			// positive segment
+			if (!(wavePos < (hLen + 0.5f * cosineLen))) {
+				relWavePos -= cosineLen + hLen;
+			}
+
+			// To ensure the output wave has no breaks, two different windows are appied to the beginning and the ending of the resonance sine segment
+			if (relWavePos < 0.5f * cosineLen) {
+				float syncSine = sinf(FLOAT_PI * relWavePos / cosineLen);
+				if (relWavePos < 0.0f) {
+					// The window is synchronous square sine here
+					resAmpFade *= syncSine * syncSine;
+				} else {
+					// The window is synchronous sine here
+					resAmpFade *= syncSine;
+				}
+			}
+
+			sample += resSample * resAmp * resAmpFade;
+		}
+
+		// sawtooth waves
+		if (sawtoothWaveform) {
+			sample *= cosf(FLOAT_2PI * wavePos / waveLen);
+		}
+
+		wavePos++;
+
+		// wavePos isn't supposed to be > waveLen
+		if (wavePos > waveLen) {
+			wavePos -= waveLen;
+		}
+	}
+
+	// Multiply sample with current TVA value
+	sample *= amp;
+	return sample;
+}
+
+void LA32WaveGenerator::deactivate() {
+	active = false;
+}
+
+bool LA32WaveGenerator::isActive() const {
+	return active;
+}
+
+bool LA32WaveGenerator::isPCMWave() const {
+	return pcmWaveAddress != NULL;
+}
+
+void LA32PartialPair::init(const bool ringModulated, const bool mixed) {
+	this->ringModulated = ringModulated;
+	this->mixed = mixed;
+	masterOutputSample = 0.0f;
+	slaveOutputSample = 0.0f;
+}
+
+void LA32PartialPair::initSynth(const PairType useMaster, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) {
+	if (useMaster == MASTER) {
+		master.initSynth(sawtoothWaveform, pulseWidth, resonance);
+	} else {
+		slave.initSynth(sawtoothWaveform, pulseWidth, resonance);
+	}
+}
+
+void LA32PartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped) {
+	if (useMaster == MASTER) {
+		master.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, true);
+	} else {
+		slave.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, !ringModulated);
+	}
+}
+
+void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff) {
+	if (useMaster == MASTER) {
+		masterOutputSample = master.generateNextSample(amp, pitch, cutoff);
+	} else {
+		slaveOutputSample = slave.generateNextSample(amp, pitch, cutoff);
+	}
+}
+
+Bit16s LA32PartialPair::nextOutSample() {
+	float outputSample;
+	if (ringModulated) {
+		float ringModulatedSample = masterOutputSample * slaveOutputSample;
+		outputSample = mixed ? masterOutputSample + ringModulatedSample : ringModulatedSample;
+	} else {
+		outputSample = masterOutputSample + slaveOutputSample;
+	}
+	return Bit16s(outputSample * 8192.0f);
+}
+
+void LA32PartialPair::deactivate(const PairType useMaster) {
+	if (useMaster == MASTER) {
+		master.deactivate();
+		masterOutputSample = 0.0f;
+	} else {
+		slave.deactivate();
+		slaveOutputSample = 0.0f;
+	}
+}
+
+bool LA32PartialPair::isActive(const PairType useMaster) const {
+	return useMaster == MASTER ? master.isActive() : slave.isActive();
+}
+
+}
+
+#endif // #if MT32EMU_ACCURATE_WG == 1
diff --git a/audio/softsynth/mt32/LegacyWaveGenerator.h b/audio/softsynth/mt32/LegacyWaveGenerator.h
new file mode 100644
index 0000000..81c1b9c
--- /dev/null
+++ b/audio/softsynth/mt32/LegacyWaveGenerator.h
@@ -0,0 +1,146 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ *  This program 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 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 Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if MT32EMU_ACCURATE_WG == 1
+
+#ifndef MT32EMU_LA32_WAVE_GENERATOR_H
+#define MT32EMU_LA32_WAVE_GENERATOR_H
+
+namespace MT32Emu {
+
+/**
+ * LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator.
+ * The output square wave is created by adding high / low linear segments in-between
+ * the rising and falling cosine segments. Basically, it’s very similar to the phase distortion synthesis.
+ * Behaviour of a true resonance filter is emulated by adding decaying sine wave.
+ * The beginning and the ending of the resonant sine is multiplied by a cosine window.
+ * To synthesise sawtooth waves, the resulting square wave is multiplied by synchronous cosine wave.
+ */
+class LA32WaveGenerator {
+	//***************************************************************************
+	//  The local copy of partial parameters below
+	//***************************************************************************
+
+	bool active;
+
+	// True means the resulting square wave is to be multiplied by the synchronous cosine
+	bool sawtoothWaveform;
+
+	// Logarithmic amp of the wave generator
+	Bit32u amp;
+
+	// Logarithmic frequency of the resulting wave
+	Bit16u pitch;
+
+	// Values in range [1..31]
+	// Value 1 correspong to the minimum resonance
+	Bit8u resonance;
+
+	// Processed value in range [0..255]
+	// Values in range [0..128] have no effect and the resulting wave remains symmetrical
+	// Value 255 corresponds to the maximum possible asymmetric of the resulting wave
+	Bit8u pulseWidth;
+
+	// Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier
+	Bit32u cutoffVal;
+
+	// Logarithmic PCM sample start address
+	const Bit16s *pcmWaveAddress;
+
+	// Logarithmic PCM sample length
+	Bit32u pcmWaveLength;
+
+	// true for looped logarithmic PCM samples
+	bool pcmWaveLooped;
+
+	// false for slave PCM partials in the structures with the ring modulation
+	bool pcmWaveInterpolated;
+
+	//***************************************************************************
+	// Internal variables below
+	//***************************************************************************
+
+	float wavePos;
+	float lastFreq;
+	float pcmPosition;
+
+	float getPCMSample(unsigned int position);
+
+public:
+	// Initialise the WG engine for generation of synth partial samples and set up the invariant parameters
+	void initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance);
+
+	// Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters
+	void initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated);
+
+	// Update parameters with respect to TVP, TVA and TVF, and generate next sample
+	float generateNextSample(const Bit32u amp, const Bit16u pitch, const Bit32u cutoff);
+
+	// Deactivate the WG engine
+	void deactivate();
+
+	// Return active state of the WG engine
+	bool isActive() const;
+
+	// Return true if the WG engine generates PCM wave samples
+	bool isPCMWave() const;
+};
+
+// LA32PartialPair contains a structure of two partials being mixed / ring modulated
+class LA32PartialPair {
+	LA32WaveGenerator master;
+	LA32WaveGenerator slave;
+	bool ringModulated;
+	bool mixed;
+	float masterOutputSample;
+	float slaveOutputSample;
+
+public:
+	enum PairType {
+		MASTER,
+		SLAVE
+	};
+
+	// ringModulated should be set to false for the structures with mixing or stereo output
+	// ringModulated should be set to true for the structures with ring modulation
+	// mixed is used for the structures with ring modulation and indicates whether the master partial output is mixed to the ring modulator output
+	void init(const bool ringModulated, const bool mixed);
+
+	// Initialise the WG engine for generation of synth partial samples and set up the invariant parameters
+	void initSynth(const PairType master, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance);
+
+	// Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters
+	void initPCM(const PairType master, const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped);
+
+	// Update parameters with respect to TVP, TVA and TVF, and generate next sample
+	void generateNextSample(const PairType master, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff);
+
+	// Perform mixing / ring modulation and return the result
+	Bit16s nextOutSample();
+
+	// Deactivate the WG engine
+	void deactivate(const PairType master);
+
+	// Return active state of the WG engine
+	bool isActive(const PairType master) const;
+};
+
+} // namespace MT32Emu
+
+#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
+
+#endif // #if MT32EMU_ACCURATE_WG == 1
diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp
index 0ef2f62..a0aec90 100644
--- a/audio/softsynth/mt32/Partial.cpp
+++ b/audio/softsynth/mt32/Partial.cpp
@@ -35,7 +35,12 @@ static const float PAN_NUMERATOR_MASTER[] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
 static const float PAN_NUMERATOR_SLAVE[]  = {0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f};
 
 Partial::Partial(Synth *useSynth, int useDebugPartialNum) :
-	synth(useSynth), debugPartialNum(useDebugPartialNum), sampleNum(0), tva(new TVA(this, &ampRamp)), tvp(new TVP(this)), tvf(new TVF(this, &cutoffModifierRamp)) {
+	synth(useSynth), debugPartialNum(useDebugPartialNum), sampleNum(0) {
+	// Initialisation of tva, tvp and tvf uses 'this' pointer
+	// and thus should not be in the initializer list to avoid a compiler warning
+	tva = new TVA(this, &ampRamp);
+	tvp = new TVP(this);
+	tvf = new TVF(this, &cutoffModifierRamp);
 	ownerPart = -1;
 	poly = NULL;
 	pair = NULL;
@@ -81,27 +86,23 @@ void Partial::deactivate() {
 	ownerPart = -1;
 	if (poly != NULL) {
 		poly->partialDeactivated(this);
-		if (pair != NULL) {
-			pair->pair = NULL;
-		}
 	}
 	synth->partialStateChanged(this, tva->getPhase(), TVA_PHASE_DEAD);
 #if MT32EMU_MONITOR_PARTIALS > 2
 	synth->printDebug("[+%lu] [Partial %d] Deactivated", sampleNum, debugPartialNum);
 	synth->printPartialUsage(sampleNum);
 #endif
-}
-
-// DEPRECATED: This should probably go away eventually, it's currently only used as a kludge to protect our old assumptions that
-// rhythm part notes were always played as key MIDDLEC.
-int Partial::getKey() const {
-	if (poly == NULL) {
-		return -1;
-	} else if (ownerPart == 8) {
-		// FIXME: Hack, should go away after new pitch stuff is committed (and possibly some TVF changes)
-		return MIDDLEC;
+	if (isRingModulatingSlave()) {
+		pair->la32Pair.deactivate(LA32PartialPair::SLAVE);
 	} else {
-		return poly->getKey();
+		la32Pair.deactivate(LA32PartialPair::MASTER);
+		if (hasRingModulatingSlave()) {
+			pair->deactivate();
+			pair = NULL;
+		}
+	}
+	if (pair != NULL) {
+		pair->pair = NULL;
 	}
 }
 
@@ -164,8 +165,6 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
 		pcmWave = &synth->pcmWaves[pcmNum];
 	} else {
 		pcmWave = NULL;
-		wavePos = 0.0f;
-		lastFreq = 0.0;
 	}
 
 	// CONFIRMED: pulseWidthVal calculation is based on information from Mok
@@ -176,26 +175,65 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
 		pulseWidthVal = 255;
 	}
 
-	pcmPosition = 0.0f;
 	pair = pairPartial;
 	alreadyOutputed = false;
 	tva->reset(part, patchCache->partialParam, rhythmTemp);
 	tvp->reset(part, patchCache->partialParam);
 	tvf->reset(patchCache->partialParam, tvp->getBasePitch());
+
+	LA32PartialPair::PairType pairType;
+	LA32PartialPair *useLA32Pair;
+	if (isRingModulatingSlave()) {
+		pairType = LA32PartialPair::SLAVE;
+		useLA32Pair = &pair->la32Pair;
+	} else {
+		pairType = LA32PartialPair::MASTER;
+		la32Pair.init(hasRingModulatingSlave(), mixType == 1);
+		useLA32Pair = &la32Pair;
+	}
+	if (isPCM()) {
+		useLA32Pair->initPCM(pairType, &synth->pcmROMData[pcmWave->addr], pcmWave->len, pcmWave->loop);
+	} else {
+		useLA32Pair->initSynth(pairType, (patchCache->waveform & 1) != 0, pulseWidthVal, patchCache->srcPartial.tvf.resonance + 1);
+	}
+	if (!hasRingModulatingSlave()) {
+		la32Pair.deactivate(LA32PartialPair::SLAVE);
+	}
+	// Temporary integration hack
+	stereoVolume.leftVol /= 8192.0f;
+	stereoVolume.rightVol /= 8192.0f;
 }
 
-float Partial::getPCMSample(unsigned int position) {
-	if (position >= pcmWave->len) {
-		if (!pcmWave->loop) {
-			return 0;
-		}
-		position = position % pcmWave->len;
+Bit32u Partial::getAmpValue() {
+	// SEMI-CONFIRMED: From sample analysis:
+	// (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc.
+	// This gives results within +/- 2 at the output (before any DAC bitshifting)
+	// when sustaining at levels 156 - 255 with no modifiers.
+	// (2) Tested with a special square wave partial (internal capture ID tva5) at TVA envelope levels 155-255.
+	// This gives deltas between -1 and 0 compared to the real output. Note that this special partial only produces
+	// positive amps, so negative still needs to be explored, as well as lower levels.
+	//
+	// Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing.
+	// TODO: The tests above were performed using the float model, to be refined
+	Bit32u ampRampVal = 67117056 - ampRamp.nextValue();
+	if (ampRamp.checkInterrupt()) {
+		tva->handleInterrupt();
 	}
-	return synth->pcmROMData[pcmWave->addr + position];
+	return ampRampVal;
 }
 
-unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) {
-	const Tables &tables = Tables::getInstance();
+Bit32u Partial::getCutoffValue() {
+	if (isPCM()) {
+		return 0;
+	}
+	Bit32u cutoffModifierRampVal = cutoffModifierRamp.nextValue();
+	if (cutoffModifierRamp.checkInterrupt()) {
+		tvf->handleInterrupt();
+	}
+	return (tvf->getBaseCutoff() << 18) + cutoffModifierRampVal;
+}
+
+unsigned long Partial::generateSamples(Bit16s *partialBuf, unsigned long length) {
 	if (!isActive() || alreadyOutputed) {
 		return 0;
 	}
@@ -203,310 +241,31 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
 		synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::generateSamples()!", debugPartialNum);
 		return 0;
 	}
-
 	alreadyOutputed = true;
 
-	// Generate samples
-
 	for (sampleNum = 0; sampleNum < length; sampleNum++) {
-		float sample = 0;
-		Bit32u ampRampVal = ampRamp.nextValue();
-		if (ampRamp.checkInterrupt()) {
-			tva->handleInterrupt();
-		}
-		if (!tva->isPlaying()) {
+		if (!tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::MASTER)) {
 			deactivate();
 			break;
 		}
-
-		Bit16u pitch = tvp->nextPitch();
-
-		// SEMI-CONFIRMED: From sample analysis:
-		// (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc.
-		// This gives results within +/- 2 at the output (before any DAC bitshifting)
-		// when sustaining at levels 156 - 255 with no modifiers.
-		// (2) Tested with a special square wave partial (internal capture ID tva5) at TVA envelope levels 155-255.
-		// This gives deltas between -1 and 0 compared to the real output. Note that this special partial only produces
-		// positive amps, so negative still needs to be explored, as well as lower levels.
-		//
-		// Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing.
-
-#if MT32EMU_ACCURATE_WG == 1
-		float amp = EXP2F((32772 - ampRampVal / 2048) / -2048.0f);
-		float freq = EXP2F(pitch / 4096.0f - 16.0f) * SAMPLE_RATE;
-#else
-		static const float ampFactor = EXP2F(32772 / -2048.0f);
-		float amp = EXP2I(ampRampVal >> 10) * ampFactor;
-
-		static const float freqFactor = EXP2F(-16.0f) * SAMPLE_RATE;
-		float freq = EXP2I(pitch) * freqFactor;
-#endif
-
-		if (patchCache->PCMPartial) {
-			// Render PCM waveform
-			int len = pcmWave->len;
-			int intPCMPosition = (int)pcmPosition;
-			if (intPCMPosition >= len && !pcmWave->loop) {
-				// We're now past the end of a non-looping PCM waveform so it's time to die.
-				deactivate();
-				break;
-			}
-			Bit32u pcmAddr = pcmWave->addr;
-			float positionDelta = freq * 2048.0f / SAMPLE_RATE;
-
-			// Linear interpolation
-			float firstSample = synth->pcmROMData[pcmAddr + intPCMPosition];
-			// We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial.
-			// It's assumed that the multiplication circuitry intended to perform the interpolation on the slave PCM partial
-			// is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair).
-			if (pair == NULL || mixType == 0 || structurePosition == 0) {
-				sample = firstSample + (getPCMSample(intPCMPosition + 1) - firstSample) * (pcmPosition - intPCMPosition);
-			} else {
-				sample = firstSample;
-			}
-
-			float newPCMPosition = pcmPosition + positionDelta;
-			if (pcmWave->loop) {
-				newPCMPosition = fmod(newPCMPosition, (float)pcmWave->len);
-			}
-			pcmPosition = newPCMPosition;
-		} else {
-			// Render synthesised waveform
-			wavePos *= lastFreq / freq;
-			lastFreq = freq;
-
-			Bit32u cutoffModifierRampVal = cutoffModifierRamp.nextValue();
-			if (cutoffModifierRamp.checkInterrupt()) {
-				tvf->handleInterrupt();
-			}
-			float cutoffModifier = cutoffModifierRampVal / 262144.0f;
-
-			// res corresponds to a value set in an LA32 register
-			Bit8u res = patchCache->srcPartial.tvf.resonance + 1;
-
-			float resAmp;
-			{
-				// resAmp = EXP2F(1.0f - (32 - res) / 4.0f);
-				static const float resAmpFactor = EXP2F(-7);
-				resAmp = EXP2I(res << 10) * resAmpFactor;
-			}
-
-			// The cutoffModifier may not be supposed to be directly added to the cutoff -
-			// it may for example need to be multiplied in some way.
-			// The 240 cutoffVal limit was determined via sample analysis (internal Munt capture IDs: glop3, glop4).
-			// More research is needed to be sure that this is correct, however.
-			float cutoffVal = tvf->getBaseCutoff() + cutoffModifier;
-			if (cutoffVal > 240.0f) {
-				cutoffVal = 240.0f;
-			}
-
-			// Wave length in samples
-			float waveLen = SAMPLE_RATE / freq;
-
-			// Init cosineLen
-			float cosineLen = 0.5f * waveLen;
-			if (cutoffVal > 128.0f) {
-#if MT32EMU_ACCURATE_WG == 1
-				cosineLen *= EXP2F((cutoffVal - 128.0f) / -16.0f); // found from sample analysis
-#else
-				static const float cosineLenFactor = EXP2F(128.0f / -16.0f);
-				cosineLen *= EXP2I(Bit32u((256.0f - cutoffVal) * 256.0f)) * cosineLenFactor;
-#endif
-			}
-
-			// Start playing in center of first cosine segment
-			// relWavePos is shifted by a half of cosineLen
-			float relWavePos = wavePos + 0.5f * cosineLen;
-			if (relWavePos > waveLen) {
-				relWavePos -= waveLen;
-			}
-
-			// Ratio of positive segment to wave length
-			float pulseLen = 0.5f;
-			if (pulseWidthVal > 128) {
-				// pulseLen = EXP2F((64 - pulseWidthVal) / 64);
-				static const float pulseLenFactor = EXP2F(-192 / 64);
-				pulseLen = EXP2I((256 - pulseWidthVal) << 6) * pulseLenFactor;
-			}
-			pulseLen *= waveLen;
-
-			float hLen = pulseLen - cosineLen;
-
-			// Ignore pulsewidths too high for given freq
-			if (hLen < 0.0f) {
-				hLen = 0.0f;
-			}
-
-			// Ignore pulsewidths too high for given freq and cutoff
-			float lLen = waveLen - hLen - 2 * cosineLen;
-			if (lLen < 0.0f) {
-				lLen = 0.0f;
-			}
-
-			// Correct resAmp for cutoff in range 50..66
-			if ((cutoffVal >= 128.0f) && (cutoffVal < 144.0f)) {
-#if MT32EMU_ACCURATE_WG == 1
-				resAmp *= sinf(FLOAT_PI * (cutoffVal - 128.0f) / 32.0f);
-#else
-				resAmp *= tables.sinf10[Bit32u(64 * (cutoffVal - 128.0f))];
-#endif
-			}
-
-			// Produce filtered square wave with 2 cosine waves on slopes
-
-			// 1st cosine segment
-			if (relWavePos < cosineLen) {
-#if MT32EMU_ACCURATE_WG == 1
-				sample = -cosf(FLOAT_PI * relWavePos / cosineLen);
-#else
-				sample = -tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) + 1024];
-#endif
-			} else
-
-			// high linear segment
-			if (relWavePos < (cosineLen + hLen)) {
-				sample = 1.f;
-			} else
-
-			// 2nd cosine segment
-			if (relWavePos < (2 * cosineLen + hLen)) {
-#if MT32EMU_ACCURATE_WG == 1
-				sample = cosf(FLOAT_PI * (relWavePos - (cosineLen + hLen)) / cosineLen);
-#else
-				sample = tables.sinf10[Bit32u(2048.0f * (relWavePos - (cosineLen + hLen)) / cosineLen) + 1024];
-#endif
-			} else {
-
-			// low linear segment
-				sample = -1.f;
-			}
-
-			if (cutoffVal < 128.0f) {
-
-				// Attenuate samples below cutoff 50
-				// Found by sample analysis
-#if MT32EMU_ACCURATE_WG == 1
-				sample *= EXP2F(-0.125f * (128.0f - cutoffVal));
-#else
-				static const float cutoffAttenuationFactor = EXP2F(-0.125f * 128.0f);
-				sample *= EXP2I(Bit32u(512.0f * cutoffVal)) * cutoffAttenuationFactor;
-#endif
-			} else {
-
-				// Add resonance sine. Effective for cutoff > 50 only
-				float resSample = 1.0f;
-
-				// Now relWavePos counts from the middle of first cosine
-				relWavePos = wavePos;
-
-				// negative segments
-				if (!(relWavePos < (cosineLen + hLen))) {
-					resSample = -resSample;
-					relWavePos -= cosineLen + hLen;
+		la32Pair.generateNextSample(LA32PartialPair::MASTER, getAmpValue(), tvp->nextPitch(), getCutoffValue());
+		if (hasRingModulatingSlave()) {
+			la32Pair.generateNextSample(LA32PartialPair::SLAVE, pair->getAmpValue(), pair->tvp->nextPitch(), pair->getCutoffValue());
+			if (!pair->tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::SLAVE)) {
+				pair->deactivate();
+				if (mixType == 2) {
+					deactivate();
+					break;
 				}
-
-				// Resonance sine WG
-#if MT32EMU_ACCURATE_WG == 1
-				resSample *= sinf(FLOAT_PI * relWavePos / cosineLen);
-#else
-				resSample *= tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) & 4095];
-#endif
-
-				// Resonance sine amp
-				float resAmpFadeLog2 = -tables.resAmpFadeFactor[res >> 2] * (relWavePos / cosineLen); // seems to be exact
-#if MT32EMU_ACCURATE_WG == 1
-				float resAmpFade = EXP2F(resAmpFadeLog2);
-#else
-				static const float resAmpFadeFactor = EXP2F(-30.0f);
-				float resAmpFade = (resAmpFadeLog2 < -30.0f) ? 0.0f : EXP2I(Bit32u((30.0f + resAmpFadeLog2) * 4096.0f)) * resAmpFadeFactor;
-#endif
-
-				// Now relWavePos set negative to the left from center of any cosine
-				relWavePos = wavePos;
-
-				// negative segment
-				if (!(wavePos < (waveLen - 0.5f * cosineLen))) {
-					relWavePos -= waveLen;
-				} else
-
-				// positive segment
-				if (!(wavePos < (hLen + 0.5f * cosineLen))) {
-					relWavePos -= cosineLen + hLen;
-				}
-
-				// Fading to zero while within cosine segments to avoid jumps in the wave
-				// Sample analysis suggests that this window is very close to cosine
-				if (relWavePos < 0.5f * cosineLen) {
-#if MT32EMU_ACCURATE_WG == 1
-					resAmpFade *= 0.5f * (1.0f - cosf(FLOAT_PI * relWavePos / (0.5f * cosineLen)));
-#else
-					resAmpFade *= 0.5f * (1.0f + tables.sinf10[Bit32s(2048.0f * relWavePos / (0.5f * cosineLen)) + 3072]);
-#endif
-				}
-
-				sample += resSample * resAmp * resAmpFade;
-			}
-
-			// sawtooth waves
-			if ((patchCache->waveform & 1) != 0) {
-#if MT32EMU_ACCURATE_WG == 1
-				sample *= cosf(FLOAT_2PI * wavePos / waveLen);
-#else
-				sample *= tables.sinf10[(Bit32u(4096.0f * wavePos / waveLen) & 4095) + 1024];
-#endif
-			}
-
-			wavePos++;
-
-			// wavePos isn't supposed to be > waveLen
-			if (wavePos > waveLen) {
-				wavePos -= waveLen;
 			}
 		}
-
-		// Multiply sample with current TVA value
-		sample *= amp;
-		*partialBuf++ = sample;
+		*partialBuf++ = la32Pair.nextOutSample();
 	}
 	unsigned long renderedSamples = sampleNum;
 	sampleNum = 0;
 	return renderedSamples;
 }
 
-float *Partial::mixBuffersRingMix(float *buf1, float *buf2, unsigned long len) {
-	if (buf1 == NULL) {
-		return NULL;
-	}
-	if (buf2 == NULL) {
-		return buf1;
-	}
-
-	while (len--) {
-		// FIXME: At this point we have no idea whether this is remotely correct...
-		*buf1 = *buf1 * *buf2 + *buf1;
-		buf1++;
-		buf2++;
-	}
-	return buf1;
-}
-
-float *Partial::mixBuffersRing(float *buf1, float *buf2, unsigned long len) {
-	if (buf1 == NULL) {
-		return NULL;
-	}
-	if (buf2 == NULL) {
-		return NULL;
-	}
-
-	while (len--) {
-		// FIXME: At this point we have no idea whether this is remotely correct...
-		*buf1 = *buf1 * *buf2;
-		buf1++;
-		buf2++;
-	}
-	return buf1;
-}
-
 bool Partial::hasRingModulatingSlave() const {
 	return pair != NULL && structurePosition == 0 && (mixType == 1 || mixType == 2);
 }
@@ -538,53 +297,14 @@ bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long lengt
 		synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::produceOutput()!", debugPartialNum);
 		return false;
 	}
-
-	float *partialBuf = &myBuffer[0];
-	unsigned long numGenerated = generateSamples(partialBuf, length);
-	if (mixType == 1 || mixType == 2) {
-		float *pairBuf;
-		unsigned long pairNumGenerated;
-		if (pair == NULL) {
-			pairBuf = NULL;
-			pairNumGenerated = 0;
-		} else {
-			pairBuf = &pair->myBuffer[0];
-			pairNumGenerated = pair->generateSamples(pairBuf, numGenerated);
-			// pair will have been set to NULL if it deactivated within generateSamples()
-			if (pair != NULL) {
-				if (!isActive()) {
-					pair->deactivate();
-					pair = NULL;
-				} else if (!pair->isActive()) {
-					pair = NULL;
-				}
-			}
-		}
-		if (pairNumGenerated > 0) {
-			if (mixType == 1) {
-				mixBuffersRingMix(partialBuf, pairBuf, pairNumGenerated);
-			} else {
-				mixBuffersRing(partialBuf, pairBuf, pairNumGenerated);
-			}
-		}
-		if (numGenerated > pairNumGenerated) {
-			if (mixType == 2) {
-				numGenerated = pairNumGenerated;
-				deactivate();
-			}
-		}
-	}
-
-	for (unsigned int i = 0; i < numGenerated; i++) {
-		*leftBuf++ = partialBuf[i] * stereoVolume.leftVol;
-	}
+	unsigned long numGenerated = generateSamples(myBuffer, length);
 	for (unsigned int i = 0; i < numGenerated; i++) {
-		*rightBuf++ = partialBuf[i] * stereoVolume.rightVol;
+		*leftBuf++ = myBuffer[i] * stereoVolume.leftVol;
+		*rightBuf++ = myBuffer[i] * stereoVolume.rightVol;
 	}
-	while (numGenerated < length) {
+	for (; numGenerated < length; numGenerated++) {
 		*leftBuf++ = 0.0f;
 		*rightBuf++ = 0.0f;
-		numGenerated++;
 	}
 	return true;
 }
diff --git a/audio/softsynth/mt32/Partial.h b/audio/softsynth/mt32/Partial.h
index 9166beb..21b1bfe 100644
--- a/audio/softsynth/mt32/Partial.h
+++ b/audio/softsynth/mt32/Partial.h
@@ -44,12 +44,7 @@ private:
 	int structurePosition; // 0 or 1 of a structure pair
 	StereoVolume stereoVolume;
 
-	// Distance in (possibly fractional) samples from the start of the current pulse
-	float wavePos;
-
-	float lastFreq;
-
-	float myBuffer[MAX_SAMPLES_PER_RUN];
+	Bit16s myBuffer[MAX_SAMPLES_PER_RUN];
 
 	// Only used for PCM partials
 	int pcmNum;
@@ -60,17 +55,16 @@ private:
 	// Range: 0-255
 	int pulseWidthVal;
 
-	float pcmPosition;
-
 	Poly *poly;
 
 	LA32Ramp ampRamp;
 	LA32Ramp cutoffModifierRamp;
 
-	float *mixBuffersRingMix(float *buf1, float *buf2, unsigned long len);
-	float *mixBuffersRing(float *buf1, float *buf2, unsigned long len);
+	// TODO: This should be owned by PartialPair
+	LA32PartialPair la32Pair;
 
-	float getPCMSample(unsigned int position);
+	Bit32u getAmpValue();
+	Bit32u getCutoffValue();
 
 public:
 	const PatchCache *patchCache;
@@ -90,7 +84,6 @@ public:
 	unsigned long debugGetSampleNum() const;
 
 	int getOwnerPart() const;
-	int getKey() const;
 	const Poly *getPoly() const;
 	bool isActive() const;
 	void activate(int part);
@@ -111,7 +104,7 @@ public:
 	bool produceOutput(float *leftBuf, float *rightBuf, unsigned long length);
 
 	// This function writes mono sample output to the provided buffer, and returns the number of samples written
-	unsigned long generateSamples(float *partialBuf, unsigned long length);
+	unsigned long generateSamples(Bit16s *partialBuf, unsigned long length);
 };
 
 }
diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp
index 6099b98..b7af992 100644
--- a/audio/softsynth/mt32/Synth.cpp
+++ b/audio/softsynth/mt32/Synth.cpp
@@ -345,17 +345,7 @@ bool Synth::loadPCMROM(const ROMImage &pcmROMImage) {
 			}
 			log = log | (short)(bit << (15 - u));
 		}
-		bool negative = log < 0;
-		log &= 0x7FFF;
-
-		// CONFIRMED from sample analysis to be 99.99%+ accurate with current TVA multiplier
-		float lin = EXP2F((32787 - log) / -2048.0f);
-
-		if (negative) {
-			lin = -lin;
-		}
-
-		pcmROMData[i] = lin;
+		pcmROMData[i] = log;
 	}
 
 	delete[] buffer;
@@ -462,7 +452,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage) {
 	// 1MB PCM ROM for CM-32L, LAPC-I, CM-64, CM-500
 	// Note that the size below is given in samples (16-bit), not bytes
 	pcmROMSize = controlROMMap->pcmCount == 256 ? 512 * 1024 : 256 * 1024;
-	pcmROMData = new float[pcmROMSize];
+	pcmROMData = new Bit16s[pcmROMSize];
 
 #if MT32EMU_MONITOR_INIT
 	printDebug("Loading PCM ROM");
diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h
index 70e0064..56e88e6 100644
--- a/audio/softsynth/mt32/Synth.h
+++ b/audio/softsynth/mt32/Synth.h
@@ -283,7 +283,7 @@ private:
 
 	const ControlROMMap *controlROMMap;
 	Bit8u controlROMData[CONTROL_ROM_SIZE];
-	float *pcmROMData;
+	Bit16s *pcmROMData;
 	size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM
 
 	Bit8s chantable[32];
diff --git a/audio/softsynth/mt32/Tables.cpp b/audio/softsynth/mt32/Tables.cpp
index c7e9437..ffd784a 100644
--- a/audio/softsynth/mt32/Tables.cpp
+++ b/audio/softsynth/mt32/Tables.cpp
@@ -72,24 +72,25 @@ Tables::Tables() {
 		//synth->printDebug("%d: %d", i, pulseWidth100To255[i]);
 	}
 
-	// The LA32 chip presumably has such a table inside as the internal computaions seem to be performed using fixed point math with 12-bit fractions
-	for (int i = 0; i < 4096; i++) {
-		exp2[i] = EXP2F(i / 4096.0f);
+	// The LA32 chip contains an exponent table inside. The table contains 12-bit integer values.
+	// The actual table size is 512 rows. The 9 higher bits of the fractional part of the argument are used as a lookup address.
+	// To improve the precision of computations, the lower bits are supposed to be used for interpolation as the LA32 chip also
+	// contains another 512-row table with inverted differences between the main table values.
+	for (int i = 0; i < 512; i++) {
+		exp9[i] = Bit16u(8191.5f - EXP2F(13.0f + ~i / 512.0f));
 	}
 
-	// found from sample analysis
-	resAmpFadeFactor[7] = 1.0f / 8.0f;
-	resAmpFadeFactor[6] = 2.0f / 8.0f;
-	resAmpFadeFactor[5] = 3.0f / 8.0f;
-	resAmpFadeFactor[4] = 5.0f / 8.0f;
-	resAmpFadeFactor[3] = 8.0f / 8.0f;
-	resAmpFadeFactor[2] = 12.0f / 8.0f;
-	resAmpFadeFactor[1] = 16.0f / 8.0f;
-	resAmpFadeFactor[0] = 31.0f / 8.0f;
-
-	for (int i = 0; i < 5120; i++) {
-		sinf10[i] = sin(FLOAT_PI * i / 2048.0f);
+	// There is a logarithmic sine table inside the LA32 chip. The table contains 13-bit integer values.
+	for (int i = 1; i < 512; i++) {
+		logsin9[i] = Bit16u(0.5f - LOG2F(sinf((i + 0.5f) / 1024.0f * FLOAT_PI)) * 1024.0f);
 	}
+
+	// The very first value is clamped to the maximum possible 13-bit integer
+	logsin9[0] = 8191;
+
+	// found from sample analysis
+	static const Bit8u resAmpDecayFactorTable[] = {31, 16, 12, 8, 5, 3, 2, 1};
+	resAmpDecayFactor = resAmpDecayFactorTable;
 }
 
 }
diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h
index 728d7ec..8b4580d 100644
--- a/audio/softsynth/mt32/Tables.h
+++ b/audio/softsynth/mt32/Tables.h
@@ -56,9 +56,10 @@ public:
 	// CONFIRMED:
 	Bit8u pulseWidth100To255[101];
 
-	float exp2[4096];
-	float resAmpFadeFactor[8];
-	float sinf10[5120];
+	Bit16u exp9[512];
+	Bit16u logsin9[512];
+
+	const Bit8u *resAmpDecayFactor;
 };
 
 }
diff --git a/audio/softsynth/mt32/mmath.h b/audio/softsynth/mt32/mmath.h
index f67433b..ee6a652 100644
--- a/audio/softsynth/mt32/mmath.h
+++ b/audio/softsynth/mt32/mmath.h
@@ -52,10 +52,6 @@ static inline float EXP2F(float x) {
 #endif
 }
 
-static inline float EXP2I(unsigned int i) {
-	return float(1 << (i >> 12)) * Tables::getInstance().exp2[i & 0x0FFF];
-}
-
 static inline float EXP10F(float x) {
 	return exp(FLOAT_LN_10 * x);
 }
diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk
index ed6b0d3..e7afdfd 100644
--- a/audio/softsynth/mt32/module.mk
+++ b/audio/softsynth/mt32/module.mk
@@ -6,6 +6,8 @@ MODULE_OBJS := \
 	DelayReverb.o \
 	FreeverbModel.o \
 	LA32Ramp.o \
+	LA32WaveGenerator.o \
+	LegacyWaveGenerator.o \
 	Part.o \
 	Partial.o \
 	PartialManager.o \
diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h
index 6812f7b..971a088 100644
--- a/audio/softsynth/mt32/mt32emu.h
+++ b/audio/softsynth/mt32/mt32emu.h
@@ -59,18 +59,6 @@
 #define MT32EMU_MONITOR_TVA 0
 #define MT32EMU_MONITOR_TVF 0
 
-// The WG algorithm involves dozens of transcendent maths, e.g. exponents and trigonometry.
-// Unfortunately, the majority of systems perform such computations inefficiently,
-// standard math libs and FPUs make no optimisations for single precision floats,
-// and use no LUTs to speedup computing internal taylor series. Though, there're rare exceptions,
-// and there's a hope it will become common soon.
-// So, this is the crucial point of speed optimisations. We have now eliminated all the transcendent maths
-// within the critical path and use LUTs instead.
-// Besides, since the LA32 chip is assumed to use similar LUTs inside, the overall emulation accuracy should be better.
-// 0: Use LUTs to speedup WG. Most common setting. You can expect about 50% performance boost.
-// 1: Use precise float math. Use this setting to achieve more accurate wave generator. If your system performs better with this setting, it is really notable. :)
-#define MT32EMU_ACCURATE_WG 0
-
 // Configuration
 // The maximum number of partials playing simultaneously
 #define MT32EMU_MAX_PARTIALS 32
@@ -87,6 +75,10 @@
 // 2: Use Bit-perfect Boss Reverb model aka BReverb (for developers, not much practical use)
 #define MT32EMU_USE_REVERBMODEL 1
 
+// 0: Use refined wave generator based on logarithmic fixed-point computations and LUTs
+// 1: Use legacy accurate wave generator based on float computations
+#define MT32EMU_ACCURATE_WG 0
+
 namespace MT32Emu
 {
 // The higher this number, the more memory will be used, but the more samples can be processed in one run -
@@ -110,6 +102,8 @@ const unsigned int MAX_PRERENDER_SAMPLES = 1024;
 #include "Tables.h"
 #include "Poly.h"
 #include "LA32Ramp.h"
+#include "LA32WaveGenerator.h"
+#include "LegacyWaveGenerator.h"
 #include "TVA.h"
 #include "TVP.h"
 #include "TVF.h"






More information about the Scummvm-git-logs mailing list