ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

cms.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "audio/softsynth/cms.h"
00024 #include "audio/null.h"
00025 
00026 #include "common/textconsole.h"
00027 #include "common/translation.h"
00028 #include "common/debug.h"
00029 
00030 // CMS/Gameblaster Emulation taken from DosBox
00031 
00032 #define LEFT    0x00
00033 #define RIGHT   0x01
00034 
00035 static const byte envelope[8][64] = {
00036     /* zero amplitude */
00037     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00038       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00039       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00040       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
00041     /* maximum amplitude */
00042     {15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
00043      15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
00044      15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
00045      15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 },
00046     /* single decay */
00047     {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
00048       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00049       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00050       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
00051     /* repetitive decay */
00052     {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
00053      15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
00054      15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
00055      15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 },
00056     /* single triangular */
00057     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
00058      15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
00059       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00060     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
00061     /* repetitive triangular */
00062     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
00063      15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
00064       0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
00065      15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 },
00066     /* single attack */
00067     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
00068       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00069       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00070       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
00071     /* repetitive attack */
00072     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
00073       0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
00074       0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
00075       0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 }
00076 };
00077 
00078 static const int amplitude_lookup[16] = {
00079      0*32767/16,  1*32767/16,  2*32767/16,  3*32767/16,
00080      4*32767/16,  5*32767/16,  6*32767/16,  7*32767/16,
00081      8*32767/16,  9*32767/16, 10*32767/16, 11*32767/16,
00082     12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16
00083 };
00084 
00085 void CMSEmulator::portWrite(int port, int val) {
00086     switch (port) {
00087     case 0x220:
00088         portWriteIntern(0, 1, val);
00089         break;
00090 
00091     case 0x221:
00092         _saa1099[0].selected_reg = val & 0x1f;
00093         if (_saa1099[0].selected_reg == 0x18 || _saa1099[0].selected_reg == 0x19) {
00094             /* clock the envelope channels */
00095             if (_saa1099[0].env_clock[0])
00096                 envelope(0, 0);
00097             if (_saa1099[0].env_clock[1])
00098                 envelope(0, 1);
00099         }
00100         break;
00101 
00102     case 0x222:
00103         portWriteIntern(1, 1, val);
00104         break;
00105 
00106     case 0x223:
00107         _saa1099[1].selected_reg = val & 0x1f;
00108         if (_saa1099[1].selected_reg == 0x18 || _saa1099[1].selected_reg == 0x19) {
00109             /* clock the envelope channels */
00110             if (_saa1099[1].env_clock[0])
00111                 envelope(1, 0);
00112             if (_saa1099[1].env_clock[1])
00113                 envelope(1, 1);
00114         }
00115         break;
00116 
00117     default:
00118         warning("CMSEmulator got port: 0x%X", port);
00119         break;
00120     }
00121 }
00122 
00123 void CMSEmulator::readBuffer(int16 *buffer, const int numSamples) {
00124     update(0, &buffer[0], numSamples);
00125     update(1, &buffer[0], numSamples);
00126 }
00127 
00128 void CMSEmulator::envelope(int chip, int ch) {
00129     SAA1099 *saa = &_saa1099[chip];
00130     if (saa->env_enable[ch]) {
00131         int step, mode, mask;
00132         mode = saa->env_mode[ch];
00133         /* step from 0..63 and then loop in steps 32..63 */
00134         step = saa->env_step[ch] = ((saa->env_step[ch] + 1) & 0x3f) | (saa->env_step[ch] & 0x20);
00135 
00136         mask = 15;
00137         if (saa->env_bits[ch])
00138             mask &= ~1;     /* 3 bit resolution, mask LSB */
00139 
00140         saa->channels[ch*3+0].envelope[ LEFT] =
00141         saa->channels[ch*3+1].envelope[ LEFT] =
00142         saa->channels[ch*3+2].envelope[ LEFT] = ::envelope[mode][step] & mask;
00143         if (saa->env_reverse_right[ch] & 0x01) {
00144             saa->channels[ch*3+0].envelope[RIGHT] =
00145             saa->channels[ch*3+1].envelope[RIGHT] =
00146             saa->channels[ch*3+2].envelope[RIGHT] = (15 - ::envelope[mode][step]) & mask;
00147         } else {
00148             saa->channels[ch*3+0].envelope[RIGHT] =
00149             saa->channels[ch*3+1].envelope[RIGHT] =
00150             saa->channels[ch*3+2].envelope[RIGHT] = ::envelope[mode][step] & mask;
00151         }
00152     } else {
00153         /* envelope mode off, set all envelope factors to 16 */
00154         saa->channels[ch*3+0].envelope[ LEFT] =
00155         saa->channels[ch*3+1].envelope[ LEFT] =
00156         saa->channels[ch*3+2].envelope[ LEFT] =
00157         saa->channels[ch*3+0].envelope[RIGHT] =
00158         saa->channels[ch*3+1].envelope[RIGHT] =
00159         saa->channels[ch*3+2].envelope[RIGHT] = 16;
00160     }
00161 }
00162 
00163 void CMSEmulator::update(int chip, int16 *buffer, int length) {
00164     struct SAA1099 *saa = &_saa1099[chip];
00165     int j, ch;
00166 
00167     if (chip == 0) {
00168         memset(buffer, 0, sizeof(int16)*length*2);
00169     }
00170 
00171     /* if the channels are disabled we're done */
00172     if (!saa->all_ch_enable) {
00173         return;
00174     }
00175 
00176     for (ch = 0; ch < 2; ch++) {
00177         switch (saa->noise_params[ch]) {
00178         case 0: saa->noise[ch].freq = 31250.0 * 2; break;
00179         case 1: saa->noise[ch].freq = 15625.0 * 2; break;
00180         case 2: saa->noise[ch].freq =  7812.5 * 2; break;
00181         case 3: saa->noise[ch].freq = saa->channels[ch * 3].freq; break;
00182         }
00183     }
00184 
00185     /* fill all data needed */
00186     for (j = 0; j < length; ++j) {
00187         int output_l = 0, output_r = 0;
00188 
00189         /* for each channel */
00190         for (ch = 0; ch < 6; ch++) {
00191             if (saa->channels[ch].freq == 0.0)
00192                 saa->channels[ch].freq = (double)((2 * 15625) << saa->channels[ch].octave) /
00193                 (511.0 - (double)saa->channels[ch].frequency);
00194 
00195             /* check the actual position in the square wave */
00196             saa->channels[ch].counter -= saa->channels[ch].freq;
00197             while (saa->channels[ch].counter < 0) {
00198                 /* calculate new frequency now after the half wave is updated */
00199                 saa->channels[ch].freq = (double)((2 * 15625) << saa->channels[ch].octave) /
00200                     (511.0 - (double)saa->channels[ch].frequency);
00201 
00202                 saa->channels[ch].counter += _sampleRate;
00203                 saa->channels[ch].level ^= 1;
00204 
00205                 /* eventually clock the envelope counters */
00206                 if (ch == 1 && saa->env_clock[0] == 0)
00207                     envelope(chip, 0);
00208                 if (ch == 4 && saa->env_clock[1] == 0)
00209                     envelope(chip, 1);
00210             }
00211 
00212             /* if the noise is enabled */
00213             if (saa->channels[ch].noise_enable) {
00214                 /* if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5) */
00215                 if (saa->noise[ch/3].level & 1) {
00216                     /* subtract to avoid overflows, also use only half amplitude */
00217                     output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16 / 2;
00218                     output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16 / 2;
00219                 }
00220             }
00221 
00222             /* if the square wave is enabled */
00223             if (saa->channels[ch].freq_enable) {
00224                 /* if the channel level is high */
00225                 if (saa->channels[ch].level & 1) {
00226                     output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16;
00227                     output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16;
00228                 }
00229             }
00230         }
00231 
00232         for (ch = 0; ch < 2; ch++) {
00233             /* check the actual position in noise generator */
00234             saa->noise[ch].counter -= saa->noise[ch].freq;
00235             while (saa->noise[ch].counter < 0) {
00236                 saa->noise[ch].counter += _sampleRate;
00237                 if (((saa->noise[ch].level & 0x4000) == 0) == ((saa->noise[ch].level & 0x0040) == 0) )
00238                     saa->noise[ch].level = (saa->noise[ch].level << 1) | 1;
00239                 else
00240                     saa->noise[ch].level <<= 1;
00241             }
00242         }
00243         /* write sound data to the buffer */
00244         buffer[j*2+0] = CLIP<int>(buffer[j*2+0] + output_l / 6, -32768, 32767);
00245         buffer[j*2+1] = CLIP<int>(buffer[j*2+1] + output_r / 6, -32768, 32767);
00246     }
00247 }
00248 
00249 void CMSEmulator::portWriteIntern(int chip, int offset, int data) {
00250     SAA1099 *saa = &_saa1099[chip];
00251     int reg = saa->selected_reg;
00252     int ch;
00253 
00254     switch (reg) {
00255     /* channel i amplitude */
00256     case 0x00:
00257     case 0x01:
00258     case 0x02:
00259     case 0x03:
00260     case 0x04:
00261     case 0x05:
00262         ch = reg & 7;
00263         saa->channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f];
00264         saa->channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f];
00265         break;
00266 
00267     /* channel i frequency */
00268     case 0x08:
00269     case 0x09:
00270     case 0x0a:
00271     case 0x0b:
00272     case 0x0c:
00273     case 0x0d:
00274         ch = reg & 7;
00275         saa->channels[ch].frequency = data & 0xff;
00276         break;
00277 
00278     /* channel i octave */
00279     case 0x10:
00280     case 0x11:
00281     case 0x12:
00282         ch = (reg - 0x10) << 1;
00283         saa->channels[ch + 0].octave = data & 0x07;
00284         saa->channels[ch + 1].octave = (data >> 4) & 0x07;
00285         break;
00286 
00287     /* channel i frequency enable */
00288     case 0x14:
00289         saa->channels[0].freq_enable = data & 0x01;
00290         saa->channels[1].freq_enable = data & 0x02;
00291         saa->channels[2].freq_enable = data & 0x04;
00292         saa->channels[3].freq_enable = data & 0x08;
00293         saa->channels[4].freq_enable = data & 0x10;
00294         saa->channels[5].freq_enable = data & 0x20;
00295         break;
00296 
00297     /* channel i noise enable */
00298     case 0x15:
00299         saa->channels[0].noise_enable = data & 0x01;
00300         saa->channels[1].noise_enable = data & 0x02;
00301         saa->channels[2].noise_enable = data & 0x04;
00302         saa->channels[3].noise_enable = data & 0x08;
00303         saa->channels[4].noise_enable = data & 0x10;
00304         saa->channels[5].noise_enable = data & 0x20;
00305         break;
00306 
00307     /* noise generators parameters */
00308     case 0x16:
00309         saa->noise_params[0] = data & 0x03;
00310         saa->noise_params[1] = (data >> 4) & 0x03;
00311         break;
00312 
00313     /* envelope generators parameters */
00314     case 0x18:
00315     case 0x19:
00316         ch = reg - 0x18;
00317         saa->env_reverse_right[ch] = data & 0x01;
00318         saa->env_mode[ch] = (data >> 1) & 0x07;
00319         saa->env_bits[ch] = data & 0x10;
00320         saa->env_clock[ch] = data & 0x20;
00321         saa->env_enable[ch] = data & 0x80;
00322         /* reset the envelope */
00323         saa->env_step[ch] = 0;
00324         break;
00325 
00326     /* channels enable & reset generators */
00327     case 0x1c:
00328         saa->all_ch_enable = data & 0x01;
00329         saa->sync_state = data & 0x02;
00330         if (data & 0x02) {
00331             int i;
00332             /* Synch & Reset generators */
00333             for (i = 0; i < 6; i++) {
00334                 saa->channels[i].level = 0;
00335                 saa->channels[i].counter = 0.0;
00336             }
00337         }
00338         break;
00339 
00340     default:
00341         // The CMS allows all registers to be written, so we just output some debug
00342         // message here
00343         debug(5, "CMS Unknown write to reg %x with %x",reg, data);
00344     }
00345 }
00346 
00347 class CMSMusicPlugin : public NullMusicPlugin {
00348 public:
00349     const char *getName() const {
00350         return _s("Creative Music System emulator");
00351     }
00352 
00353     const char *getId() const {
00354         return "cms";
00355     }
00356 
00357     MusicDevices getDevices() const;
00358 };
00359 
00360 MusicDevices CMSMusicPlugin::getDevices() const {
00361     MusicDevices devices;
00362     devices.push_back(MusicDevice(this, "", MT_CMS));
00363     return devices;
00364 }
00365 
00366 //#if PLUGIN_ENABLED_DYNAMIC(CMS)
00367     //REGISTER_PLUGIN_DYNAMIC(CMS, PLUGIN_TYPE_MUSIC, CMSMusicPlugin);
00368 //#else
00369     REGISTER_PLUGIN_STATIC(CMS, PLUGIN_TYPE_MUSIC, CMSMusicPlugin);
00370 //#endif


Generated on Sat Feb 16 2019 05:00:47 for ResidualVM by doxygen 1.7.1
curved edge   curved edge