From bb8132beb82a4f5d5a3f8db0fb9dec08b2965b72 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 7 Jul 2015 20:41:30 -0400 Subject: AUDIO: Move ALSA OPL 'driver' out of softsynth Might eventually be worth moving to backends/ --- audio/alsa_opl.cpp | 349 +++++++++++++++++++++++++++++++++++++++++++ audio/module.mk | 2 +- audio/softsynth/opl/alsa.cpp | 349 ------------------------------------------- 3 files changed, 350 insertions(+), 350 deletions(-) create mode 100644 audio/alsa_opl.cpp delete mode 100644 audio/softsynth/opl/alsa.cpp (limited to 'audio') diff --git a/audio/alsa_opl.cpp b/audio/alsa_opl.cpp new file mode 100644 index 0000000000..6b9e48e987 --- /dev/null +++ b/audio/alsa_opl.cpp @@ -0,0 +1,349 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* OPL implementation for hardware OPL using ALSA Direct FM API. + * + * Caveats and limitations: + * - Pretends to be a softsynth (emitting silence). + * - Dual OPL2 mode requires OPL3 hardware. + * - Every register write leads to a series of register writes on the hardware, + * due to the lack of direct register access in the ALSA Direct FM API. + * - No timers + */ + +#define FORBIDDEN_SYMBOL_ALLOW_ALL +#include "common/scummsys.h" + +#include "common/debug.h" +#include "common/str.h" +#include "audio/fmopl.h" + +#include +#include +#include + +namespace OPL { +namespace ALSA { + +class OPL : public ::OPL::RealOPL { +private: + enum { + kOpl2Voices = 9, + kVoices = 18, + kOpl2Operators = 18, + kOperators = 36 + }; + + Config::OplType _type; + int _iface; + snd_hwdep_t *_opl; + snd_dm_fm_voice _oper[kOperators]; + snd_dm_fm_note _voice[kVoices]; + snd_dm_fm_params _params; + int index[2]; + static const int voiceToOper0[kVoices]; + static const int regOffsetToOper[0x20]; + + void writeOplReg(int c, int r, int v); + void clear(); + +public: + OPL(Config::OplType type); + ~OPL(); + + bool init(); + void reset(); + + void write(int a, int v); + byte read(int a); + + void writeReg(int r, int v); +}; + +const int OPL::voiceToOper0[OPL::kVoices] = + { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 }; + +const int OPL::regOffsetToOper[0x20] = + { 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) { +} + +OPL::~OPL() { + stop(); + + if (_opl) { + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr); + snd_hwdep_close(_opl); + } +} + +void OPL::clear() { + index[0] = index[1] = 0; + + memset(_oper, 0, sizeof(_oper)); + memset(_voice, 0, sizeof(_voice)); + memset(&_params, 0, sizeof(_params)); + + for (int i = 0; i < kOperators; ++i) { + _oper[i].op = (i / 3) % 2; + _oper[i].voice = (i / 6) * 3 + (i % 3); + } + + for (int i = 0; i < kVoices; ++i) + _voice[i].voice = i; + + // For OPL3 hardware we need to set up the panning in OPL2 modes + if (_iface == SND_HWDEP_IFACE_OPL3) { + if (_type == Config::kDualOpl2) { + for (int i = 0; i < kOpl2Operators; ++i) + _oper[i].left = 1; // FIXME below + for (int i = kOpl2Operators; i < kOperators; ++i) + _oper[i].right = 1; + } else if (_type == Config::kOpl2) { + for (int i = 0; i < kOpl2Operators; ++i) { + _oper[i].left = 1; + _oper[i].right = 1; + } + } + } +} + +bool OPL::init() { + clear(); + + int card = -1; + snd_ctl_t *ctl; + snd_hwdep_info_t *info; + snd_hwdep_info_alloca(&info); + + int iface = SND_HWDEP_IFACE_OPL3; + if (_type == Config::kOpl2) + iface = SND_HWDEP_IFACE_OPL2; + + // Look for OPL hwdep interface + while (!snd_card_next(&card) && card >= 0) { + int dev = -1; + Common::String name = Common::String::format("hw:%d", card); + + if (snd_ctl_open(&ctl, name.c_str(), 0) < 0) + continue; + + while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) { + name = Common::String::format("hw:%d,%d", card, dev); + + if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0) + continue; + + if (!snd_hwdep_info(_opl, info)) { + int found = snd_hwdep_info_get_iface(info); + // OPL3 can be used for (Dual) OPL2 mode + if (found == iface || found == SND_HWDEP_IFACE_OPL3) { + snd_ctl_close(ctl); + _iface = found; + reset(); + return true; + } + } + + // Wrong interface, try next device + snd_hwdep_close(_opl); + _opl = nullptr; + } + + snd_ctl_close(ctl); + } + + return false; +} + +void OPL::reset() { + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr); + if (_iface == SND_HWDEP_IFACE_OPL3) + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3); + + clear(); + + // Sync up with the hardware + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); + for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i) + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]); + for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i) + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]); +} + +void OPL::write(int port, int val) { + val &= 0xff; + int chip = (port & 2) >> 1; + + if (port & 1) { + switch(_type) { + case Config::kOpl2: + writeOplReg(0, index[0], val); + break; + case Config::kDualOpl2: + if (port & 8) { + writeOplReg(0, index[0], val); + writeOplReg(1, index[1], val); + } else + writeOplReg(chip, index[chip], val); + break; + case Config::kOpl3: + writeOplReg(chip, index[chip], val); + } + } else { + switch(_type) { + case Config::kOpl2: + index[0] = val; + break; + case Config::kDualOpl2: + if (port & 8) { + index[0] = val; + index[1] = val; + } else + index[chip] = val; + break; + case Config::kOpl3: + index[chip] = val; + } + } +} + +byte OPL::read(int port) { + return 0; +} + +void OPL::writeReg(int r, int v) { + switch (_type) { + case Config::kOpl2: + writeOplReg(0, r, v); + break; + case Config::kDualOpl2: + writeOplReg(0, r, v); + writeOplReg(1, r, v); + break; + case Config::kOpl3: + writeOplReg(r >= 0x100, r & 0xff, v); + } +} + +void OPL::writeOplReg(int c, int r, int v) { + if (r == 0x04 && c == 1 && _type == Config::kOpl3) { + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast(v & 0x3f)); + } else if (r == 0x08 && c == 0) { + _params.kbd_split = (v >> 6) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); + } else if (r == 0xbd && c == 0) { + _params.hihat = v & 0x1; + _params.cymbal = (v >> 1) & 0x1; + _params.tomtom = (v >> 2) & 0x1; + _params.snare = (v >> 3) & 0x1; + _params.bass = (v >> 4) & 0x1; + _params.rhythm = (v >> 5) & 0x1; + _params.vib_depth = (v >> 6) & 0x1; + _params.am_depth = (v >> 7) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); + } else if (r < 0xa0 || r >= 0xe0) { + // Operator + int idx = regOffsetToOper[r & 0x1f]; + + if (idx == -1) + return; + + if (c == 1) + idx += kOpl2Operators; + + switch (r & 0xf0) { + case 0x20: + case 0x30: + _oper[idx].harmonic = v & 0xf; + _oper[idx].kbd_scale = (v >> 4) & 0x1; + _oper[idx].do_sustain = (v >> 5) & 0x1; + _oper[idx].vibrato = (v >> 6) & 0x1; + _oper[idx].am = (v >> 7) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0x40: + case 0x50: + _oper[idx].volume = ~v & 0x3f; + _oper[idx].scale_level = (v >> 6) & 0x3; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0x60: + case 0x70: + _oper[idx].decay = v & 0xf; + _oper[idx].attack = (v >> 4) & 0xf; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0x80: + case 0x90: + _oper[idx].release = v & 0xf; + _oper[idx].sustain = (v >> 4) & 0xf; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0xe0: + case 0xf0: + _oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3); + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + } + } else { + // Voice + int idx = r & 0xf; + + if (idx >= kOpl2Voices) + return; + + if (c == 1) + idx += kOpl2Voices; + + int opIdx = voiceToOper0[idx]; + + switch (r & 0xf0) { + case 0xa0: + _voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff); + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]); + break; + case 0xb0: + _voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff); + _voice[idx].octave = (v >> 2) & 0x7; + _voice[idx].key_on = (v >> 5) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]); + break; + case 0xc0: + _oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1; + _oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7; + if (_type == Config::kOpl3) { + _oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1; + _oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1; + } + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]); + } + } +} + +OPL *create(Config::OplType type) { + return new OPL(type); +} + +} // End of namespace ALSA +} // End of namespace OPL diff --git a/audio/module.mk b/audio/module.mk index 7743c49a60..9e002d57ba 100644 --- a/audio/module.mk +++ b/audio/module.mk @@ -60,7 +60,7 @@ MODULE_OBJS := \ ifdef USE_ALSA MODULE_OBJS += \ - softsynth/opl/alsa.o + alsa_opl.o endif ifndef USE_ARM_SOUND_ASM diff --git a/audio/softsynth/opl/alsa.cpp b/audio/softsynth/opl/alsa.cpp deleted file mode 100644 index 6b9e48e987..0000000000 --- a/audio/softsynth/opl/alsa.cpp +++ /dev/null @@ -1,349 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -/* OPL implementation for hardware OPL using ALSA Direct FM API. - * - * Caveats and limitations: - * - Pretends to be a softsynth (emitting silence). - * - Dual OPL2 mode requires OPL3 hardware. - * - Every register write leads to a series of register writes on the hardware, - * due to the lack of direct register access in the ALSA Direct FM API. - * - No timers - */ - -#define FORBIDDEN_SYMBOL_ALLOW_ALL -#include "common/scummsys.h" - -#include "common/debug.h" -#include "common/str.h" -#include "audio/fmopl.h" - -#include -#include -#include - -namespace OPL { -namespace ALSA { - -class OPL : public ::OPL::RealOPL { -private: - enum { - kOpl2Voices = 9, - kVoices = 18, - kOpl2Operators = 18, - kOperators = 36 - }; - - Config::OplType _type; - int _iface; - snd_hwdep_t *_opl; - snd_dm_fm_voice _oper[kOperators]; - snd_dm_fm_note _voice[kVoices]; - snd_dm_fm_params _params; - int index[2]; - static const int voiceToOper0[kVoices]; - static const int regOffsetToOper[0x20]; - - void writeOplReg(int c, int r, int v); - void clear(); - -public: - OPL(Config::OplType type); - ~OPL(); - - bool init(); - void reset(); - - void write(int a, int v); - byte read(int a); - - void writeReg(int r, int v); -}; - -const int OPL::voiceToOper0[OPL::kVoices] = - { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 }; - -const int OPL::regOffsetToOper[0x20] = - { 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, - 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - -OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) { -} - -OPL::~OPL() { - stop(); - - if (_opl) { - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr); - snd_hwdep_close(_opl); - } -} - -void OPL::clear() { - index[0] = index[1] = 0; - - memset(_oper, 0, sizeof(_oper)); - memset(_voice, 0, sizeof(_voice)); - memset(&_params, 0, sizeof(_params)); - - for (int i = 0; i < kOperators; ++i) { - _oper[i].op = (i / 3) % 2; - _oper[i].voice = (i / 6) * 3 + (i % 3); - } - - for (int i = 0; i < kVoices; ++i) - _voice[i].voice = i; - - // For OPL3 hardware we need to set up the panning in OPL2 modes - if (_iface == SND_HWDEP_IFACE_OPL3) { - if (_type == Config::kDualOpl2) { - for (int i = 0; i < kOpl2Operators; ++i) - _oper[i].left = 1; // FIXME below - for (int i = kOpl2Operators; i < kOperators; ++i) - _oper[i].right = 1; - } else if (_type == Config::kOpl2) { - for (int i = 0; i < kOpl2Operators; ++i) { - _oper[i].left = 1; - _oper[i].right = 1; - } - } - } -} - -bool OPL::init() { - clear(); - - int card = -1; - snd_ctl_t *ctl; - snd_hwdep_info_t *info; - snd_hwdep_info_alloca(&info); - - int iface = SND_HWDEP_IFACE_OPL3; - if (_type == Config::kOpl2) - iface = SND_HWDEP_IFACE_OPL2; - - // Look for OPL hwdep interface - while (!snd_card_next(&card) && card >= 0) { - int dev = -1; - Common::String name = Common::String::format("hw:%d", card); - - if (snd_ctl_open(&ctl, name.c_str(), 0) < 0) - continue; - - while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) { - name = Common::String::format("hw:%d,%d", card, dev); - - if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0) - continue; - - if (!snd_hwdep_info(_opl, info)) { - int found = snd_hwdep_info_get_iface(info); - // OPL3 can be used for (Dual) OPL2 mode - if (found == iface || found == SND_HWDEP_IFACE_OPL3) { - snd_ctl_close(ctl); - _iface = found; - reset(); - return true; - } - } - - // Wrong interface, try next device - snd_hwdep_close(_opl); - _opl = nullptr; - } - - snd_ctl_close(ctl); - } - - return false; -} - -void OPL::reset() { - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr); - if (_iface == SND_HWDEP_IFACE_OPL3) - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3); - - clear(); - - // Sync up with the hardware - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); - for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i) - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]); - for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i) - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]); -} - -void OPL::write(int port, int val) { - val &= 0xff; - int chip = (port & 2) >> 1; - - if (port & 1) { - switch(_type) { - case Config::kOpl2: - writeOplReg(0, index[0], val); - break; - case Config::kDualOpl2: - if (port & 8) { - writeOplReg(0, index[0], val); - writeOplReg(1, index[1], val); - } else - writeOplReg(chip, index[chip], val); - break; - case Config::kOpl3: - writeOplReg(chip, index[chip], val); - } - } else { - switch(_type) { - case Config::kOpl2: - index[0] = val; - break; - case Config::kDualOpl2: - if (port & 8) { - index[0] = val; - index[1] = val; - } else - index[chip] = val; - break; - case Config::kOpl3: - index[chip] = val; - } - } -} - -byte OPL::read(int port) { - return 0; -} - -void OPL::writeReg(int r, int v) { - switch (_type) { - case Config::kOpl2: - writeOplReg(0, r, v); - break; - case Config::kDualOpl2: - writeOplReg(0, r, v); - writeOplReg(1, r, v); - break; - case Config::kOpl3: - writeOplReg(r >= 0x100, r & 0xff, v); - } -} - -void OPL::writeOplReg(int c, int r, int v) { - if (r == 0x04 && c == 1 && _type == Config::kOpl3) { - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast(v & 0x3f)); - } else if (r == 0x08 && c == 0) { - _params.kbd_split = (v >> 6) & 0x1; - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); - } else if (r == 0xbd && c == 0) { - _params.hihat = v & 0x1; - _params.cymbal = (v >> 1) & 0x1; - _params.tomtom = (v >> 2) & 0x1; - _params.snare = (v >> 3) & 0x1; - _params.bass = (v >> 4) & 0x1; - _params.rhythm = (v >> 5) & 0x1; - _params.vib_depth = (v >> 6) & 0x1; - _params.am_depth = (v >> 7) & 0x1; - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); - } else if (r < 0xa0 || r >= 0xe0) { - // Operator - int idx = regOffsetToOper[r & 0x1f]; - - if (idx == -1) - return; - - if (c == 1) - idx += kOpl2Operators; - - switch (r & 0xf0) { - case 0x20: - case 0x30: - _oper[idx].harmonic = v & 0xf; - _oper[idx].kbd_scale = (v >> 4) & 0x1; - _oper[idx].do_sustain = (v >> 5) & 0x1; - _oper[idx].vibrato = (v >> 6) & 0x1; - _oper[idx].am = (v >> 7) & 0x1; - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); - break; - case 0x40: - case 0x50: - _oper[idx].volume = ~v & 0x3f; - _oper[idx].scale_level = (v >> 6) & 0x3; - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); - break; - case 0x60: - case 0x70: - _oper[idx].decay = v & 0xf; - _oper[idx].attack = (v >> 4) & 0xf; - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); - break; - case 0x80: - case 0x90: - _oper[idx].release = v & 0xf; - _oper[idx].sustain = (v >> 4) & 0xf; - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); - break; - case 0xe0: - case 0xf0: - _oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3); - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); - } - } else { - // Voice - int idx = r & 0xf; - - if (idx >= kOpl2Voices) - return; - - if (c == 1) - idx += kOpl2Voices; - - int opIdx = voiceToOper0[idx]; - - switch (r & 0xf0) { - case 0xa0: - _voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff); - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]); - break; - case 0xb0: - _voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff); - _voice[idx].octave = (v >> 2) & 0x7; - _voice[idx].key_on = (v >> 5) & 0x1; - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]); - break; - case 0xc0: - _oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1; - _oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7; - if (_type == Config::kOpl3) { - _oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1; - _oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1; - } - snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]); - } - } -} - -OPL *create(Config::OplType type) { - return new OPL(type); -} - -} // End of namespace ALSA -} // End of namespace OPL -- cgit v1.2.3