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

alsa_opl.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 /* OPL implementation for hardware OPL using ALSA Direct FM API.
00024  *
00025  * Caveats and limitations:
00026  * - Pretends to be a softsynth (emitting silence).
00027  * - Dual OPL2 mode requires OPL3 hardware.
00028  * - Every register write leads to a series of register writes on the hardware,
00029  *   due to the lack of direct register access in the ALSA Direct FM API.
00030  * - No timers
00031  */
00032 
00033 #define FORBIDDEN_SYMBOL_ALLOW_ALL
00034 #include "common/scummsys.h"
00035 
00036 #include "common/debug.h"
00037 #include "common/str.h"
00038 #include "audio/fmopl.h"
00039 
00040 #include <sys/ioctl.h>
00041 #include <alsa/asoundlib.h>
00042 #include <sound/asound_fm.h>
00043 
00044 namespace OPL {
00045 namespace ALSA {
00046 
00047 class OPL : public ::OPL::RealOPL {
00048 private:
00049     enum {
00050         kOpl2Voices = 9,
00051         kVoices = 18,
00052         kOpl2Operators = 18,
00053         kOperators = 36
00054     };
00055 
00056     Config::OplType _type;
00057     int _iface;
00058     snd_hwdep_t *_opl;
00059     snd_dm_fm_voice _oper[kOperators];
00060     snd_dm_fm_note _voice[kVoices];
00061     snd_dm_fm_params _params;
00062     int index[2];
00063     static const int voiceToOper0[kVoices];
00064     static const int regOffsetToOper[0x20];
00065 
00066     void writeOplReg(int c, int r, int v);
00067     void clear();
00068 
00069 public:
00070     OPL(Config::OplType type);
00071     ~OPL();
00072 
00073     bool init();
00074     void reset();
00075 
00076     void write(int a, int v);
00077     byte read(int a);
00078 
00079     void writeReg(int r, int v);
00080 };
00081 
00082 const int OPL::voiceToOper0[OPL::kVoices] =
00083     { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 };
00084 
00085 const int OPL::regOffsetToOper[0x20] =
00086     { 0,  1,  2,  3,  4,  5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
00087      12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
00088 
00089 OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) {
00090 }
00091 
00092 OPL::~OPL() {
00093     stop();
00094 
00095     if (_opl) {
00096         snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
00097         snd_hwdep_close(_opl);
00098     }
00099 }
00100 
00101 void OPL::clear() {
00102     index[0] = index[1] = 0;
00103 
00104     memset(_oper, 0, sizeof(_oper));
00105     memset(_voice, 0, sizeof(_voice));
00106     memset(&_params, 0, sizeof(_params));
00107 
00108     for (int i = 0; i < kOperators; ++i) {
00109         _oper[i].op = (i / 3) % 2;
00110         _oper[i].voice = (i / 6) * 3 + (i % 3);
00111     }
00112 
00113     for (int i = 0; i < kVoices; ++i)
00114         _voice[i].voice = i;
00115 
00116     // For OPL3 hardware we need to set up the panning in OPL2 modes
00117     if (_iface == SND_HWDEP_IFACE_OPL3) {
00118         if (_type == Config::kDualOpl2) {
00119             for (int i = 0; i < kOpl2Operators; ++i)
00120                 _oper[i].left = 1; // FIXME below
00121             for (int i = kOpl2Operators; i < kOperators; ++i)
00122                 _oper[i].right = 1;
00123         } else if (_type == Config::kOpl2) {
00124             for (int i = 0; i < kOpl2Operators; ++i) {
00125                 _oper[i].left = 1;
00126                 _oper[i].right = 1;
00127             }
00128         }
00129     }
00130 }
00131 
00132 bool OPL::init() {
00133     clear();
00134 
00135     int card = -1;
00136     snd_ctl_t *ctl;
00137     snd_hwdep_info_t *info;
00138     snd_hwdep_info_alloca(&info);
00139 
00140     int iface = SND_HWDEP_IFACE_OPL3;
00141     if (_type == Config::kOpl2)
00142         iface = SND_HWDEP_IFACE_OPL2;
00143 
00144     // Look for OPL hwdep interface
00145     while (!snd_card_next(&card) && card >= 0) {
00146         int dev = -1;
00147         Common::String name = Common::String::format("hw:%d", card);
00148 
00149         if (snd_ctl_open(&ctl, name.c_str(), 0) < 0)
00150             continue;
00151 
00152         while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) {
00153             name = Common::String::format("hw:%d,%d", card, dev);
00154 
00155             if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0)
00156                 continue;
00157 
00158             if (!snd_hwdep_info(_opl, info)) {
00159                 int found = snd_hwdep_info_get_iface(info);
00160                 // OPL3 can be used for (Dual) OPL2 mode
00161                 if (found == iface || found == SND_HWDEP_IFACE_OPL3) {
00162                     snd_ctl_close(ctl);
00163                     _iface = found;
00164                     reset();
00165                     return true;
00166                 }
00167             }
00168 
00169             // Wrong interface, try next device
00170             snd_hwdep_close(_opl);
00171             _opl = nullptr;
00172         }
00173 
00174         snd_ctl_close(ctl);
00175     }
00176 
00177     return false;
00178 }
00179 
00180 void OPL::reset() {
00181     snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
00182     if (_iface == SND_HWDEP_IFACE_OPL3)
00183         snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3);
00184 
00185     clear();
00186 
00187     // Sync up with the hardware
00188     snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
00189     for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i)
00190         snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]);
00191     for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i)
00192         snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]);
00193 }
00194 
00195 void OPL::write(int port, int val) {
00196     val &= 0xff;
00197     int chip = (port & 2) >> 1;
00198 
00199     if (port & 1) {
00200         switch(_type) {
00201         case Config::kOpl2:
00202             writeOplReg(0, index[0], val);
00203             break;
00204         case Config::kDualOpl2:
00205             if (port & 8) {
00206                 writeOplReg(0, index[0], val);
00207                 writeOplReg(1, index[1], val);
00208             } else
00209                 writeOplReg(chip, index[chip], val);
00210             break;
00211         case Config::kOpl3:
00212             writeOplReg(chip, index[chip], val);
00213         }
00214     } else {
00215         switch(_type) {
00216         case Config::kOpl2:
00217             index[0] = val;
00218             break;
00219         case Config::kDualOpl2:
00220             if (port & 8) {
00221                 index[0] = val;
00222                 index[1] = val;
00223             } else
00224                 index[chip] = val;
00225             break;
00226         case Config::kOpl3:
00227             index[chip] = val;
00228         }
00229     }
00230 }
00231 
00232 byte OPL::read(int port) {
00233     return 0;
00234 }
00235 
00236 void OPL::writeReg(int r, int v) {
00237     switch (_type) {
00238     case Config::kOpl2:
00239         writeOplReg(0, r, v);
00240         break;
00241     case Config::kDualOpl2:
00242         writeOplReg(0, r, v);
00243         writeOplReg(1, r, v);
00244         break;
00245     case Config::kOpl3:
00246         writeOplReg(r >= 0x100, r & 0xff, v);
00247     }
00248 }
00249 
00250 void OPL::writeOplReg(int c, int r, int v) {
00251     if (r == 0x04 && c == 1 && _type == Config::kOpl3) {
00252         snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast<void *>(v & 0x3f));
00253     } else if (r == 0x08 && c == 0) {
00254         _params.kbd_split = (v >> 6) & 0x1;
00255         snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
00256     } else if (r == 0xbd && c == 0) {
00257         _params.hihat = v & 0x1;
00258         _params.cymbal = (v >> 1) & 0x1;
00259         _params.tomtom = (v >> 2) & 0x1;
00260         _params.snare = (v >> 3) & 0x1;
00261         _params.bass = (v >> 4) & 0x1;
00262         _params.rhythm = (v >> 5) & 0x1;
00263         _params.vib_depth = (v >> 6) & 0x1;
00264         _params.am_depth = (v >> 7) & 0x1;
00265         snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
00266     } else if (r < 0xa0 || r >= 0xe0) {
00267         // Operator
00268         int idx = regOffsetToOper[r & 0x1f];
00269 
00270         if (idx == -1)
00271             return;
00272 
00273         if (c == 1)
00274             idx += kOpl2Operators;
00275 
00276         switch (r & 0xf0) {
00277         case 0x20:
00278         case 0x30:
00279             _oper[idx].harmonic = v & 0xf;
00280             _oper[idx].kbd_scale = (v >> 4) & 0x1;
00281             _oper[idx].do_sustain = (v >> 5) & 0x1;
00282             _oper[idx].vibrato = (v >> 6) & 0x1;
00283             _oper[idx].am = (v >> 7) & 0x1;
00284             snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
00285             break;
00286         case 0x40:
00287         case 0x50:
00288             _oper[idx].volume = ~v & 0x3f;
00289             _oper[idx].scale_level = (v >> 6) & 0x3;
00290             snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
00291             break;
00292         case 0x60:
00293         case 0x70:
00294             _oper[idx].decay = v & 0xf;
00295             _oper[idx].attack = (v >> 4) & 0xf;
00296             snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
00297             break;
00298         case 0x80:
00299         case 0x90:
00300             _oper[idx].release = v & 0xf;
00301             _oper[idx].sustain = (v >> 4) & 0xf;
00302             snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
00303             break;
00304         case 0xe0:
00305         case 0xf0:
00306             _oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3);
00307             snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
00308         }
00309     } else {
00310         // Voice
00311         int idx = r & 0xf;
00312 
00313         if (idx >= kOpl2Voices)
00314             return;
00315 
00316         if (c == 1)
00317             idx += kOpl2Voices;
00318 
00319         int opIdx = voiceToOper0[idx];
00320 
00321         switch (r & 0xf0) {
00322         case 0xa0:
00323             _voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff);
00324             snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
00325             break;
00326         case 0xb0:
00327             _voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff);
00328             _voice[idx].octave = (v >> 2) & 0x7;
00329             _voice[idx].key_on = (v >> 5) & 0x1;
00330             snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
00331             break;
00332         case 0xc0:
00333             _oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1;
00334             _oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7;
00335             if (_type == Config::kOpl3) {
00336                 _oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1;
00337                 _oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1;
00338             }
00339             snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]);
00340         }
00341     }
00342 }
00343 
00344 OPL *create(Config::OplType type) {
00345     return new OPL(type);
00346 }
00347 
00348 } // End of namespace ALSA
00349 } // End of namespace OPL


Generated on Sat May 18 2019 05:00:54 for ResidualVM by doxygen 1.7.1
curved edge   curved edge