surroundopl.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /*
  2. * Adplug - Replayer for many OPL2/OPL3 audio file formats.
  3. * Copyright (C) 1999 - 2010 Simon Peter, <dn.tlp@gmx.net>, et al.
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Lesser General Public
  7. * License as published by the Free Software Foundation; either
  8. * version 2.1 of the License, or (at your option) any later version.
  9. *
  10. * This library is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with this library; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. *
  19. * surroundopl.cpp - Wrapper class to provide a surround/harmonic effect
  20. * for another OPL emulator, by Adam Nielsen <malvineous@shikadi.net>
  21. *
  22. * Stereo harmonic algorithm by Adam Nielsen <malvineous@shikadi.net>
  23. * Please give credit if you use this algorithm elsewhere :-)
  24. */
  25. #include <math.h> // for pow()
  26. #include "surroundopl.h"
  27. //#include "debug.h"
  28. CSurroundopl::CSurroundopl(Copl *a, Copl *b, bool use16bit, double opl_freq, double freq_offset)
  29. : a(a), b(b), freq_offset(freq_offset), opl_freq(opl_freq), bufsize(4096), use16bit(use16bit)
  30. {
  31. currType = TYPE_OPL2;
  32. this->lbuf = new short[this->bufsize];
  33. this->rbuf = new short[this->bufsize];
  34. };
  35. CSurroundopl::~CSurroundopl()
  36. {
  37. delete[] this->rbuf;
  38. delete[] this->lbuf;
  39. delete a;
  40. delete b;
  41. }
  42. void CSurroundopl::update(short *buf, int samples)
  43. {
  44. if (samples * 2 > this->bufsize) {
  45. // Need to realloc the buffer
  46. delete[] this->rbuf;
  47. delete[] this->lbuf;
  48. this->bufsize = samples * 2;
  49. this->lbuf = new short[this->bufsize];
  50. this->rbuf = new short[this->bufsize];
  51. }
  52. a->update(this->lbuf, samples);
  53. b->update(this->rbuf, samples);
  54. // Copy the two mono OPL buffers into the stereo buffer
  55. for (int i = 0; i < samples; i++) {
  56. if (this->use16bit) {
  57. buf[i * 2] = this->lbuf[i];
  58. buf[i * 2 + 1] = this->rbuf[i];
  59. } else {
  60. ((char *)buf)[i * 2] = ((char *)this->lbuf)[i];
  61. ((char *)buf)[i * 2 + 1] = ((char *)this->rbuf)[i];
  62. }
  63. }
  64. }
  65. // template methods
  66. void CSurroundopl::write(int reg, int val)
  67. {
  68. a->write(reg, val);
  69. // Transpose the other channel to produce the harmonic effect
  70. int iChannel = -1;
  71. int iRegister = reg; // temp
  72. int iValue = val; // temp
  73. if ((iRegister >> 4 == 0xA) || (iRegister >> 4 == 0xB)) iChannel = iRegister & 0x0F;
  74. // Remember the FM state, so that the harmonic effect can access
  75. // previously assigned register values.
  76. /*if (((iRegister >> 4 == 0xB) && (iValue & 0x20) && !(this->iFMReg[iRegister] & 0x20)) ||
  77. (iRegister == 0xBD) && (
  78. ((iValue & 0x01) && !(this->iFMReg[0xBD] & 0x01))
  79. )) {
  80. this->iFMReg[iRegister] = iValue;
  81. }*/
  82. this->iFMReg[iRegister] = iValue;
  83. if ((iChannel >= 0)) {// && (i == 1)) {
  84. unsigned char iBlock = (this->iFMReg[0xB0 + iChannel] >> 2) & 0x07;
  85. unsigned short iFNum = ((this->iFMReg[0xB0 + iChannel] & 0x03) << 8) | this->iFMReg[0xA0 + iChannel];
  86. //double dbOriginalFreq = 50000.0 * (double)iFNum * pow(2, iBlock - 20);
  87. double dbOriginalFreq = opl_freq * (double)iFNum * pow(2.0, iBlock - 20);
  88. unsigned char iNewBlock = iBlock;
  89. unsigned short iNewFNum;
  90. // Adjust the frequency and calculate the new FNum
  91. //double dbNewFNum = (dbOriginalFreq+(dbOriginalFreq/FREQ_OFFSET)) / (50000.0 * pow(2.0, iNewBlock - 20));
  92. //#define calcFNum() ((dbOriginalFreq+(dbOriginalFreq/FREQ_OFFSET)) / (50000.0 * pow(2.0, iNewBlock - 20)))
  93. #define calcFNum() ((dbOriginalFreq + (dbOriginalFreq / freq_offset)) / (opl_freq * pow(2.0, iNewBlock - 20)))
  94. double dbNewFNum = calcFNum();
  95. // Make sure it's in range for the OPL chip
  96. if (dbNewFNum > 1023 - NEWBLOCK_LIMIT) {
  97. // It's too high, so move up one block (octave) and recalculate
  98. if (iNewBlock > 6) {
  99. // Uh oh, we're already at the highest octave!
  100. // AdPlug_LogWrite("OPL WARN: FNum %d/B#%d would need block 8+ after being transposed (new FNum is %d)\n",
  101. // iFNum, iBlock, (int)dbNewFNum);
  102. // The best we can do here is to just play the same note out of the second OPL, so at least it shouldn't
  103. // sound *too* bad (hopefully it will just miss out on the nice harmonic.)
  104. iNewBlock = iBlock;
  105. iNewFNum = iFNum;
  106. } else {
  107. iNewBlock++;
  108. iNewFNum = (unsigned short)calcFNum();
  109. }
  110. } else if (dbNewFNum < 0 + NEWBLOCK_LIMIT) {
  111. // It's too low, so move down one block (octave) and recalculate
  112. if (iNewBlock == 0) {
  113. // Uh oh, we're already at the lowest octave!
  114. // AdPlug_LogWrite("OPL WARN: FNum %d/B#%d would need block -1 after being transposed (new FNum is %d)!\n",
  115. // iFNum, iBlock, (int)dbNewFNum);
  116. // The best we can do here is to just play the same note out of the second OPL, so at least it shouldn't
  117. // sound *too* bad (hopefully it will just miss out on the nice harmonic.)
  118. iNewBlock = iBlock;
  119. iNewFNum = iFNum;
  120. } else {
  121. iNewBlock--;
  122. iNewFNum = (unsigned short)calcFNum();
  123. }
  124. } else {
  125. // Original calculation is within range, use that
  126. iNewFNum = (unsigned short)dbNewFNum;
  127. }
  128. // Sanity check
  129. if (iNewFNum > 1023) {
  130. // Uh oh, the new FNum is still out of range! (This shouldn't happen)
  131. // AdPlug_LogWrite("OPL ERR: Original note (FNum %d/B#%d is still out of range after change to FNum %d/B#%d!\n",
  132. // iFNum, iBlock, iNewFNum, iNewBlock);
  133. // The best we can do here is to just play the same note out of the second OPL, so at least it shouldn't
  134. // sound *too* bad (hopefully it will just miss out on the nice harmonic.)
  135. iNewBlock = iBlock;
  136. iNewFNum = iFNum;
  137. }
  138. if ((iRegister >= 0xB0) && (iRegister <= 0xB8)) {
  139. // Overwrite the supplied value with the new F-Number and Block.
  140. iValue = (iValue & ~0x1F) | (iNewBlock << 2) | ((iNewFNum >> 8) & 0x03);
  141. this->iCurrentTweakedBlock[iChannel] = iNewBlock; // save it so we don't have to update register 0xB0 later on
  142. this->iCurrentFNum[iChannel] = (unsigned char)iNewFNum;
  143. if (this->iTweakedFMReg[0xA0 + iChannel] != (iNewFNum & 0xFF)) {
  144. // Need to write out low bits
  145. unsigned char iAdditionalReg = 0xA0 + iChannel;
  146. unsigned char iAdditionalValue = iNewFNum & 0xFF;
  147. b->write(iAdditionalReg, iAdditionalValue);
  148. this->iTweakedFMReg[iAdditionalReg] = iAdditionalValue;
  149. }
  150. } else if ((iRegister >= 0xA0) && (iRegister <= 0xA8)) {
  151. // Overwrite the supplied value with the new F-Number.
  152. iValue = iNewFNum & 0xFF;
  153. // See if we need to update the block number, which is stored in a different register
  154. unsigned char iNewB0Value = (this->iFMReg[0xB0 + iChannel] & ~0x1F) | (iNewBlock << 2) | ((iNewFNum >> 8) & 0x03);
  155. if (
  156. (iNewB0Value & 0x20) && // but only update if there's a note currently playing (otherwise we can just wait
  157. (this->iTweakedFMReg[0xB0 + iChannel] != iNewB0Value) // until the next noteon and update it then)
  158. ) {
  159. // AdPlug_LogWrite("OPL INFO: CH%d - FNum %d/B#%d -> FNum %d/B#%d == keyon register update!\n",
  160. // iChannel, iFNum, iBlock, iNewFNum, iNewBlock);
  161. // The note is already playing, so we need to adjust the upper bits too
  162. unsigned char iAdditionalReg = 0xB0 + iChannel;
  163. b->write(iAdditionalReg, iNewB0Value);
  164. this->iTweakedFMReg[iAdditionalReg] = iNewB0Value;
  165. } // else the note is not playing, the upper bits will be set when the note is next played
  166. } // if (register 0xB0 or 0xA0)
  167. } // if (a register we're interested in)
  168. // Now write to the original register with a possibly modified value
  169. b->write(iRegister, iValue);
  170. this->iTweakedFMReg[iRegister] = iValue;
  171. };
  172. void CSurroundopl::init()
  173. {
  174. a->init();
  175. b->init();
  176. }