diff --git a/docs/licenses/License Attribution.txt b/docs/licenses/License Attribution.txt index 83e87e133..c04548d39 100644 --- a/docs/licenses/License Attribution.txt +++ b/docs/licenses/License Attribution.txt @@ -16,6 +16,8 @@ AlmostEquals Google Test Framework Component - Copyright (c) 2005 Google, Inc. Blasphemer (various assets) - Copyright (c) 2021 Contributors to the Blasphemer project +Emu de MIDI library - Copyright (C) 2004 Mitsutaka Okazaki + FMMIDI library - Copyright (c) 2003-2006 yuno Freedoom (various assets) - Copyright (c) 2001-2019 Contributors to the Freedoom project diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index aa96c08d8..3e1dc7328 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -9,6 +9,7 @@ else() add_subdirectory(gl4es) endif() add_subdirectory(hmm) +add_subdirectory(libemidi) add_subdirectory(libRAD) add_subdirectory(libvwad) add_subdirectory(lua) diff --git a/libraries/libemidi/CMakeLists.txt b/libraries/libemidi/CMakeLists.txt new file mode 100644 index 000000000..1e989cc80 --- /dev/null +++ b/libraries/libemidi/CMakeLists.txt @@ -0,0 +1,18 @@ +########################################## +# Emu de MIDI +########################################## + +add_library(libemidi + source/CEnvelope.cpp + source/device/emu2413.c + source/device/emu2212.c + source/device/emu2149.c + source/CMIDIModule.cpp + source/CSccDevice.cpp + source/CPSGDrum.cpp + source/CMIDIMessage.cpp + source/COpllDevice.cpp + source/CSMFPlay.cpp +) + +target_include_directories(libemidi PUBLIC ./source) \ No newline at end of file diff --git a/libraries/libemidi/license.txt b/libraries/libemidi/license.txt new file mode 100644 index 000000000..160af4bab --- /dev/null +++ b/libraries/libemidi/license.txt @@ -0,0 +1,17 @@ + Copyright (C) Mitsutaka Okazaki 2004 + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from + the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. \ No newline at end of file diff --git a/libraries/libemidi/source/CEnvelope.cpp b/libraries/libemidi/source/CEnvelope.cpp new file mode 100644 index 000000000..353ea6c66 --- /dev/null +++ b/libraries/libemidi/source/CEnvelope.cpp @@ -0,0 +1,132 @@ +#include "CEnvelope.hpp" + +#if defined(_MSC_VER) +#if defined(_DEBUG) +#define new new (_CLIENT_BLOCK, __FILE__, __LINE__) +#endif +#endif + +using namespace dsa; + +#define GETA_BITS 20 +#define MAX_CNT (1 << (GETA_BITS + 8)) + +uint32_t CEnvelope::_CalcSpeed(uint32_t ms) +{ + if (ms == 0) + return MAX_CNT; + else + return (MAX_CNT / (ms * m_clock / 1000)) * m_rate; +} + +CEnvelope::CEnvelope(uint32_t ch) : m_ch(ch) +{ + m_ci = new ChannelInfo[ch]; +} + +CEnvelope::~CEnvelope() +{ + delete[] m_ci; +} + +void CEnvelope::Reset(uint32_t clock, uint32_t rate) +{ + m_rate = rate; + m_clock = clock; + m_cnt = 0; + m_inc = (MAX_CNT / m_clock) * m_rate; + for (uint32_t ch = 0; ch < m_ch; ch++) + { + m_ci[ch].value = 0; + m_ci[ch].speed = 0; + m_ci[ch].state = FINISH; + } +} + +bool CEnvelope::Update() +{ + + m_cnt += m_inc; + if (m_cnt < MAX_CNT) + return false; + m_cnt &= 0xFFFFFFF; + + for (uint32_t ch = 0; ch < m_ch; ch++) + { + + switch (m_ci[ch].state) + { + case ATTACK: + if (m_ci[ch].speed + m_ci[ch].value < MAX_CNT) + m_ci[ch].value += m_ci[ch].speed; + else + { + m_ci[ch].value = MAX_CNT; + m_ci[ch].speed = _CalcSpeed(m_ci[ch].param.dr); + m_ci[ch].state = DECAY; + } + break; + case DECAY: + if ((m_ci[ch].value > m_ci[ch].speed) && (m_ci[ch].value > (uint32_t)m_ci[ch].param.sl << GETA_BITS)) + { + m_ci[ch].value -= m_ci[ch].speed; + } + else + { + m_ci[ch].speed = _CalcSpeed(m_ci[ch].param.sr); + m_ci[ch].value = (uint32_t)m_ci[ch].param.sl << GETA_BITS; + m_ci[ch].state = SUSTINE; + } + break; + case SUSTINE: + if ((m_ci[ch].speed > m_ci[ch].value)) + { + m_ci[ch].value = 0; + m_ci[ch].state = FINISH; + } + else + { + m_ci[ch].value -= m_ci[ch].speed; + } + break; + case RELEASE: + if ((m_ci[ch].speed > m_ci[ch].value)) + { + m_ci[ch].value = 0; + m_ci[ch].state = FINISH; + } + else + { + m_ci[ch].value -= m_ci[ch].speed; + } + break; + default: + break; + } + } + + return true; +} + +void CEnvelope::KeyOn(uint32_t ch) +{ + m_ci[ch].value = 0; + m_ci[ch].speed = _CalcSpeed(m_ci[ch].param.ar); + m_ci[ch].state = ATTACK; +} + +void CEnvelope::KeyOff(uint32_t ch) +{ + m_ci[ch].state = RELEASE; + m_ci[ch].speed = _CalcSpeed(m_ci[ch].param.rr); +} + +void CEnvelope::SetParam(uint32_t ch, const Param ¶m) +{ + m_ci[ch].param = param; +} + +uint32_t CEnvelope::GetValue(uint32_t ch) const +{ + return m_ci[ch].value >> GETA_BITS; +} diff --git a/libraries/libemidi/source/CEnvelope.hpp b/libraries/libemidi/source/CEnvelope.hpp new file mode 100644 index 000000000..925001125 --- /dev/null +++ b/libraries/libemidi/source/CEnvelope.hpp @@ -0,0 +1,54 @@ +#ifndef __CENVELOPE_HPP__ +#define __CENVELOPE_HPP__ + +#include + +namespace dsa +{ + +class CEnvelope +{ + public: + enum EnvState + { + SETTLE, + ATTACK, + DECAY, + SUSTINE, + RELEASE, + FINISH + }; + struct Param + { + uint32_t ar, dr, sl, sr, rr; + }; + struct ChannelInfo + { + EnvState state; + uint32_t speed; + uint32_t value; + Param param; + }; + + private: + uint32_t m_ch; + ChannelInfo *m_ci; + uint32_t m_clock; + uint32_t m_rate; + uint32_t m_cnt; + uint32_t m_inc; + uint32_t _CalcSpeed(uint32_t ms); + + public: + CEnvelope(uint32_t ch); + ~CEnvelope(); + void Reset(uint32_t clock = 44100, uint32_t rate = 60); + void KeyOn(uint32_t ch); + void KeyOff(uint32_t ch); + bool Update(); + void SetParam(uint32_t ch, const Param ¶m); + uint32_t GetValue(uint32_t ch) const; +}; + +} // namespace dsa +#endif diff --git a/libraries/libemidi/source/CMIDIMessage.cpp b/libraries/libemidi/source/CMIDIMessage.cpp new file mode 100644 index 000000000..7d81bec21 --- /dev/null +++ b/libraries/libemidi/source/CMIDIMessage.cpp @@ -0,0 +1,46 @@ +#include "CMIDIMessage.hpp" + +using namespace dsa; + +CMIDIMsg::CMIDIMsg(MsgType type, int ch, uint8_t data1, uint8_t data2 ) + : m_type(type), m_ch(ch), m_data1(data1), m_data2(data2) +{ +} + +CMIDIMsg &CMIDIMsg::operator=(const CMIDIMsg &arg) +{ + m_type = arg.m_type; + m_ch = arg.m_ch; + m_data1 = arg.m_data1; + m_data2 = arg.m_data2; + return (*this); +} + +CMIDIMsg::CMIDIMsg(const CMIDIMsg &arg) +{ + m_type = arg.m_type; + m_ch = arg.m_ch; + m_data1 = arg.m_data1; + m_data2 = arg.m_data2; +} + +CMIDIMsg::~CMIDIMsg() +{ +} + +const char *CMIDIMsg::c_str() const +{ + const char *text[] = {// CANNEL + "NOTE_OFF", "NOTE_ON", "POLYPHONIC_KEY_PRESSURE", "CONTROL_CHANGE", "PROGRAM_CHANGE", + "CHANNEL_PRESSURE", "PITCH_BEND_CHANGE", + // MODE + "ALL_SOUND_OFF", "RESET_ALL_CONTROLLERS", "LOCAL_CONTROL", "ALL_NOTES_OFF", "OMNI_OFF", + "OMNI_ON", "POLYPHONIC_OPERATION", "MONOPHONIC_OPERATION", + // SYSTEM + "SYSTEM_EXCLUSIVE", "MTC_QUARTER_FRAME", "SONG_POSITION_POINTER", "SONG_SELECT", + "TUNE_REQUEST", + // REALTIME + "REALTIME_CLOCK", "REALTIME_TICK", "REALTIME_START", "REALTIME_CONTINUE", "REALTIME_STOP", + "REALTIME_ACTIVE_SENSE", "REALTIME_SYSTEM_RESET", "UNKNOWN_MESSAGE"}; + return text[m_type]; +} \ No newline at end of file diff --git a/libraries/libemidi/source/CMIDIMessage.hpp b/libraries/libemidi/source/CMIDIMessage.hpp new file mode 100644 index 000000000..1a0d09fe9 --- /dev/null +++ b/libraries/libemidi/source/CMIDIMessage.hpp @@ -0,0 +1,65 @@ +#ifndef __MIDI_MESSAGE_HPP__ +#define __MIDI_MESSAGE_HPP__ +#include +#include + +#include +#include + +namespace dsa +{ + +// MIDI Msgs (Channel or exclusive Msg) +class CMIDIMsg +{ + public: + enum MsgType + { + // CHANNEL Msg + NOTE_OFF = 0, // 8n #note #velo + NOTE_ON, // 9n #note #velo + POLYPHONIC_KEY_PRESSURE, // An #note #data + CONTROL_CHANGE, // Bn #ctrl #data + PROGRAM_CHANGE, // Cn #data + CHANNEL_PRESSURE, // Dn #data + PITCH_BEND_CHANGE, // En #data #data + // MODE Msg + ALL_SOUND_OFF, // Bn 78 00 + RESET_ALL_CONTROLLERS, // Bn 79 00 + LOCAL_CONTROL, // Bn 7A #data + ALL_NOTES_OFF, // Bn 7B 00 + OMNI_OFF, // Bn 7C 00 + OMNI_ON, // Bn 7D 00 + POLYPHONIC_OPERATION, // Bn 7E 00 + MONOPHONIC_OPERATION, // Bn 7F 00 + // SYSTEM Msg + SYSTEM_EXCLUSIVE, // F0 ... F7 + MTC_QUARTER_FRAME, // F1 #data + SONG_POSITION_POINTER, // F2 #data #data + SONG_SELECT, // F3 #data + TUNE_REQUEST, // F6 + // REALTIME Msg + REALTIME_CLOCK, // F8 + REALTIME_TICK, // F9 + REALTIME_START, // FA + REALTIME_CONTINUE, // FB + REALTIME_STOP, // FC + REALTIME_ACTIVE_SENSE, // FE + REALTIME_SYSTEM_RESET, // FF + UNKNOWN_MESSAGE + }; + + MsgType m_type; // The Msg identifier + uint32_t m_ch; // The channel + uint8_t m_data1; + uint8_t m_data2; + CMIDIMsg(MsgType type = UNKNOWN_MESSAGE, int ch = 0, uint8_t data1 = 0, uint8_t data2 = 0); + CMIDIMsg(const CMIDIMsg &); + ~CMIDIMsg(); + CMIDIMsg &operator=(const CMIDIMsg &arg); + const char *c_str() const; +}; + +} // namespace dsa + +#endif diff --git a/libraries/libemidi/source/CMIDIModule.cpp b/libraries/libemidi/source/CMIDIModule.cpp new file mode 100644 index 000000000..15cfa0de0 --- /dev/null +++ b/libraries/libemidi/source/CMIDIModule.cpp @@ -0,0 +1,435 @@ +#include "CMIDIModule.hpp" + +#if defined(_MSC_VER) +#if defined(_DEBUG) +#define new new (_NORMAL_BLOCK, __FILE__, __LINE__) +#endif +#endif + +using namespace dsa; + +CMIDIModule::CMIDIModule() : m_device(NULL) +{ +} +CMIDIModule::~CMIDIModule() +{ +} + +bool CMIDIModule::Reset() +{ + + if (m_device == NULL) + return false; + if (!m_device->Reset()) + return false; + + m_off_channels.clear(); + + { + for (int i = 0; i < 16; i++) + { + m_used_channels[i].clear(); + m_program[i] = 3; + m_volume[i] = 127; + m_bend[i] = 0; + m_bend_coarse[i] = 0; + m_bend_fine[i] = 0; + m_bend_range[i] = (2 << 7); + m_pan[i] = 64; + m_RPN[i] = m_NRPN[i] = 0; + m_drum[i] = 0; + for (int j = 0; j < 128; j++) + m_keyon_table[i][j] = -1; + } + } + m_drum[9] = 1; + + m_entry_mode = 0; + + const SoundDeviceInfo &si = m_device->GetDeviceInfo(); + + { + for (uint32_t i = 0; i < si.max_ch; i++) + { + KeyInfo ki; + ki.midi_ch = i; + ki.dev_ch = i; + ki.note = 0; + m_keyon_table[i][0] = 0; + m_off_channels.push_back(ki); + m_used_channels[i].push_back(ki); + } + } + + return true; +} + +void CMIDIModule::Panpot(uint8_t midi_ch, bool is_fine, uint8_t data) +{ + if (!is_fine) + { + m_pan[midi_ch] = data; + std::deque::iterator it; + for (it = m_used_channels[midi_ch].begin(); it != m_used_channels[midi_ch].end(); it++) + m_device->SetPan((*it).dev_ch, m_pan[midi_ch]); + } +} + +void CMIDIModule::UpdatePitchBend(uint8_t midi_ch) +{ + int range = (m_bend_range[midi_ch] >> 7); + if (range != 0) + { + m_bend_coarse[midi_ch] = (m_bend[midi_ch] * range) / 8192; // note offset + m_bend_fine[midi_ch] = ((m_bend[midi_ch] % (8192 / range)) * 100 * range) / 8192; // cent offset + } + else + { + m_bend_coarse[midi_ch] = 0; + m_bend_fine[midi_ch] = 0; + } + std::deque::iterator it; + for (it = m_used_channels[midi_ch].begin(); it != m_used_channels[midi_ch].end(); it++) + m_device->SetBend((*it).dev_ch, m_bend_coarse[midi_ch], m_bend_fine[midi_ch]); +} + +void CMIDIModule::PitchBend(uint8_t midi_ch, uint8_t msb, uint8_t lsb) +{ + m_bend[midi_ch] = ((msb & 0x7f) | ((lsb & 0x7f) << 7)) - 8192; + UpdatePitchBend(midi_ch); +} + +void CMIDIModule::ChannelPressure(uint8_t midi_ch, uint8_t velo) +{ + std::deque::iterator it; + for (it = m_used_channels[midi_ch].begin(); it != m_used_channels[midi_ch].end(); it++) + m_device->SetVelocity((*it).dev_ch, velo); +} + +void CMIDIModule::NoteOn(uint8_t midi_ch, uint8_t note, uint8_t velo) +{ + + if (m_drum[midi_ch]) + { + m_device->PercSetVelocity(note, velo); + m_device->PercKeyOn(note); + return; + } + + if (0 <= m_keyon_table[midi_ch][note]) + return; // キーオン中なら無視 + + KeyInfo ki; + + if (m_off_channels.empty()) + { // キーオフ中のデバイスチャンネルが無いとき + ki.dev_ch = -1; + for (int i = 0; i < 16; i++) + { // 発音数が規定値より多いMIDIチャンネルを消音 + if (m_used_channels[i].size() > 1) + { + ki = m_used_channels[i].front(); + m_device->KeyOff(ki.dev_ch); + m_keyon_table[i][ki.note] = -1; + m_used_channels[i].pop_front(); + break; + } + } + if (ki.dev_ch == -1) + { // だめならどこでもいいから消音 + for (int i = 0; i < 16; i++) + { + if (!m_used_channels[i].empty()) + { + ki = m_used_channels[i].front(); + m_device->KeyOff(ki.dev_ch); + m_keyon_table[i][ki.note] = -1; + m_used_channels[i].pop_front(); + break; + } + } + } + } + else + { // キーオフ中のチャンネルがあるときはそれを利用 + ki = m_off_channels.front(); + m_off_channels.pop_front(); + std::deque::iterator it; + for (it = m_used_channels[ki.midi_ch].begin(); it != m_used_channels[ki.midi_ch].end(); it++) + { + if ((*it).dev_ch == ki.dev_ch) + { + m_used_channels[ki.midi_ch].erase(it); + break; + } + } + } + + m_device->SetProgram(ki.dev_ch, 0, m_program[midi_ch]); + m_device->SetVolume(ki.dev_ch, m_volume[midi_ch]); + m_device->SetVelocity(ki.dev_ch, velo); + m_device->SetBend(ki.dev_ch, m_bend_coarse[midi_ch], m_bend_fine[midi_ch]); + m_device->SetPan(ki.dev_ch, m_pan[midi_ch]); + m_device->KeyOn(ki.dev_ch, note); + m_keyon_table[midi_ch][note] = ki.dev_ch; + ki.midi_ch = midi_ch; + ki.note = note; + m_used_channels[midi_ch].push_back(ki); +} + +void CMIDIModule::NoteOff(uint8_t midi_ch, uint8_t note, uint8_t velo) +{ + + if (m_drum[midi_ch]) + { + m_device->PercKeyOff(note); + } + + int dev_ch = m_keyon_table[midi_ch][note]; + if (dev_ch < 0) + return; + m_device->KeyOff(dev_ch); + m_keyon_table[midi_ch][note] = -1; + std::deque::iterator it; + KeyInfo ki; + ki.dev_ch = dev_ch; + ki.midi_ch = midi_ch; + ki.note = 0; + m_off_channels.push_back(ki); +} + +void CMIDIModule::MainVolume(uint8_t midi_ch, bool is_fine, uint8_t data) +{ + + if (is_fine) + return; + + if (m_drum[midi_ch]) + { + m_device->PercSetVolume(data); + return; + } + + std::deque::iterator it; + for (it = m_used_channels[midi_ch].begin(); it != m_used_channels[midi_ch].end(); it++) + m_device->SetVolume((*it).dev_ch, data); +} + +void CMIDIModule::LoadRPN(uint8_t midi_ch, uint16_t data) +{ + switch (m_RPN[midi_ch]) + { + case 0x0000: + m_bend_range[midi_ch] = data; + UpdatePitchBend(midi_ch); + break; + default: + break; + } +} + +uint16_t CMIDIModule::SaveRPN(uint8_t midi_ch) +{ + switch (m_RPN[midi_ch]) + { + case 0x0000: + return m_bend_range[midi_ch]; + break; + default: + return 0; + break; + } +} + +void CMIDIModule::ResetRPN(uint8_t midi_ch) +{ + m_bend_range[midi_ch] = (2 << 7); +} + +void CMIDIModule::LoadNRPN(uint8_t midi_ch, uint16_t data) +{ +} + +uint16_t CMIDIModule::SaveNRPN(uint8_t midi_ch) +{ + return 0; +} + +void CMIDIModule::ResetNRPN(uint8_t midi_ch) +{ +} + +void CMIDIModule::DataEntry(uint8_t midi_ch, bool is_fine, uint8_t data) +{ + int entry = m_entry_mode ? SaveNRPN(midi_ch) : SaveRPN(midi_ch); + if (is_fine) + entry = (entry & 0x3F80) | (data & 0x7F); + else + entry = ((data & 0x7F) << 7) | (entry & 0x7F); + m_entry_mode ? LoadNRPN(midi_ch, entry) : LoadRPN(midi_ch, entry); +} + +void CMIDIModule::DataIncrement(uint8_t midi_ch, uint8_t data) +{ + int entry = m_entry_mode ? SaveNRPN(midi_ch) : SaveRPN(midi_ch); + if (entry < 0x3FFF) + entry++; + m_entry_mode ? LoadNRPN(midi_ch, entry) : LoadRPN(midi_ch, entry); +} + +void CMIDIModule::DataDecrement(uint8_t midi_ch, uint8_t data) +{ + int entry = m_entry_mode ? SaveNRPN(midi_ch) : SaveRPN(midi_ch); + if (entry > 0) + entry--; + m_entry_mode ? LoadNRPN(midi_ch, entry) : LoadRPN(midi_ch, entry); +} + +void CMIDIModule::NRPN(uint8_t midi_ch, bool is_lsb, uint8_t data) +{ + if (is_lsb) + { + m_NRPN[midi_ch] = (m_NRPN[midi_ch] & 0x3F80) | (data & 0x7F); + } + else + { + m_NRPN[midi_ch] = ((data & 0x7F) << 7) | (m_NRPN[midi_ch] & 0x7F); + } + if (m_NRPN[midi_ch] == 0x3FFF) + { // NRPN NULL + ResetNRPN(midi_ch); + } + if (m_entry_mode == 0) + { + m_entry_mode = 1; // NRPN MODE + } +} + +void CMIDIModule::RPN(uint8_t midi_ch, bool is_lsb, uint8_t data) +{ + if (is_lsb) + { + m_RPN[midi_ch] = (m_RPN[midi_ch] & 0x3F80) | (data & 0x7F); + // if(m_RPN[midi_ch] == 0x3FFF) RPN_Reset(); + } + else + { + m_RPN[midi_ch] = ((data & 0x7F) << 7) | (m_RPN[midi_ch] & 0x7F); + } + if (m_RPN[midi_ch] == 0x3FFF) + { // RPN NULL + ResetRPN(midi_ch); + } + if (m_entry_mode == 1) + { + m_entry_mode = 0; // RPN MODE + } +} + +void CMIDIModule::ControlChange(uint8_t midi_ch, uint8_t msb, uint8_t lsb) +{ + + if (msb < 0x40) + { // 14-bit + bool is_low = (msb & 0x20) ? true : false; + switch (msb & 0x1F) + { + // case 0x00: BankSelect(midi_ch, is_low, lsb); break; + // case 0x01: ModulationDepth(midi_ch, is_low, lsb); break; + // case 0x02: BreathControl(midi_ch, is_low, lsb); break; + // case 0x04: FootControl(midi_ch, is_low, lsb); break; + // case 0x05: PortamentTime(midi_ch, is_low, lsb); break; + case 0x06: + DataEntry(midi_ch, is_low, lsb); + break; + case 0x07: + MainVolume(midi_ch, is_low, lsb); + break; + // case 0x08: BalanceControl(midi_ch, is_low, lsb); break; + case 0x0A: + Panpot(midi_ch, is_low, lsb); + break; + // case 0x11: Expression(midi_ch, is_low, lsb); break; + default: + break; + } + } + else + { // 7-bit + switch (msb) + { + case 0x40: + break; + case 0x60: + DataIncrement(midi_ch, lsb); + break; + case 0x61: + DataDecrement(midi_ch, lsb); + break; + case 0x62: + NRPN(midi_ch, 0, lsb); + break; + case 0x63: + NRPN(midi_ch, 1, lsb); + break; + case 0x64: + RPN(midi_ch, 0, lsb); + break; + case 0x65: + RPN(midi_ch, 1, lsb); + break; + default: + break; + } + } +} + +bool CMIDIModule::Render(int32_t buf[2]) +{ + if (m_device == NULL) + return false; + else + return m_device->Render(buf); +} + +bool CMIDIModule::SendMIDIMsg(const CMIDIMsg &msg) +{ + + if (m_device == NULL) + return false; + + if (msg.m_type == CMIDIMsg::NOTE_OFF) + { + NoteOff(msg.m_ch, msg.m_data1, msg.m_data2); + } + else if (msg.m_type == CMIDIMsg::NOTE_ON) + { + if (msg.m_data2 == 0) + NoteOff(msg.m_ch, msg.m_data1, msg.m_data2); + else + NoteOn(msg.m_ch, msg.m_data1, msg.m_data2); + } + else if (msg.m_type == CMIDIMsg::PROGRAM_CHANGE) + { + m_program[msg.m_ch] = msg.m_data1; + } + else if (msg.m_type == CMIDIMsg::CONTROL_CHANGE) + { + ControlChange(msg.m_ch, msg.m_data1, msg.m_data2); + } + else if (msg.m_type == CMIDIMsg::PITCH_BEND_CHANGE) + { + PitchBend(msg.m_ch, msg.m_data1, msg.m_data2); + } + else if (msg.m_type == CMIDIMsg::CHANNEL_PRESSURE) + { + ChannelPressure(msg.m_ch, msg.m_data1); + } + return true; +} + +bool CMIDIModule::SetDrumChannel(int midi_ch, int enable) +{ + m_drum[midi_ch] = enable; + return true; +} diff --git a/libraries/libemidi/source/CMIDIModule.hpp b/libraries/libemidi/source/CMIDIModule.hpp new file mode 100644 index 000000000..4cf92fa79 --- /dev/null +++ b/libraries/libemidi/source/CMIDIModule.hpp @@ -0,0 +1,85 @@ +#ifndef __MIDI_MODULE_HPP__ +#define __MIDI_MODULE_HPP__ + +#include + +#include "CMIDIMessage.hpp" +#include "ISoundDevice.hpp" + +namespace dsa +{ + +class CMIDIModule +{ + private: + struct KeyInfo + { + int midi_ch, dev_ch, note; + }; + ISoundDevice *m_device; + + int m_NRPN[16], m_RPN[16]; + int m_volume[16]; + int m_bend_coarse[16]; + int m_bend_fine[16]; + int m_bend_range[16]; + int m_program[16]; + int m_pan[16]; + int m_bend[16]; + int m_drum[16]; + // そのキーを発音しているチャンネル番号を格納する配列 + int m_keyon_table[16][128]; + // MIDIチャンネルで使用しているOPLLチャンネルの集合(発音順のキュー) + std::deque m_used_channels[16]; + // キーオフしているOPLLチャンネルの集合 + std::deque m_off_channels; + // The current entry value of RPN/NRPN + // NRPN=1, RPN=0; + int m_entry_mode; + + protected: + virtual void ControlChange(uint8_t ch, uint8_t msb, uint8_t lsb); + virtual void NoteOn(uint8_t ch, uint8_t note, uint8_t velo); + virtual void NoteOff(uint8_t ch, uint8_t note, uint8_t velo); + virtual void UpdatePitchBend(uint8_t ch); + virtual void PitchBend(uint8_t ch, uint8_t msb, uint8_t lsb); + virtual void ChannelPressure(uint8_t ch, uint8_t velo); + virtual void DataEntry(uint8_t midi_ch, bool is_low, uint8_t data); + virtual void DataIncrement(uint8_t midi_ch, uint8_t data); + virtual void DataDecrement(uint8_t midi_ch, uint8_t data); + virtual void MainVolume(uint8_t midi_ch, bool is_fine, uint8_t data); + virtual void NRPN(uint8_t midi_ch, bool is_fine, uint8_t data); + virtual void RPN(uint8_t midi_ch, bool is_fine, uint8_t data); + virtual void LoadRPN(uint8_t midi_ch, uint16_t data); + virtual void LoadNRPN(uint8_t midi_ch, uint16_t data); + virtual uint16_t SaveRPN(uint8_t midi_ch); + virtual uint16_t SaveNRPN(uint8_t midi_ch); + virtual void ResetRPN(uint8_t midi_ch); + virtual void ResetNRPN(uint8_t midi_ch); + virtual void Panpot(uint8_t ch, bool is_fine, uint8_t data); + + public: + CMIDIModule(); + virtual ~CMIDIModule(); + void AttachDevice(ISoundDevice *device) + { + m_device = device; + } + ISoundDevice *DetachDevice() + { + ISoundDevice *tmp = m_device; + m_device = NULL; + return tmp; + } + bool Reset(); + // CMIDIメッセージ形式のMIDIメッセージを処理する。 + bool SendMIDIMsg(const CMIDIMsg &mes); + // 音声のレンダリングを行う。 + bool Render(int32_t buf[2]); + + bool SetDrumChannel(int midi_ch, int enable); +}; + +} // namespace dsa + +#endif diff --git a/libraries/libemidi/source/COpllDevice.cpp b/libraries/libemidi/source/COpllDevice.cpp new file mode 100644 index 000000000..02ffbfa13 --- /dev/null +++ b/libraries/libemidi/source/COpllDevice.cpp @@ -0,0 +1,416 @@ +#include "COpllDevice.hpp" + +#include +#include + +#include + +#if defined(_MSC_VER) +#if defined(_DEBUG) +#define new new (_NORMAL_BLOCK, __FILE__, __LINE__) +#endif +#endif + +using namespace dsa; +using namespace dsa::C; + +// GM音色マップ +static uint8_t program_table[128] = { + 3, 3, 3, 3, 3, 3, 11, 11, // 000- PIANO + 12, 12, 12, 12, 12, 12, 12, 12, // 008- BELL + 8, 8, 8, 8, 8, 8, 8, 8, // 016- ORGAN + 2, 2, 2, 2, 0, 0, 0, 0, // 024- GUITAR + 14, 15, 15, 14, 15, 15, 14, 15, // 032- BASS + 1, 1, 1, 1, 1, 1, 1, 1, // 040- STRING + 1, 1, 1, 1, 3, 4, 3, 13, // 048- STRING2 + 7, 7, 7, 7, 9, 7, 7, 7, // 056- BRASS + 6, 6, 6, 6, 6, 9, 5, 5, // 064- LEAD + 4, 4, 4, 4, 4, 4, 4, 4, // 072- PIPE + 5, 1, 1, 1, 1, 1, 1, 7, // 080- SYN LEAD + 9, 9, 3, 9, 9, 9, 9, 0, // 088- SYN + 5, 5, 5, 5, 5, 5, 5, 5, // 096- SYN.EFFECT + 0, 0, 0, 2, 2, 2, 5, 5, // 104- ETHNIC + 5, 5, 5, 5, 5, 5, 5, 5, // 112- EFFECT + 5, 5, 5, 5, 5, 5, 5, 5 // 120- SFX +}; + +// 音色別の音量差調整 -n 音量アップ +n 音量ダウン +static int prog_att[16] = { + -1, // 0 org + -2, // 1 violin + 1, // 2 guitar + 1, // 3 piano + 0, // 4 flute + 0, // 5 clarinet + 0, // 6 oboe + 1, // 7 trumpet + 0, // 8 organ + 0, // 9 horn + 0, // 10 synth + 0, // 11 harp + 0, // 12 vibra + 0, // 13 s.bass + 0, // 14 w.bass + 0, // 15 e.bass +}; + +static int prog_oct[16] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static uint8_t perc_table[128] = { + // 5:B.D 4:S.D 3:TOM 2:CYM 1:HH 0:NONE + 0, 0, 0, 0, 0, 0, 0, 0, // 000- + 0, 0, 0, 0, 0, 0, 0, 5, // 008- + 0, 0, 0, 0, 0, 0, 0, 0, // 016- + 0, 0, 0, 0, 0, 0, 0, 0, // 024- + 0, 5, 4, 5, 5, 1, 4, 1, // 032- + 4, 3, 1, 3, 1, 3, 2, 3, // 040- + 3, 2, 3, 2, 2, 2, 2, 2, // 048- + 2, 2, 2, 2, 3, 3, 3, 3, // 056- + 3, 3, 3, 0, 0, 0, 0, 0, // 064- + 0, 0, 0, 0, 0, 0, 0, 0, // 072- + 0, 0, 0, 0, 0, 0, 0, 0, // 080- + 0, 0, 0, 0, 0, 0, 0, 0, // 088- + 0, 0, 0, 0, 0, 0, 0, 0, // 096- + 0, 0, 0, 0, 0, 0, 0, 0, // 104- + 0, 0, 0, 0, 0, 0, 0, 0, // 112- + 0, 0, 0, 0, 0, 0, 0, 0 // 120- +}; + +COpllDevice::COpllDevice(uint32_t rate, uint32_t nch) +{ + + if (nch == 2) + m_nch = 2; + else + m_nch = 1; + + for (uint32_t i = 0; i < m_nch; i++) + m_opll[i] = OPLL_new(3579545, rate); + Reset(); +} + +COpllDevice::~COpllDevice() +{ + for (uint32_t i = 0; i < m_nch; i++) + { + m_rbuf[i].clear(); + OPLL_delete(m_opll[i]); + } +} + +const SoundDeviceInfo &COpllDevice::GetDeviceInfo(void) const +{ + static SoundDeviceInfo si; + si.name = (uint8_t *)"OPLL Module"; + si.desc = (uint8_t *)"(C) Mitsutaka Okazaki 2004" __FILE__; + si.version = 0x0001; + si.max_ch = 6; + return si; +} + +bool COpllDevice::Reset() +{ + + { + for (uint32_t i = 0; i < m_nch; i++) + { + OPLL_reset(m_opll[i]); + OPLL_set_quality(m_opll[i], 1); + // Rhythm Initial Value + _WriteReg(0x16, 0x20, i); + _WriteReg(0x26, 0x05, i); + _WriteReg(0x17, 0x50, i); + _WriteReg(0x27, 0x05, i); + _WriteReg(0x18, 0xC0, i); + _WriteReg(0x28, 0x01, i); + // Original Voice + _WriteReg(0x00, 0x61, i); + _WriteReg(0x01, 0x61, i); + _WriteReg(0x02, 0x03, i); + _WriteReg(0x03, 0x0D, i); + _WriteReg(0x04, 0xf9, i); + _WriteReg(0x05, 0xf4, i); + _WriteReg(0x06, 0x37, i); + _WriteReg(0x07, 0x27, i); + memset(m_reg_cache[i], 0, 128); + m_rbuf[i].clear(); + } + } + + for (int i = 0; i < 9; i++) + { + m_ci[i].bend_coarse = 0; + m_ci[i].bend_fine = 0; + m_ci[i]._bend_fine = 1.0; + m_ci[i].octave = 0; + m_ci[i].volume = 127; + m_ci[i].velocity = 127; + m_ci[i].program = 0; + m_ci[i].note = 0; + m_ci[i].pan = 64; + m_ci[i].keyon = false; + m_ci[i].fnum = 0; + } + + m_pi.volume = 127; + m_pi.prog = 0; + m_pi.bank = 0; + for (int i = 0; i < 5; i++) + { + m_pi.velocity[i] = 127; + m_pi.vcache[i] = 0; + } + m_pi.keymap = 0; + + return true; +} + +void COpllDevice::_WriteReg(uint8_t reg, uint8_t val, int32_t pan) +{ + + if (m_nch == 2) + { + if (pan < 0 || 1 < pan) + { + _WriteReg(reg, val, 1); + pan = 0; + } + } + else + pan = 0; + + if (m_reg_cache[pan][reg] != val) + { + OPLL_writeReg(m_opll[pan], reg, val); + m_reg_cache[pan][reg] = val; + + // At least one calc() method must be invoked between two sequence of writeReg(). + if (m_rbuf[pan].size() < 8192) + { + m_rbuf[pan].push_back(OPLL_calc(m_opll[pan])); + } + } +} + +bool COpllDevice::Render(int32_t buf[2]) +{ + + for (uint32_t i = 0; i < m_nch; i++) + { + if (m_rbuf[i].empty()) + buf[i] = OPLL_calc(m_opll[i]); + else + { + buf[i] = m_rbuf[i].front(); + m_rbuf[i].pop_front(); + } + } + if (m_nch < 2) + buf[1] = buf[0]; + return true; +} + +void COpllDevice::_UpdateVolume(uint32_t ch) +{ + + int32_t att = 14 - m_ci[ch].volume / 16 - m_ci[ch].velocity / 16 + prog_att[m_ci[ch].program]; + if (att < 0) + att = 0; + else if (15 < att) + att = 15; + + if (m_nch < 2) + { + _WriteReg(0x30 + ch, att | ((m_ci[ch].program) << 4)); + return; + } + + // LEFT CHANNEL + if (64 < m_ci[ch].pan) + { + int tmp = att + (m_ci[ch].pan - 64) / 4; + _WriteReg(0x30 + ch, ((tmp < 15) ? tmp : 15) | ((m_ci[ch].program) << 4), 0); + } + else + { + _WriteReg(0x30 + ch, att | ((m_ci[ch].program) << 4), 0); + } + + // RIGHT CHANNEL + if (m_ci[ch].pan < 64) + { + int tmp = att + (63 - m_ci[ch].pan) / 4; + _WriteReg(0x30 + ch, ((tmp < 15) ? tmp : 15) | ((m_ci[ch].program) << 4), 1); + } + else + { + _WriteReg(0x30 + ch, att | ((m_ci[ch].program) << 4), 1); + } +} + +void COpllDevice::SetPan(uint32_t ch, uint8_t pan) +{ + m_ci[ch].pan = pan; + _UpdateVolume(ch); +} + +void COpllDevice::_UpdateFreq(uint32_t ch) +{ + static const uint8_t base = 67; // G + static const uint16_t note2freq[12] = { // 172, 183, 194, 205, 217, 230, 244, + 258, 274, 290, 307, 325, 344, 365, 387, 410, 434, 460, 487}; + + int32_t note = m_ci[ch].note + m_ci[ch].bend_coarse; + uint16_t freq = (int)(m_ci[ch]._bend_fine * note2freq[(note + 240 - base) % 12]); + int32_t oct = 4 + prog_oct[m_ci[ch].program]; + + if (note >= base) + oct += (note - base) / 12; + else + oct -= ((base - note - 1) / 12 + 1); + + while (oct < 0) + { + oct++; + freq = (freq >> 1) + 1; + } + while (7 < oct) + { + oct--; + freq <<= 1; + } + + while (0x1ff < freq) + { + if (oct < 7) + { + freq = (freq >> 1) + 1; + oct++; + } + else + { + freq = 0x1FF; + } + } + + _WriteReg(0x10 + ch, freq & 0xFF); + _WriteReg(0x20 + ch, (m_ci[ch].keyon ? 0xF0 : 0) | (oct << 1) | (freq >> 8)); + + m_ci[ch].fnum = (oct << 9) | freq; +} + +void COpllDevice::SetBend(uint32_t ch, int8_t coarse, int8_t fine) +{ + m_ci[ch].bend_coarse = coarse; + m_ci[ch].bend_fine = fine; + m_ci[ch]._bend_fine = pow(2.0, (double)fine / 1200); + _UpdateFreq(ch); +} + +void COpllDevice::SetProgram(uint32_t ch, uint8_t bank, uint8_t prog) +{ + m_ci[ch].program = program_table[prog]; + _UpdateVolume(ch); +} + +void COpllDevice::SetVolume(uint32_t ch, uint8_t vol) +{ + m_ci[ch].volume = vol; + _UpdateVolume(ch); +} + +void COpllDevice::SetVelocity(uint32_t ch, uint8_t velo) +{ + m_ci[ch].velocity = velo; + _UpdateVolume(ch); +} + +void COpllDevice::KeyOn(uint32_t ch, uint8_t note) +{ + m_ci[ch].note = note; + m_ci[ch].keyon = true; + _UpdateFreq(ch); +} + +void COpllDevice::KeyOff(uint32_t ch) +{ + m_ci[ch].keyon = false; + _WriteReg(0x20 + ch, m_ci[ch].fnum >> 8); +} + +void COpllDevice::PercSetVelocity(uint8_t note, uint8_t velo) +{ + note = perc_table[note]; + if (0 < note) + m_pi.velocity[note - 1] = velo; + _PercUpdateVolume(note); +} + +void COpllDevice::_PercUpdateVolume(uint8_t note) +{ + + if (note > 5) + return; + + int vol = 13 - m_pi.volume / 16 - m_pi.velocity[note] / 16; + if (vol < 0) + m_pi.vcache[note] = 0; + else if (15 < vol) + m_pi.vcache[note] = 15; + else + m_pi.vcache[note] = vol; + + switch (note) + { + case 4: // B.D + _WriteReg(0x30 + 6, m_pi.vcache[4]); + break; + case 3: // S.D + case 0: // HH + _WriteReg(0x30 + 7, m_pi.vcache[3] | (m_pi.vcache[0] << 4)); + break; + case 2: // TOM + case 1: // CYM + _WriteReg(0x30 + 8, m_pi.vcache[1] | (m_pi.vcache[2] << 4)); + break; + default: + break; + } +} + +void COpllDevice::PercSetProgram(uint8_t bank, uint8_t prog) +{ + m_pi.bank = bank; + m_pi.prog = prog; +} + +void COpllDevice::PercSetVolume(uint8_t vol) +{ + m_pi.volume = vol; + for (int i = 0; i < 5; i++) + _PercUpdateVolume(i); +} + +void COpllDevice::PercKeyOn(uint8_t note) +{ + note = perc_table[note]; + if (0 < note) + { + if (m_pi.keymap & (1 << (note - 1))) + _WriteReg(0xE, 0x20 | (m_pi.keymap & ~(1 << (note - 1)))); + m_pi.keymap |= (1 << (note - 1)); + _WriteReg(0xE, 0x20 | m_pi.keymap); + } +} + +void COpllDevice::PercKeyOff(uint8_t note) +{ + note = perc_table[note]; + if (note) + { + m_pi.keymap &= ~(1 << (note - 1)); + _WriteReg(0xE, 0x20 | m_pi.keymap); + } +} diff --git a/libraries/libemidi/source/COpllDevice.hpp b/libraries/libemidi/source/COpllDevice.hpp new file mode 100644 index 000000000..ea1a3d5c4 --- /dev/null +++ b/libraries/libemidi/source/COpllDevice.hpp @@ -0,0 +1,83 @@ +#ifndef __CDeviceOpll_H__ +#define __CDeviceOpll_H__ +#include + +#include + +#include "ISoundDevice.hpp" + +namespace dsa +{ + +namespace C +{ +#include "device/emu2413.h" +} // namespace C + +class COpllDevice : public ISoundDevice +{ + public: + struct PercInfo + { + uint8_t volume; + uint8_t vcache[5]; + uint8_t velocity[5]; + uint8_t keymap; + uint8_t bank; + uint8_t prog; + }; + struct ChannelInfo + { + uint16_t fnum; + uint8_t bank; + uint8_t program; + uint8_t octave; + uint8_t velocity; + uint8_t volume; + uint8_t note; + uint8_t pan; + int8_t bend_coarse; + int8_t bend_fine; + bool keyon; + double _bend_fine; + }; + + private: + uint32_t m_nch; + C::OPLL *m_opll[2]; + uint8_t m_reg_cache[2][0x80]; + ChannelInfo m_ci[9]; + PercInfo m_pi; + std::deque m_rbuf[2]; // The rendering buffer + + void _UpdateFreq(uint32_t ch); + void _UpdateVolume(uint32_t ch); + void _PercUpdateVolume(uint8_t note); + void _WriteReg(uint8_t reg, uint8_t val, int32_t pan = -1); + + public: + COpllDevice(uint32_t rate = 44100, uint32_t nch = 2); + virtual ~COpllDevice(); + + const SoundDeviceInfo &GetDeviceInfo(void) const; + bool Reset(void); + bool Render(int32_t buf[2]); + + void SetProgram(uint32_t ch, uint8_t bank, uint8_t prog); + void SetVelocity(uint32_t ch, uint8_t vel); + void SetPan(uint32_t ch, uint8_t pan); + void SetVolume(uint32_t ch, uint8_t vol); + void SetBend(uint32_t ch, int8_t coarse, int8_t fine); + void KeyOn(uint32_t ch, uint8_t note); + void KeyOff(uint32_t ch); + + void PercKeyOn(uint8_t note); + void PercKeyOff(uint8_t note); + void PercSetProgram(uint8_t bank, uint8_t prog); + void PercSetVelocity(uint8_t note, uint8_t vel); + void PercSetVolume(uint8_t vol); +}; + +} // namespace dsa + +#endif diff --git a/libraries/libemidi/source/CPSGDrum.cpp b/libraries/libemidi/source/CPSGDrum.cpp new file mode 100644 index 000000000..1c5a20ee0 --- /dev/null +++ b/libraries/libemidi/source/CPSGDrum.cpp @@ -0,0 +1,243 @@ +#include "CPSGDrum.hpp" + +#include +#include + +using namespace dsa; +using namespace dsa::C; + +static CPSGDrum::Instrument inst_table[128] = { + // { NOTE, VOL, MODE, { AR, DR, SL, SR, RR } } + {48, 2, 1, {0, 20, 0, 0, 20}}, // BD + {60, -2, 2, {0, 80, 0, 0, 80}}, // SD +}; + +CPSGDrum::CPSGDrum(uint32_t rate, uint32_t nch) : m_env(6) +{ + + if (nch == 2) + m_nch = 2; + else + m_nch = 1; + m_rate = rate; + + for (uint32_t i = 0; i < 2; i++) + m_psg[i] = PSG_new(3579545, rate); + + Reset(); + + for (int i = 0; i < 128; i++) + { + m_note2freq[i] = (uint16_t)(3579545.0 / 16 / (440.0 * pow(2.0, (double)(i - 57) / 12))); + if (0xFFF < m_note2freq[i]) + m_note2freq[i] = 0xFFF; + } + + inst_table[35] = inst_table[36] = inst_table[0]; + inst_table[38] = inst_table[40] = inst_table[1]; +} + +CPSGDrum::~CPSGDrum() +{ +} + +bool CPSGDrum::Reset() +{ + + for (uint32_t i = 0; i < 2; i++) + { + PSG_reset(m_psg[i]); + PSG_set_quality(m_psg[i], 1); + memset(m_reg_cache[i], 0, 128); + m_rbuf[i].clear(); + m_noise_mode[i] = 0xFF; + } + + m_env.Reset(); + m_off_channels.clear(); + m_on_channels.clear(); + + for (int i = 0; i < 6; i++) + { + m_ci[i].keyon = false; + m_ci[i].note = 0; + m_ci[i].noise = 0; + m_off_channels.push_back(i); + } + + for (int i = 0; i < 128; i++) + { + m_keytable[i] = -1; + m_velocity[i] = 127; + } + + return true; +} + +void CPSGDrum::_WriteReg(uint8_t reg, uint8_t val, uint32_t id) +{ + if (m_reg_cache[id][reg] != val) + { + PSG_writeReg(m_psg[id], reg, val); + m_reg_cache[id][reg] = val; + if (m_rbuf[id].size() < 8192) + { + m_rbuf[id].push_back(PSG_calc(m_psg[id]) << 16); + if (m_env.Update()) + { + for (int ch = 0; ch < 6; ch++) + _UpdateVolume(ch); + } + } + } +} + +const SoundDeviceInfo &CPSGDrum::GetDeviceInfo(void) const +{ + + static SoundDeviceInfo si; + si.max_ch = 0; + si.version = 0x0001; + si.name = (uint8_t *)"PSG DRUM"; + si.desc = (uint8_t *)""; + return si; +} + +bool CPSGDrum::Render(int32_t buf[2]) +{ + + buf[0] = 0; + for (uint32_t i = 0; i < 2; i++) + { + if (m_rbuf[i].empty()) + { + buf[0] += PSG_calc(m_psg[i]) << 16; + if (m_env.Update()) + { + for (int ch = 0; ch < 6; ch++) + _UpdateVolume(ch); + } + } + else + buf[0] += m_rbuf[i].front(); + m_rbuf[i].pop_front(); + } + buf[0] <<= 1; + buf[1] = buf[0]; + + return true; +} + +void CPSGDrum::_UpdateFreq(uint32_t ch) +{ + int note = m_ci[ch].note; + if (note < 0) + note = 0; + else if (127 < note) + note = 127; + + int fnum = m_note2freq[note]; + if (0xFFF < fnum) + fnum = 0xFFF; + + _WriteReg((ch % 3) * 2, fnum & 0xff, ch / 3); + _WriteReg((ch % 3) * 2 + 1, fnum >> 8, ch / 3); +} + +void CPSGDrum::_UpdateVolume(uint32_t ch) +{ + + int vol = m_volume / 16 + m_velocity[m_ci[ch].note] / 16 + 1; + vol += m_ci[ch].vol; + vol = (vol * m_env.GetValue(ch)) >> 8; + if (vol > 15) + vol = 15; + + _WriteReg(8 + (ch % 3), vol, ch / 3); +} + +void CPSGDrum::_UpdateMode(uint32_t ch) +{ + m_noise_mode[ch / 3] &= ~(0x09 << (ch % 3)); + m_noise_mode[ch / 3] |= ((m_ci[ch].noise & 2) << (2 + ch % 3)) | ((m_ci[ch].noise & 1) << (ch % 3)); + _WriteReg(7, m_noise_mode[ch / 3], ch / 3); +} + +void CPSGDrum::PercKeyOn(uint8_t note) +{ + + if (note != 35 && note != 36 && note != 38 && note != 40) + return; + + if (m_keytable[note] >= 0) + PercKeyOff(note); + + KeyInfo ki; + + if (m_off_channels.empty()) + { + ki = m_on_channels.front(); + PercKeyOff(ki.note); + m_on_channels.pop_front(); + } + else + { + ki.ch = m_off_channels.front(); + m_off_channels.pop_front(); + std::deque::iterator it; + for (it = m_on_channels.begin(); it != m_on_channels.end(); it++) + { + if ((*it).ch == ki.ch) + { + m_on_channels.erase(it); + break; + } + } + } + + ki.note = note; + m_ci[ki.ch].note = inst_table[note].note; + m_ci[ki.ch].noise = inst_table[note].noise ^ 3; + m_ci[ki.ch].vol = inst_table[note].vol; + m_env.SetParam(ki.ch, inst_table[note].param); + m_env.KeyOn(ki.ch); + m_ci[ki.ch].keyon = true; + m_on_channels.push_back(ki); + m_keytable[note] = ki.ch; + + _UpdateMode(ki.ch); + _UpdateFreq(ki.ch); + _UpdateVolume(ki.ch); +} + +void CPSGDrum::PercKeyOff(uint8_t note) +{ + + if (m_keytable[note] < 0) + return; + + uint32_t ch = m_keytable[note]; + m_ci[ch].keyon = false; + m_env.KeyOff(ch); + m_off_channels.push_back(ch); + m_keytable[note] = -1; + _UpdateVolume(ch); +} + +void CPSGDrum::PercSetProgram(uint8_t bank, uint8_t prog) +{ +} + +void CPSGDrum::PercSetVelocity(uint8_t note, uint8_t velo) +{ + m_velocity[note] = velo; + for (int ch = 0; ch < 6; ch++) + _UpdateVolume(ch); +} + +void CPSGDrum::PercSetVolume(uint8_t vol) +{ + m_volume = vol; + for (int ch = 0; ch < 6; ch++) + _UpdateVolume(ch); +} diff --git a/libraries/libemidi/source/CPSGDrum.hpp b/libraries/libemidi/source/CPSGDrum.hpp new file mode 100644 index 000000000..8922120fb --- /dev/null +++ b/libraries/libemidi/source/CPSGDrum.hpp @@ -0,0 +1,87 @@ +#ifndef __CPSG_DRUM_HPP__ +#include + +#include +namespace dsa +{ +namespace C +{ +#include "device/emu2149.h" +} +}; // namespace dsa +#include "CEnvelope.hpp" +#include "ISoundDevice.hpp" + +namespace dsa +{ + +class CPSGDrum : public ISoundDevice +{ + public: + struct Instrument + { + uint8_t note; + int8_t vol; + uint8_t noise; + CEnvelope::Param param; + }; + + struct ChannelInfo + { + uint8_t note; + int8_t vol; + uint8_t noise; + bool keyon; + }; + + private: + uint32_t m_rate; + uint32_t m_nch; + C::PSG *m_psg[2]; + uint8_t m_reg_cache[2][0x10]; + uint8_t m_noise_mode[2]; + uint16_t m_note2freq[128]; + struct KeyInfo + { + uint32_t ch; + uint8_t note; + }; + std::deque m_on_channels; + std::deque m_off_channels; + ChannelInfo m_ci[6]; + CEnvelope m_env; + uint8_t m_volume; + uint8_t m_velocity[128]; + int32_t m_keytable[128]; + std::deque m_rbuf[2]; + void _UpdateMode(uint32_t ch); + void _UpdateVolume(uint32_t ch); + void _UpdateFreq(uint32_t ch); + void _UpdateProgram(uint32_t ch); + void _WriteReg(uint8_t reg, uint8_t val, uint32_t id); + + public: + explicit CPSGDrum(uint32_t rate = 44100, uint32_t m_nch = 1); + virtual ~CPSGDrum(); + const SoundDeviceInfo &GetDeviceInfo(void) const; + bool Reset(void); + bool Render(int32_t buf[2]); + + void PercKeyOn(uint8_t note); + void PercKeyOff(uint8_t note); + void PercSetVolume(uint8_t vol); + void PercSetVelocity(uint8_t note, uint8_t velo); + void PercSetProgram(uint8_t bank, uint8_t prog); + + void SetProgram(uint32_t ch, uint8_t bank, uint8_t prog) {}; + void SetVelocity(uint32_t ch, uint8_t vel) {}; + void SetPan(uint32_t ch, uint8_t pan) {}; + void SetVolume(uint32_t ch, uint8_t vol) {}; + void SetBend(uint32_t ch, int8_t coarse, int8_t fine) {}; + void KeyOn(uint32_t ch, uint8_t note) {}; + void KeyOff(uint32_t ch) {}; +}; + +} // namespace dsa + +#endif diff --git a/libraries/libemidi/source/CSMFPlay.cpp b/libraries/libemidi/source/CSMFPlay.cpp new file mode 100644 index 000000000..89f92df20 --- /dev/null +++ b/libraries/libemidi/source/CSMFPlay.cpp @@ -0,0 +1,45 @@ +#include +#include +#include "CSMFPlay.hpp" +#include "COpllDevice.hpp" +#include "CSccDevice.hpp" + +using namespace dsa; + +CSMFPlay::CSMFPlay(uint32_t rate, int mods) { + m_mods = mods; + for(int i=0;i +#include + +#include "CMIDIModule.hpp" + +namespace dsa { + +class CSMFPlay { + CMIDIModule m_module[16]; + int m_mods; + +public: + CSMFPlay(uint32_t rate, int mods=4); + ~CSMFPlay(); + void Start(bool reset = true); + void SendMIDIMessage(const CMIDIMsg &msg); + uint32_t Render16(int16_t *buf, uint32_t length); +}; + +} // namespace dsa + +#endif diff --git a/libraries/libemidi/source/CSccDevice.cpp b/libraries/libemidi/source/CSccDevice.cpp new file mode 100644 index 000000000..46006a6cd --- /dev/null +++ b/libraries/libemidi/source/CSccDevice.cpp @@ -0,0 +1,354 @@ +#include "CSccDevice.hpp" + +#include +#include +#include + +#if defined(_MSC_VER) +#if defined(_DEBUG) +#define new new (_CLIENT_BLOCK, __FILE__, __LINE__) +#endif +#endif + +using namespace dsa; +using namespace dsa::C; + +static CSccDevice::Instrument inst_table[128] = { +#include "SccInst.h" +}; +static uint32_t decay_table[256][4]; + +static uint8_t scctone[128][32] = { +#include "SccWave.h" +}; + +void CSccDevice::_CalcEnvelope(void) +{ + + m_env_counter += m_env_incr; + if (m_env_counter < 0x10000000) + return; + m_env_counter &= 0xFFFFFFF; + + for (uint32_t ch = 0; ch < 5; ch++) + { + switch (m_ci[ch].env_state) + { + case ATTACK: + if (m_ci[ch].env_speed + m_ci[ch].env_value < 0x10000000) + m_ci[ch].env_value += m_ci[ch].env_speed; + else + { + m_ci[ch].env_value = 0x10000000; + m_ci[ch].env_speed = decay_table[inst_table[m_ci[ch].program].dr][0] >> 4; + m_ci[ch].env_state = DECAY; + } + break; + case DECAY: + if ((m_ci[ch].env_value > m_ci[ch].env_speed) && + (m_ci[ch].env_value > (((uint32_t)inst_table[m_ci[ch].program].sl) << 20))) + { + m_ci[ch].env_value -= m_ci[ch].env_speed; + } + else + { + m_ci[ch].env_speed = decay_table[inst_table[m_ci[ch].program].sr][0] >> 4; + m_ci[ch].env_value = (((uint32_t)inst_table[m_ci[ch].program].sl) << 20); + m_ci[ch].env_state = SUSTINE; + } + break; + case SUSTINE: + if ((m_ci[ch].env_speed > m_ci[ch].env_value)) + { + m_ci[ch].env_value = 0; + m_ci[ch].env_state = FINISH; + } + else + { + m_ci[ch].env_value -= m_ci[ch].env_speed; + } + break; + case RELEASE: + if ((m_ci[ch].env_speed > m_ci[ch].env_value)) + { + m_ci[ch].env_value = 0; + m_ci[ch].env_state = FINISH; + } + else + { + m_ci[ch].env_value -= m_ci[ch].env_speed; + } + break; + default: + break; + } + _UpdateVolume(ch); + } +} + +CSccDevice::CSccDevice(uint32_t rate, uint32_t nch) +{ + + if (nch == 2) + m_nch = 2; + else + m_nch = 1; + m_rate = rate; + + { + for (uint32_t i = 0; i < m_nch; i++) + m_scc[i] = SCC_new(3579545, rate); + } + + Reset(); + + { + for (int i = 0; i < 127; i++) + { + m_note2freq[i] = (uint16_t)(3579545.0 / 16 / (440.0 * pow(2.0, (double)(i - 57) / 12))); + if (0xFFF < m_note2freq[i]) + m_note2freq[i] = 0xFFF; + } + } + + for (int j = 0; j < 4; j++) + { + double span[4] = {1600.0, 1400.0, 1200.0, 1000.0}; + double mult = pow(10.0, log10(span[j]) / 256); // 256 = env width + double base = 1.0; + decay_table[255][j] = 0x10000000; + for (int i = 1; i < 255; i++) + { + double tmp = (uint32_t)(1000.0 / base * 0x10000000 / 60); + decay_table[255 - i][j] = ((tmp < 0x10000000) ? (uint32_t)tmp : 0x10000000); + base *= mult; + } + decay_table[0][j] = 0; + } +} + +CSccDevice::~CSccDevice() +{ + for (uint32_t i = 0; i < m_nch; i++) + { + m_rbuf[i].clear(); + SCC_delete(m_scc[i]); + } +} + +const SoundDeviceInfo &CSccDevice::GetDeviceInfo(void) const +{ + + static SoundDeviceInfo si; + si.max_ch = 5; + si.version = 0x0001; + si.name = (uint8_t *)"SCC"; + si.desc = (uint8_t *)""; + return si; +} + +bool CSccDevice::Reset(void) +{ + + { + for (uint32_t i = 0; i < m_nch; i++) + { + SCC_reset(m_scc[i]); + SCC_set_type(m_scc[i], SCC_ENHANCED); + memset(m_reg_cache[i], 0, 128); + m_rbuf[i].clear(); + } + } + + m_env_counter = 0; + m_env_incr = (0x10000000 / m_rate) * 60; + + for (int i = 0; i < 5; i++) + { + m_ci[i].keyon = false; + m_ci[i].note = 0; + m_ci[i].program = 0; + m_ci[i].bend_coarse = 0; + m_ci[i].bend_fine = 0; + m_ci[i]._bend_fine = 1.0; + m_ci[i].freq = 0; + m_ci[i].velocity = 127; + m_ci[i].volume = 127; + m_ci[i].pan = 64; + m_ci[i].env_state = FINISH; + m_ci[i].env_value = 0; + } + + return true; +} + +void CSccDevice::_WriteReg(uint8_t reg, uint8_t val, int32_t pan) +{ + + if (m_nch == 2) + { + if (pan < 0 || 1 < pan) + { + _WriteReg(reg, val, 1); + pan = 0; + } + } + else + pan = 0; + + if (m_reg_cache[pan][reg] != val) + { + SCC_writeReg(m_scc[pan], reg, val); + m_reg_cache[pan][reg] = val; + if (m_rbuf[pan].size() < 8192) + { + m_rbuf[pan].push_back(SCC_calc(m_scc[pan])); + if (!pan) + _CalcEnvelope(); + } + } +} + +bool CSccDevice::Render(int32_t buf[2]) +{ + + for (uint32_t i = 0; i < m_nch; i++) + { + if (m_rbuf[i].empty()) + { + buf[i] = SCC_calc(m_scc[i]); + if (!i) + _CalcEnvelope(); + } + else + { + buf[i] = m_rbuf[i].front(); + m_rbuf[i].pop_front(); + } + } + if (m_nch < 2) + buf[1] = buf[0]; + return true; +} + +void CSccDevice::SetPan(uint32_t ch, uint8_t pan) +{ + m_ci[ch].pan = pan; + _UpdateVolume(ch); +} + +void CSccDevice::_UpdateVolume(uint32_t ch) +{ + + int vol = m_ci[ch].volume / 16 + m_ci[ch].velocity / 16 + 1; + vol = (vol * (m_ci[ch].env_value >> 20) / 256); + if (vol > 15) + vol = 15; + + if (m_ci[ch].keyon == false) + { + _WriteReg(0xD0 + ch, 0); + return; + } + + if (m_nch < 2) + { + _WriteReg(0xD0 + ch, vol); + return; + } + + // LEFT CHANNEL + if (64 < m_ci[ch].pan) + { + int tmp = vol - (m_ci[ch].pan - 64) / 4; + _WriteReg(0xD0 + ch, (0 < tmp) ? tmp : 0, 0); + } + else + { + _WriteReg(0xD0 + ch, vol, 0); + } + + // RIGHT CHANNEL + if (m_ci[ch].pan < 64) + { + int tmp = vol - (63 - m_ci[ch].pan) / 4; + _WriteReg(0xD0 + ch, (0 < tmp) ? tmp : 0, 1); + } + else + { + _WriteReg(0xD0 + ch, vol, 1); + } +} + +void CSccDevice::_UpdateFreq(uint32_t ch) +{ + int note = m_ci[ch].note + m_ci[ch].bend_coarse + (int)inst_table[m_ci[ch].program].oct * 12; + if (note < 0) + note = 0; + else if (127 < note) + note = 127; + + int fnum = (int)((double)m_note2freq[note] / m_ci[ch]._bend_fine); + if (0xFFF < fnum) + fnum = 0xFFF; + + _WriteReg(0xC0 + ch * 2, fnum & 0xff); + _WriteReg(0xC0 + ch * 2 + 1, fnum >> 8); +} + +void CSccDevice::SetBend(uint32_t ch, int8_t coarse, int8_t fine) +{ + m_ci[ch].bend_coarse = coarse; + m_ci[ch].bend_fine = fine; + m_ci[ch]._bend_fine = pow(2.0, (double)fine / 1200); + _UpdateFreq(ch); +} + +void CSccDevice::_UpdateProgram(uint32_t ch) +{ + for (int i = 0; i < 32; i++) + _WriteReg(ch * 32 + i, scctone[inst_table[m_ci[ch].program].wav][i]); +} + +void CSccDevice::SetProgram(uint32_t ch, uint8_t bank, uint8_t prog) +{ + m_ci[ch].program = prog; +} + +void CSccDevice::SetVolume(uint32_t ch, uint8_t vol) +{ + m_ci[ch].volume = vol; + _UpdateVolume(ch); +} + +void CSccDevice::SetVelocity(uint32_t ch, uint8_t velo) +{ + m_ci[ch].velocity = velo; + _UpdateVolume(ch); +} + +void CSccDevice::KeyOn(uint32_t ch, uint8_t note) +{ + if (!m_ci[ch].keyon) + { + m_ci[ch].note = note; + m_ci[ch].keyon = true; + m_ci[ch].env_value = 0; // inst_table[m_ci[ch].program].iv<<20; + m_ci[ch].env_speed = decay_table[inst_table[m_ci[ch].program].ar][0]; + m_ci[ch].env_state = ATTACK; + _UpdateProgram(ch); + _UpdateFreq(ch); + _UpdateVolume(ch); + } +} + +void CSccDevice::KeyOff(uint32_t ch) +{ + if (m_ci[ch].keyon) + { + m_ci[ch].keyon = false; + m_ci[ch].env_state = RELEASE; + m_ci[ch].env_speed = decay_table[inst_table[m_ci[ch].program].rr][0] >> 4; + _UpdateVolume(ch); + } +} diff --git a/libraries/libemidi/source/CSccDevice.hpp b/libraries/libemidi/source/CSccDevice.hpp new file mode 100644 index 000000000..578194451 --- /dev/null +++ b/libraries/libemidi/source/CSccDevice.hpp @@ -0,0 +1,92 @@ +#ifndef __CSCC_DEVICE_HPP__ +#define __CSCC_DEVICE_HPP__ +#include + +#include +namespace dsa +{ +namespace C +{ +#include "device/emu2212.h" +} +}; // namespace dsa +#include "ISoundDevice.hpp" + +namespace dsa +{ + +class CSccDevice : public ISoundDevice +{ + public: + struct Instrument + { + uint8_t wav; + int8_t oct; + uint8_t ar, dr, sl, sr, rr; + }; + enum EnvState + { + SETTLE, + ATTACK, + DECAY, + SUSTINE, + RELEASE, + FINISH + }; + struct ChannelInfo + { + EnvState env_state; + uint32_t env_speed; + uint32_t env_value; + uint8_t program; + uint8_t volume; + uint8_t velocity; + uint16_t freq; + uint8_t note; + int8_t bend_coarse; + int8_t bend_fine; + double _bend_fine; + uint8_t pan; + bool keyon; + }; + + private: + uint32_t m_rate; + uint32_t m_env_counter, m_env_incr; + uint32_t m_nch; + C::SCC *m_scc[2]; + uint8_t m_reg_cache[2][0x100]; + uint16_t m_note2freq[128]; + ChannelInfo m_ci[5]; + std::deque m_rbuf[2]; // The rendering buffer + void _UpdateVolume(uint32_t ch); + void _UpdateFreq(uint32_t ch); + void _UpdateProgram(uint32_t ch); + void _WriteReg(uint8_t reg, uint8_t val, int32_t pan = -1); + void _CalcEnvelope(void); + + public: + CSccDevice(uint32_t rate = 44100, uint32_t nch = 2); + virtual ~CSccDevice(); + const SoundDeviceInfo &GetDeviceInfo(void) const; + bool Reset(void); + bool Render(int32_t buf[2]); + + void PercKeyOn(uint8_t note) {}; + void PercKeyOff(uint8_t note) {}; + void PercSetVolume(uint8_t vol) {}; + void PercSetVelocity(uint8_t note, uint8_t velo) {}; + void PercSetProgram(uint8_t note, uint8_t velo) {}; + + void SetProgram(uint32_t ch, uint8_t bank, uint8_t prog); + void SetVelocity(uint32_t ch, uint8_t vel); + void SetPan(uint32_t ch, uint8_t pan); + void SetVolume(uint32_t ch, uint8_t vol); + void SetBend(uint32_t ch, int8_t coarse, int8_t fine); + void KeyOn(uint32_t ch, uint8_t note); + void KeyOff(uint32_t ch); +}; + +} // namespace dsa + +#endif diff --git a/libraries/libemidi/source/ISoundDevice.hpp b/libraries/libemidi/source/ISoundDevice.hpp new file mode 100644 index 000000000..73795fb38 --- /dev/null +++ b/libraries/libemidi/source/ISoundDevice.hpp @@ -0,0 +1,48 @@ +#ifndef __DSA_ISOUND_DEVICE_HPP__ +#define __DSA_ISOUND_DEVICE_HPP__ +#include +// dsa +namespace dsa +{ + +struct SoundDeviceInfo +{ + uint8_t *name; + uint8_t *desc; + uint32_t max_ch; // Maximum Channels. + uint32_t version; // Version no. +}; + +// Sound Device Interface +// +class ISoundDevice +{ + public: + virtual ~ISoundDevice() + { + } + virtual const SoundDeviceInfo &GetDeviceInfo(void) const = 0; + virtual bool Reset(void) = 0; + virtual bool Render(int32_t buf[2]) = 0; + + virtual void SetProgram(uint32_t ch, uint8_t bank, uint8_t prog) = 0; + virtual void SetVelocity(uint32_t ch, uint8_t vel) = 0; + virtual void SetPan(uint32_t ch, uint8_t pan) = 0; + virtual void SetVolume(uint32_t ch, uint8_t vol) = 0; + // coarse: Bend depth at note (-128 to +127) + // fine : Bend depth at cent (-128 to +128) 100 cent equals 1 note. + virtual void SetBend(uint32_t ch, int8_t coarse, int8_t fine) = 0; + virtual void KeyOn(uint32_t ch, uint8_t note) = 0; + virtual void KeyOff(uint32_t ch) = 0; + + // For percussions + virtual void PercKeyOn(uint8_t note) = 0; + virtual void PercKeyOff(uint8_t note) = 0; + virtual void PercSetProgram(uint8_t bank, uint8_t prog) = 0; + virtual void PercSetVelocity(uint8_t note, uint8_t vel) = 0; + virtual void PercSetVolume(uint8_t vol) = 0; +}; + +} // namespace dsa + +#endif // __DSA_ISOUND_DEVICE_HPP__ diff --git a/libraries/libemidi/source/SccInst.h b/libraries/libemidi/source/SccInst.h new file mode 100644 index 000000000..822933a45 --- /dev/null +++ b/libraries/libemidi/source/SccInst.h @@ -0,0 +1,82 @@ +// WAV OCT AR DR SL SR RR +{22, 0, 255, 164, 96, 32, 240}, {22, 0, 255, 164, 96, 32, 240}, {22, 0, 255, 164, 96, 32, 240}, + {22, 0, 255, 164, 96, 32, 240}, {22, 0, 255, 164, 96, 32, 240}, {22, 0, 255, 164, 96, 32, 240}, + {22, 0, 255, 164, 96, 32, 240}, {22, 0, 255, 164, 96, 32, 240}, + + // BELL + {1, 0, 255, 192, 128, 32, 212}, {1, 0, 255, 192, 128, 32, 212}, {1, 0, 255, 192, 128, 32, 212}, + {1, 0, 255, 192, 128, 32, 212}, {1, 0, 255, 192, 128, 32, 212}, {1, 0, 255, 192, 128, 32, 212}, + {1, 0, 255, 192, 128, 32, 212}, {1, 0, 255, 192, 128, 32, 212}, + + // ORGAN + {12, 0, 255, 192, 128, 32, 240}, {12, 0, 255, 192, 128, 32, 240}, {12, 0, 255, 192, 128, 32, 240}, + {12, 0, 255, 192, 128, 32, 240}, {12, 0, 255, 192, 128, 32, 240}, {12, 0, 255, 192, 128, 32, 240}, + {12, 0, 255, 192, 128, 32, 240}, {12, 0, 255, 192, 128, 32, 240}, + + // GUITAR + {16, 0, 255, 192, 64, 32, 240}, {16, 0, 255, 192, 64, 32, 240}, {16, 0, 255, 192, 64, 32, 240}, + {16, 0, 255, 192, 64, 32, 240}, {16, 0, 255, 192, 160, 16, 240}, {16, 0, 255, 160, 96, 8, 240}, + {16, 0, 255, 192, 160, 16, 240}, {0, 2, 255, 192, 192, 16, 240}, + + // BASS + {12, 0, 255, 192, 128, 16, 240}, {4, 0, 255, 192, 128, 16, 240}, {5, 0, 255, 192, 128, 16, 240}, + {4, 0, 255, 192, 128, 16, 240}, {12, 0, 255, 192, 128, 16, 240}, {4, 0, 255, 192, 128, 16, 240}, + {12, 0, 255, 192, 128, 16, 240}, {4, 0, 255, 192, 128, 16, 240}, + + // STRING + {13, 0, 192, 32, 192, 8, 240}, {13, 0, 192, 32, 192, 8, 240}, {13, 0, 192, 32, 192, 8, 240}, + {13, 0, 192, 32, 192, 8, 240}, {13, 0, 192, 32, 192, 8, 240}, {13, 0, 192, 32, 192, 8, 240}, + {13, 0, 192, 32, 192, 8, 240}, {13, 0, 192, 32, 192, 8, 240}, + + // STRING2 + {14, 0, 192, 64, 212, 32, 240}, {14, 0, 192, 64, 212, 32, 240}, {14, 0, 192, 64, 212, 32, 240}, + {14, 0, 192, 64, 212, 32, 240}, {14, 0, 192, 64, 212, 32, 240}, {14, 0, 192, 64, 212, 32, 240}, + {14, 0, 192, 64, 212, 32, 240}, {14, 0, 192, 64, 212, 32, 240}, + + // BRASS OK + {7, 0, 255, 160, 188, 32, 240}, {7, 0, 255, 160, 188, 32, 240}, {7, 0, 255, 160, 188, 32, 240}, + {7, 0, 255, 160, 188, 32, 240}, {7, 0, 255, 160, 188, 32, 240}, {7, 0, 255, 160, 188, 32, 240}, + {7, 0, 255, 160, 188, 32, 240}, {7, 0, 255, 160, 188, 32, 240}, + + // LEAD OK + {9, 0, 240, 120, 188, 32, 240}, {9, 0, 240, 120, 188, 32, 240}, {9, 0, 240, 120, 188, 32, 240}, + {9, 0, 240, 120, 188, 32, 240}, {9, 0, 240, 120, 188, 32, 240}, {9, 0, 240, 120, 188, 32, 240}, + {9, 0, 240, 120, 188, 32, 240}, {9, 0, 240, 120, 188, 32, 240}, + + // PIPE OK + {11, 0, 255, 192, 128, 32, 240}, {11, 0, 255, 192, 128, 32, 240}, {11, 0, 255, 192, 128, 32, 240}, + {11, 0, 255, 192, 128, 32, 240}, {11, 0, 255, 192, 128, 32, 240}, {11, 0, 255, 192, 128, 32, 240}, + {11, 0, 255, 192, 128, 32, 240}, {11, 0, 255, 192, 128, 32, 240}, + + // SYN LEAD + {6, 0, 255, 192, 128, 32, 240}, {6, 0, 255, 192, 128, 32, 240}, {6, 0, 255, 192, 128, 32, 240}, + {6, 0, 255, 192, 128, 32, 240}, {6, 0, 255, 192, 128, 32, 240}, {6, 0, 255, 192, 128, 32, 240}, + {6, 0, 255, 192, 128, 32, 240}, {6, 0, 255, 192, 128, 32, 240}, + + // SYN + {20, 0, 255, 192, 128, 32, 240}, {20, 0, 255, 192, 128, 32, 240}, {20, 0, 255, 192, 128, 32, 240}, + {20, 0, 255, 192, 128, 32, 240}, {20, 0, 255, 192, 128, 32, 240}, {20, 0, 255, 192, 128, 32, 240}, + {20, 0, 255, 192, 128, 32, 240}, {20, 0, 255, 192, 128, 32, 240}, + + // SYN.EFFECT + {12, 0, 255, 192, 128, 32, 240}, {12, 0, 255, 192, 128, 32, 240}, {12, 0, 255, 192, 128, 32, 240}, + {12, 0, 255, 192, 128, 32, 240}, {12, 0, 255, 192, 128, 32, 240}, {12, 0, 255, 192, 128, 32, 240}, + {12, 0, 255, 192, 128, 32, 240}, {12, 0, 255, 192, 128, 32, 240}, + + // ETHNIC + {13, 0, 255, 192, 128, 32, 240}, {13, 0, 255, 192, 128, 32, 240}, {13, 0, 255, 192, 128, 32, 240}, + {13, 0, 255, 192, 128, 32, 240}, {13, 0, 255, 192, 128, 32, 240}, {13, 0, 255, 192, 128, 32, 240}, + {13, 0, 255, 192, 128, 32, 240}, {13, 0, 255, 192, 128, 32, 240}, + + // EFFECT + {14, 0, 255, 192, 128, 32, 240}, {14, 0, 255, 192, 128, 32, 240}, {14, 0, 255, 192, 128, 32, 240}, + {14, 0, 255, 192, 128, 32, 240}, {14, 0, 255, 192, 128, 32, 240}, {14, 0, 255, 192, 128, 32, 240}, + {14, 0, 255, 192, 128, 32, 240}, {14, 0, 255, 192, 128, 32, 240}, + + // SFX + {0, 0, 255, 192, 128, 32, 240}, {15, 0, 255, 192, 128, 32, 240}, {15, 0, 255, 192, 128, 32, 240}, + {15, 0, 255, 192, 128, 32, 240}, {15, 0, 255, 192, 128, 32, 240}, {15, 0, 255, 192, 128, 32, 240}, + {15, 0, 255, 192, 128, 32, 240}, +{ + 15, 0, 255, 192, 128, 32, 240 +} diff --git a/libraries/libemidi/source/SccWave.h b/libraries/libemidi/source/SccWave.h new file mode 100644 index 000000000..045122b44 --- /dev/null +++ b/libraries/libemidi/source/SccWave.h @@ -0,0 +1,68 @@ +{0x80, 0x60, 0xA0, 0x60, 0xC0, 0x60, 0xE0, 0x60, 0x00, 0x61, 0x20, 0x61, 0x40, 0x61, 0x60, 0x61, + 0x80, 0x61, 0xA0, 0x61, 0xC0, 0x61, 0xE0, 0x61, 0x00, 0x62, 0x20, 0x62, 0x40, 0x62, 0x40, 0x62}, + {0x40, 0x62, 0x60, 0x62, 0x80, 0x62, 0xA0, 0x62, 0xC0, 0x62, 0xE0, 0x62, 0x00, 0x60, 0x20, 0x63, + 0x40, 0x63, 0x60, 0x63, 0x80, 0x63, 0x80, 0x63, 0x80, 0x63, 0x80, 0x63, 0xA0, 0x63, 0xC0, 0x63}, + {0xC0, 0x63, 0xE0, 0x63, 0xE0, 0x63, 0x00, 0x64, 0x00, 0x64, 0x00, 0x64, 0x00, 0x60, 0x20, 0x64, + 0x20, 0x64, 0x40, 0x64, 0x60, 0x64, 0x60, 0x64, 0x60, 0x64, 0x60, 0x64, 0x60, 0x64, 0x60, 0x64}, + {0x80, 0x64, 0x80, 0x64, 0x80, 0x64, 0x80, 0x64, 0x80, 0x64, 0x80, 0x64, 0x80, 0x60, 0x80, 0x64, + 0x80, 0x64, 0x80, 0x64, 0x80, 0x64, 0x80, 0x64, 0x80, 0x64, 0x80, 0x64, 0x80, 0x64, 0x80, 0x64}, + {0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x70, 0x7F, 0x7F, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}, + {0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}, + {0x00, 0xF8, 0xF0, 0xE8, 0xE0, 0xD8, 0xD0, 0xC8, 0xC0, 0xB8, 0xB0, 0xA8, 0xA0, 0x90, 0x90, 0x88, + 0x80, 0x78, 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40, 0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08}, + {0x00, 0xF0, 0xE0, 0xD0, 0xC0, 0xB0, 0xA0, 0x90, 0x80, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, + 0x00, 0xF0, 0xE0, 0xD0, 0xC0, 0xB0, 0xA0, 0x90, 0x80, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10}, + {0x00, 0x4E, 0x62, 0x6D, 0x75, 0x7A, 0x7D, 0x7E, 0x7F, 0x7E, 0x7D, 0x7A, 0x75, 0x60, 0x62, 0x4E, + 0x00, 0xB1, 0x9D, 0x92, 0x8A, 0x85, 0x82, 0x81, 0x80, 0x81, 0x82, 0x85, 0x8A, 0x92, 0x9D, 0xB1}, + {0x00, 0x19, 0x31, 0x47, 0x5A, 0x6A, 0x75, 0x7D, 0x7F, 0x7D, 0x75, 0x6A, 0x5A, 0x40, 0x31, 0x19, + 0x00, 0xE7, 0xCF, 0xB9, 0xA6, 0x96, 0x8B, 0x83, 0x80, 0x83, 0x8B, 0x96, 0xA6, 0xB9, 0xCF, 0xE7}, + {0x00, 0xF0, 0xE0, 0xD0, 0xC0, 0xB0, 0xA0, 0x90, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x7F, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10}, + {0x00, 0xE0, 0xC0, 0xA0, 0x80, 0xA0, 0xC0, 0xE0, 0x00, 0x20, 0x40, 0x60, 0x7F, 0x60, 0x40, 0x20, + 0x00, 0xE0, 0xC0, 0xA0, 0x80, 0xA0, 0xC0, 0xE0, 0x00, 0x20, 0x40, 0x60, 0x7F, 0x60, 0x40, 0x20}, + {0x00, 0x19, 0x31, 0x47, 0x5A, 0x6A, 0x75, 0x7D, 0x7F, 0x7D, 0x75, 0x6A, 0x5A, 0x40, 0x31, 0x19, + 0x00, 0xF0, 0xE0, 0xD0, 0xC0, 0xB0, 0xA0, 0x90, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0}, + {0x00, 0x19, 0x31, 0x47, 0x5A, 0x6A, 0x75, 0x7D, 0x7F, 0x7D, 0x75, 0x6A, 0x5A, 0x40, 0x31, 0x19, + 0x00, 0xE0, 0xC0, 0xA0, 0x80, 0xA0, 0xC0, 0xE0, 0x00, 0x20, 0x40, 0x60, 0x7F, 0x60, 0x40, 0x20}, + {0x00, 0x19, 0x31, 0x47, 0x5A, 0x6A, 0x75, 0x7D, 0x7F, 0x7D, 0x75, 0x6A, 0x5A, 0x40, 0x31, 0x19, + 0x80, 0xA0, 0xC0, 0xE0, 0x00, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xE0, 0x00, 0x20, 0x40, 0x60}, + {0x01, 0x2A, 0x40, 0x50, 0x5C, 0x68, 0x70, 0x78, 0x7F, 0x78, 0x70, 0x68, 0x5C, 0x50, 0x40, 0x2A, + 0xFF, 0xD6, 0xC0, 0xB0, 0xA4, 0x98, 0x90, 0x88, 0x81, 0x88, 0x90, 0x98, 0xA4, 0xB0, 0xC0, 0xD6}, + {0x00, 0x40, 0x7F, 0x40, 0x01, 0xC0, 0x81, 0xC0, 0x01, 0x40, 0x7F, 0x40, 0x01, 0xC0, 0x01, 0x40, + 0x01, 0xE0, 0x01, 0x20, 0x01, 0xF0, 0x01, 0x10, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x40, 0x40, 0x40}, + {0x00, 0x40, 0x7F, 0x40, 0x01, 0x10, 0x20, 0x2D, 0x37, 0x44, 0x4E, 0x59, 0x63, 0x60, 0x73, 0x78, + 0x7C, 0x7F, 0x7C, 0x78, 0x73, 0x6C, 0x59, 0x4E, 0x44, 0x37, 0x2D, 0x20, 0x10, 0x03, 0x02, 0x01}, + {0x00, 0x40, 0x7F, 0x40, 0x10, 0x01, 0xEA, 0xD6, 0xC3, 0xB9, 0xAF, 0xA4, 0x9C, 0x90, 0x8F, 0x8A, + 0x86, 0x83, 0x81, 0x83, 0x86, 0x8A, 0x8F, 0x95, 0x9C, 0xA4, 0xAF, 0xB9, 0xC3, 0xD6, 0xEA, 0xFF}, + {0x00, 0x40, 0x7F, 0x40, 0x00, 0xC0, 0xFF, 0xC0, 0x05, 0xEB, 0xD6, 0xC3, 0xB9, 0xA0, 0xA4, 0x9C, + 0x95, 0x8F, 0x89, 0x84, 0x81, 0x84, 0x89, 0x8F, 0x95, 0x9C, 0xA4, 0xAF, 0xB9, 0xC3, 0xD6, 0xEA}, + {0x00, 0xF0, 0xE0, 0xD0, 0xC0, 0xB0, 0xA0, 0x90, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10}, + {0x00, 0x30, 0x50, 0x60, 0x70, 0x60, 0x50, 0x30, 0x00, 0xD0, 0xB0, 0xA0, 0x90, 0xA0, 0xB0, 0xD0, + 0x00, 0x40, 0x60, 0x70, 0x60, 0x40, 0x00, 0xC0, 0xA0, 0x90, 0xA0, 0xC0, 0x00, 0x70, 0x00, 0x90}, + {0x30, 0x50, 0x50, 0x30, 0x00, 0x00, 0x10, 0x40, 0x60, 0x70, 0x60, 0x30, 0xF0, 0xE0, 0xE0, 0x00, + 0x20, 0x20, 0x10, 0xC0, 0xA0, 0x90, 0xA0, 0xC0, 0x00, 0x00, 0xD0, 0xB0, 0xB0, 0xD0, 0x00, 0x00}, + {0xA0, 0x90, 0x90, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xC0, 0xC0, 0xD0, 0xD0, 0xE0, 0xE0, 0xF0, 0xF0, + 0x00, 0x00, 0x10, 0x10, 0x20, 0x20, 0x30, 0x30, 0x40, 0x40, 0x50, 0x50, 0x60, 0x60, 0x60, 0x50}, + {0x00, 0x7F, 0x00, 0x80, 0xA0, 0xC0, 0xD8, 0xF0, 0x08, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x78, + 0x7C, 0x7F, 0x7C, 0x78, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x08, 0xF0, 0xD8, 0xC0, 0xA0, 0x80}, + {0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, + 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80}, + {0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80}, + {0x00, 0x70, 0x50, 0x20, 0x50, 0x70, 0x30, 0x00, 0x50, 0x7F, 0x60, 0x10, 0x30, 0x40, 0x00, 0xB0, + 0x10, 0x60, 0x00, 0xE0, 0xF0, 0x00, 0xB0, 0x90, 0xC0, 0x10, 0xE0, 0xA0, 0xC0, 0xF0, 0xC0, 0xA0}, + {0x80, 0x88, 0x90, 0x98, 0xA0, 0xB0, 0xB0, 0xB8, 0xC0, 0xC8, 0xD0, 0xD8, 0xE0, 0xE0, 0xF0, 0xF8, + 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x78, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x78, 0x78, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x78, 0x78, 0x00, 0x00, 0x80, 0x80}, + {0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}, + {0x00, 0x00, 0x00, 0x80, 0x00, 0x70, 0x70, 0x70, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80}, + {0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x70, 0x70, 0x70, 0x80, 0x80, 0x70, 0x70, 0x70, 0x70, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}, + {0xA0, 0x90, 0x90, 0x90, 0xA0, 0xA0, 0xB0, 0xB0, 0xC0, 0xC0, 0xD0, 0xD0, 0xE0, 0xE0, 0xF0, 0xF0, + 0x00, 0x00, 0x10, 0x10, 0x20, 0x20, 0x30, 0x30, 0x40, 0x40, 0x50, 0x50, 0x60, 0x60, 0x60, 0x50}, diff --git a/libraries/libemidi/source/device/2413tone.h b/libraries/libemidi/source/device/2413tone.h new file mode 100644 index 000000000..73e634333 --- /dev/null +++ b/libraries/libemidi/source/device/2413tone.h @@ -0,0 +1,17 @@ +/* YM2413 tone by okazaki@angel.ne.jp */ +0x49, 0x4c, 0x4c, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x61, 0x1e, 0x17, + 0xf0, 0x7f, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x41, 0x16, 0x0e, 0xfd, 0xf4, 0x23, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x9a, 0x04, 0xf3, 0xf3, 0x13, 0xf3, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x61, 0x0e, 0x07, 0xfa, 0x64, 0x70, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x21, 0x1e, 0x06, 0xf0, 0x76, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x21, 0x22, 0x16, 0x05, 0xf0, 0x71, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x61, 0x1d, + 0x07, 0x82, 0x80, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x21, 0x2d, 0x16, 0x90, 0x90, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x21, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x21, 0x0b, 0x1a, 0x85, 0xa0, 0x70, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x01, 0x83, 0x10, 0xff, 0xb4, 0x10, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x97, 0xc1, 0x20, 0x07, 0xff, 0xf4, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, + 0x0c, 0x05, 0xc2, 0xf6, 0x40, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x56, 0x03, 0x94, + 0xc2, 0x03, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x89, 0x03, 0xf1, 0xe4, 0xf0, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x21, 0x14, 0x00, 0xee, 0xf8, 0xff, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x31, 0x00, 0x00, 0xa7, 0xf7, 0xf7, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x25, 0x11, 0x00, 0x00, 0xf8, 0xfb, 0xf8, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/libraries/libemidi/source/device/281btone.h b/libraries/libemidi/source/device/281btone.h new file mode 100644 index 000000000..cb58bfa90 --- /dev/null +++ b/libraries/libemidi/source/device/281btone.h @@ -0,0 +1,17 @@ +/* YMF281B tone by Chabin */ +0x49, 0x4c, 0x4c, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x21, 0x1a, 0x07, + 0xf0, 0x6f, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x44, 0x02, 0xf6, 0xf4, 0x54, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x97, 0x04, 0xf3, 0xf3, 0x13, 0xf3, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x61, 0x0a, 0x0f, 0xfa, 0x64, 0x70, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x21, 0x1e, 0x06, 0xf0, 0x76, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x61, 0x8a, 0x0e, 0xc0, 0x61, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x61, 0x1b, + 0x07, 0x84, 0x80, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x32, 0xc9, 0x01, 0x66, 0x64, + 0x40, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x21, 0x06, 0x03, 0xa5, 0x71, 0x51, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x11, 0x5e, 0x07, 0xf3, 0xf2, 0xf6, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x18, 0x06, 0xf5, 0xf3, 0x20, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x97, 0x41, 0x20, 0x07, 0xff, 0xf4, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x61, + 0x15, 0x00, 0xf7, 0xf3, 0x16, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x31, 0x0e, 0x07, 0xfa, + 0xf3, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x61, 0x09, 0x07, 0xf1, 0x94, 0xf0, 0xf5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x21, 0x14, 0x00, 0xee, 0xf8, 0xff, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x31, 0x00, 0x00, 0xf8, 0xf7, 0xf8, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x25, 0x11, 0x00, 0x00, 0xf8, 0xfa, 0xf8, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/libraries/libemidi/source/device/emu2149.c b/libraries/libemidi/source/device/emu2149.c new file mode 100644 index 000000000..00b31f422 --- /dev/null +++ b/libraries/libemidi/source/device/emu2149.c @@ -0,0 +1,344 @@ +/**************************************************************************** + + emu2149.c -- YM2149/AY-3-8910 emulator by Mitsutaka Okazaki 2001 + + 2001 04-28 : Version 1.00beta -- 1st Beta Release. + 2001 08-14 : Version 1.10 + 2001 10-03 : Version 1.11 -- Added PSG_set_quality(). + 2002 03-02 : Version 1.12 -- Removed PSG_init & PSG_close. + 2002 10-13 : Version 1.14 -- Fixed the envelope unit. + 2003 09-19 : Version 1.15 -- Added PSG_setMask and PSG_toggleMask + + References: + psg.vhd -- 2000 written by Kazuhiro Tsujikawa. + s_fme7.c -- 1999,2000 written by Mamiya (NEZplug). + ay8910.c -- 1998-2001 Author unknown (MAME). + MSX-Datapack -- 1991 ASCII Corp. + AY-3-8910 data sheet + +*****************************************************************************/ +#include "emu2149.h" + +#include +#include +#include + +static e_uint32 voltbl[2][32] = { + {0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0B, 0x0D, 0x0F, 0x12, + 0x16, 0x1A, 0x1F, 0x25, 0x2D, 0x35, 0x3F, 0x4C, 0x5A, 0x6A, 0x7F, 0x97, 0xB4, 0xD6, 0xFF, 0xFF}, + {0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, 0x0B, 0x0B, 0x0F, 0x0F, + 0x16, 0x16, 0x1F, 0x1F, 0x2D, 0x2D, 0x3F, 0x3F, 0x5A, 0x5A, 0x7F, 0x7F, 0xB4, 0xB4, 0xFF, 0xFF}}; + +#define GETA_BITS 24 + +static void internal_refresh(PSG *psg) +{ + if (psg->quality) + { + psg->base_incr = 1 << GETA_BITS; + psg->realstep = (e_uint32)((1 << 31) / psg->rate); + psg->psgstep = (e_uint32)((1 << 31) / (psg->clk / 16)); + psg->psgtime = 0; + } + else + { + psg->base_incr = (e_uint32)((double)psg->clk * (1 << GETA_BITS) / (16 * psg->rate)); + } +} + +EMU2149_API void PSG_set_rate(PSG *psg, e_uint32 r) +{ + psg->rate = r ? r : 44100; + internal_refresh(psg); +} + +EMU2149_API void PSG_set_quality(PSG *psg, e_uint32 q) +{ + psg->quality = q; + internal_refresh(psg); +} + +EMU2149_API PSG *PSG_new(e_uint32 c, e_uint32 r) +{ + PSG *psg; + + psg = (PSG *)malloc(sizeof(PSG)); + if (psg == NULL) + return NULL; + + PSG_setVolumeMode(psg, EMU2149_VOL_DEFAULT); + psg->clk = c; + psg->rate = r ? r : 44100; + PSG_set_quality(psg, 0); + + return psg; +} + +EMU2149_API void PSG_setVolumeMode(PSG *psg, int type) +{ + switch (type) + { + case 1: + psg->voltbl = voltbl[EMU2149_VOL_YM2149]; + break; + case 2: + psg->voltbl = voltbl[EMU2149_VOL_AY_3_8910]; + break; + default: + psg->voltbl = voltbl[EMU2149_VOL_DEFAULT]; + break; + } +} + +EMU2149_API e_uint32 PSG_setMask(PSG *psg, e_uint32 mask) +{ + e_uint32 ret = 0; + if (psg) + { + ret = psg->mask; + psg->mask = mask; + } + return ret; +} + +EMU2149_API e_uint32 PSG_toggleMask(PSG *psg, e_uint32 mask) +{ + e_uint32 ret = 0; + if (psg) + { + ret = psg->mask; + psg->mask ^= mask; + } + return ret; +} + +EMU2149_API void PSG_reset(PSG *psg) +{ + int i; + + psg->base_count = 0; + + for (i = 0; i < 3; i++) + { + psg->count[i] = 0x1000; + psg->freq[i] = 0; + psg->edge[i] = 0; + psg->volume[i] = 0; + } + + psg->mask = 0; + + for (i = 0; i < 16; i++) + psg->reg[i] = 0; + psg->adr = 0; + + psg->noise_seed = 0xffff; + psg->noise_count = 0x40; + psg->noise_freq = 0; + + psg->env_volume = 0; + psg->env_ptr = 0; + psg->env_freq = 0; + psg->env_count = 0; + psg->env_pause = 1; + + psg->out = 0; +} + +EMU2149_API void PSG_delete(PSG *psg) +{ + free(psg); +} + +EMU2149_API e_uint8 PSG_readIO(PSG *psg) +{ + return (e_uint8)(psg->reg[psg->adr]); +} + +EMU2149_API e_uint8 PSG_readReg(PSG *psg, e_uint32 reg) +{ + return (e_uint8)(psg->reg[reg & 0x1f]); +} + +EMU2149_API void PSG_writeIO(PSG *psg, e_uint32 adr, e_uint32 val) +{ + if (adr & 1) + PSG_writeReg(psg, psg->adr, val); + else + psg->adr = val & 0x1f; +} + +EMU2149_API void PSG_writeReg(PSG *psg, e_uint32 reg, e_uint32 val) +{ + + int c; + + if (reg > 15) + return; + + psg->reg[reg] = (e_uint8)(val & 0xff); + switch (reg) + { + case 0: + case 2: + case 4: + case 1: + case 3: + case 5: + c = reg >> 1; + psg->freq[c] = ((psg->reg[c * 2 + 1] & 15) << 8) + psg->reg[c * 2]; + break; + + case 6: + psg->noise_freq = (val == 0) ? 1 : ((val & 31) << 1); + break; + + case 7: + psg->tmask[0] = (val & 1); + psg->tmask[1] = (val & 2); + psg->tmask[2] = (val & 4); + psg->nmask[0] = (val & 8); + psg->nmask[1] = (val & 16); + psg->nmask[2] = (val & 32); + break; + + case 8: + case 9: + case 10: + psg->volume[reg - 8] = val << 1; + + break; + + case 11: + case 12: + psg->env_freq = (psg->reg[12] << 8) + psg->reg[11]; + break; + + case 13: + psg->env_continue = (val >> 3) & 1; + psg->env_attack = (val >> 2) & 1; + psg->env_alternate = (val >> 1) & 1; + psg->env_hold = val & 1; + psg->env_face = psg->env_attack; + psg->env_pause = 0; + psg->env_count = 0x10000 - psg->env_freq; + psg->env_ptr = psg->env_face ? 0 : 0x1f; + break; + + case 14: + case 15: + default: + break; + } + + return; +} + +INLINE static e_int16 calc(PSG *psg) +{ + + int i, noise; + e_uint32 incr; + e_int32 mix = 0; + + psg->base_count += psg->base_incr; + incr = (psg->base_count >> GETA_BITS); + psg->base_count &= (1 << GETA_BITS) - 1; + + /* Envelope */ + psg->env_count += incr; + while (psg->env_count >= 0x10000) + { + if (!psg->env_pause) + { + if (psg->env_face) + psg->env_ptr = (psg->env_ptr + 1) & 0x3f; + else + psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; + } + + if (psg->env_ptr & 0x20) /* if carry or borrow */ + { + if (psg->env_continue) + { + if (psg->env_alternate ^ psg->env_hold) + psg->env_face ^= 1; + if (psg->env_hold) + psg->env_pause = 1; + psg->env_ptr = psg->env_face ? 0 : 0x1f; + } + else + { + psg->env_pause = 1; + psg->env_ptr = 0; + } + } + if (psg->env_freq == 0) + break; + psg->env_count -= psg->env_freq; + } + + /* Noise */ + psg->noise_count += incr; + if (psg->noise_count & 0x40) + { + if (psg->noise_seed & 1) + psg->noise_seed ^= 0x24000; + psg->noise_seed >>= 1; + psg->noise_count -= psg->noise_freq; + } + noise = psg->noise_seed & 1; + + /* Tone */ + for (i = 0; i < 3; i++) + { + psg->count[i] += incr; + if (psg->count[i] & 0x1000) + { + if (psg->freq[i] > 1) + { + psg->edge[i] = !psg->edge[i]; + psg->count[i] -= psg->freq[i]; + } + else + { + psg->edge[i] = 1; + } + } + + if (psg->mask & PSG_MASK_CH(i)) + continue; + + if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) + { + if (!(psg->volume[i] & 32)) + mix += psg->voltbl[psg->volume[i] & 31]; + else + mix += psg->voltbl[psg->env_ptr]; + } + else if (psg->tmask[i] && psg->nmask[i]) + { + mix += psg->voltbl[psg->volume[i] & 31]; + } + } + + return (e_int16)mix; +} + +EMU2149_API e_int16 PSG_calc(PSG *psg) +{ + if (!psg->quality) + return (e_int16)(calc(psg) << 4); + + /* Simple rate converter */ + while (psg->realstep > psg->psgtime) + { + psg->psgtime += psg->psgstep; + psg->out += calc(psg); + psg->out >>= 1; + } + + psg->psgtime = psg->psgtime - psg->realstep; + + return (e_int16)(psg->out << 4); +} diff --git a/libraries/libemidi/source/device/emu2149.h b/libraries/libemidi/source/device/emu2149.h new file mode 100644 index 000000000..cc6fddc48 --- /dev/null +++ b/libraries/libemidi/source/device/emu2149.h @@ -0,0 +1,92 @@ +/* emu2149.h */ +#ifndef _EMU2149_H_ +#define _EMU2149_H_ +#include "emutypes.h" + +#ifdef EMU2149_DLL_EXPORTS +#define EMU2149_API __declspec(dllexport) +#elif EMU2149_DLL_IMPORTS +#define EMU2149_API __declspec(dllimport) +#else +#define EMU2149_API +#endif + +#define EMU2149_VOL_DEFAULT 1 +#define EMU2149_VOL_YM2149 0 +#define EMU2149_VOL_AY_3_8910 1 + +#define PSG_MASK_CH(x) (1 << (x)) + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct __PSG + { + + /* Volume Table */ + e_uint32 *voltbl; + + e_uint8 reg[0x20]; + e_int32 out; + + e_uint32 clk, rate, base_incr, quality; + + e_uint32 count[3]; + e_uint32 volume[3]; + e_uint32 freq[3]; + e_uint32 edge[3]; + e_uint32 tmask[3]; + e_uint32 nmask[3]; + e_uint32 mask; + + e_uint32 base_count; + + e_uint32 env_volume; + e_uint32 env_ptr; + e_uint32 env_face; + + e_uint32 env_continue; + e_uint32 env_attack; + e_uint32 env_alternate; + e_uint32 env_hold; + e_uint32 env_pause; + e_uint32 env_reset; + + e_uint32 env_freq; + e_uint32 env_count; + + e_uint32 noise_seed; + e_uint32 noise_count; + e_uint32 noise_freq; + + /* rate converter */ + e_uint32 realstep; + e_uint32 psgtime; + e_uint32 psgstep; + + /* I/O Ctrl */ + e_uint32 adr; + + } PSG; + + EMU2149_API void PSG_set_quality(PSG *psg, e_uint32 q); + EMU2149_API void PSG_set_rate(PSG *psg, e_uint32 r); + EMU2149_API PSG *PSG_new(e_uint32 clk, e_uint32 rate); + EMU2149_API void PSG_reset(PSG *); + EMU2149_API void PSG_delete(PSG *); + EMU2149_API void PSG_writeReg(PSG *, e_uint32 reg, e_uint32 val); + EMU2149_API void PSG_writeIO(PSG *psg, e_uint32 adr, e_uint32 val); + EMU2149_API e_uint8 PSG_readReg(PSG *psg, e_uint32 reg); + EMU2149_API e_uint8 PSG_readIO(PSG *psg); + EMU2149_API e_int16 PSG_calc(PSG *); + EMU2149_API void PSG_setVolumeMode(PSG *psg, int type); + EMU2149_API e_uint32 PSG_setMask(PSG *, e_uint32 mask); + EMU2149_API e_uint32 PSG_toggleMask(PSG *, e_uint32 mask); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libraries/libemidi/source/device/emu2212.c b/libraries/libemidi/source/device/emu2212.c new file mode 100644 index 000000000..527b30cbe --- /dev/null +++ b/libraries/libemidi/source/device/emu2212.c @@ -0,0 +1,535 @@ +/**************************************************************************** + + emu2212.c -- S.C.C. emulator by Mitsutaka Okazaki 2001 + + 2001 09-30 : Version 1.00 + 2001 10-03 : Version 1.01 -- Added SCC_set_quality(). + 2002 02-14 : Version 1.10 -- Added SCC_writeReg(), SCC_set_type(). + Fixed SCC_write(). + 2002 02-17 : Version 1.11 -- Fixed SCC_write(). + 2002 03-02 : Version 1.12 -- Removed SCC_init & SCC_close. + 2003 09-19 : Version 1.13 -- Added SCC_setMask() and SCC_toggleMask() + + Registar map for SCC_writeReg() + + $00-1F : WaveTable CH.A + $20-3F : WaveTable CH.B + $40-5F : WaveTable CH.C + $60-7F : WaveTable CH.D&E(SCC), CH.D(SCC+) + $80-9F : WaveTable CH.E + + $C0 : CH.A Freq(L) + $C1 : CH.A Freq(H) + $C2 : CH.B Freq(L) + $C3 : CH.B Freq(H) + $C4 : CH.C Freq(L) + $C5 : CH.C Freq(H) + $C6 : CH.D Freq(L) + $C7 : CH.D Freq(H) + $C8 : CH.E Freq(L) + $C9 : CH.E Freq(H) + + $D0 : CH.A Volume + $D1 : CH.B Volume + $D2 : CH.C Volume + $D3 : CH.D Volume + $D4 : CH.E Volume + + $E0 : Bit0 = 0:SCC, 1:SCC+ + $E1 : CH mask + $E2 : Extra Flags + +(Extra) + $F0 : CH.A Pan + $F1 : CH.B Pan + $F2 : CH.C Pan + $F3 : CH.D Pan + $F4 : CH.E Pan + +*****************************************************************************/ +#include "emu2212.h" + +#include +#include +#include + +#define GETA_BITS 22 + +static void internal_refresh(SCC *scc) +{ + if (scc->quality) + { + scc->base_incr = 2 << GETA_BITS; + scc->realstep = (e_uint32)((1 << 31) / scc->rate); + scc->sccstep = (e_uint32)((1 << 31) / (scc->clk / 2)); + scc->scctime = 0; + } + else + { + scc->base_incr = (e_uint32)((double)scc->clk * (1 << GETA_BITS) / scc->rate); + } +} + +EMU2212_API e_uint32 SCC_setMask(SCC *scc, e_uint32 mask) +{ + e_uint32 ret = 0; + if (scc) + { + ret = scc->mask; + scc->mask = mask; + } + return ret; +} + +EMU2212_API e_uint32 SCC_toggleMask(SCC *scc, e_uint32 mask) +{ + e_uint32 ret = 0; + if (scc) + { + ret = scc->mask; + scc->mask ^= mask; + } + return ret; +} + +EMU2212_API void SCC_set_quality(SCC *scc, e_uint32 q) +{ + scc->quality = q; + internal_refresh(scc); +} + +EMU2212_API void SCC_set_rate(SCC *scc, e_uint32 r) +{ + scc->rate = r ? r : 44100; + internal_refresh(scc); +} + +EMU2212_API SCC *SCC_new(e_uint32 c, e_uint32 r) +{ + SCC *scc; + + scc = (SCC *)malloc(sizeof(SCC)); + if (scc == NULL) + return NULL; + memset(scc, 0, sizeof(SCC)); + + scc->clk = c; + scc->rate = r ? r : 44100; + SCC_set_quality(scc, 0); + + return scc; +} + +EMU2212_API void SCC_reset(SCC *scc) +{ + + int i, j; + + if (scc == NULL) + return; + + scc->type = SCC_STANDARD; + scc->mode = 0; + scc->save_9000 = 0x3f; + scc->save_B000 = 0; + scc->save_BFFE = 0; + + for (i = 0; i < 5; i++) + { + for (j = 0; j < 5; j++) + scc->wave[i][j] = 0; + scc->count[i] = 0; + scc->freq[i] = 0; + scc->phase[i] = 0; + scc->volume[i] = 0; + scc->offset[i] = 0; + scc->rotate[i] = 0; + scc->ch_pan[i] = 3; + } + + scc->mask = 0; + + scc->ch_enable = 0xff; + scc->ch_enable_next = 0xff; + + scc->cycle_4bit = 0; + scc->cycle_8bit = 0; + scc->refresh = 0; + + scc->out = 0; + scc->prev = 0; + scc->next = 0; + + return; +} + +EMU2212_API void SCC_delete(SCC *scc) +{ + if (scc != NULL) + free(scc); +} + +INLINE static e_int16 calc(SCC *scc) +{ + int i; + e_int32 mix = 0; + + for (i = 0; i < 5; i++) + { + scc->count[i] = (scc->count[i] + scc->incr[i]); + + if (scc->count[i] & (1 << (GETA_BITS + 5))) + { + scc->count[i] &= ((1 << (GETA_BITS + 5)) - 1); + scc->offset[i] = (scc->offset[i] + 31) & scc->rotate[i]; + scc->ch_enable &= ~(1 << i); + scc->ch_enable |= scc->ch_enable_next & (1 << i); + } + + if (scc->ch_enable & (1 << i)) + { + scc->phase[i] = ((scc->count[i] >> (GETA_BITS)) + scc->offset[i]) & 0x1F; + if (!(scc->mask & SCC_MASK_CH(i))) + mix += ((((e_int8)(scc->wave[i][scc->phase[i]]) * (e_int8)scc->volume[i]))) >> 4; + } + } + + return (e_int16)(mix << 4); +} + +EMU2212_API e_int16 SCC_calc(SCC *scc) +{ + if (!scc->quality) + return calc(scc); + + while (scc->realstep > scc->scctime) + { + scc->scctime += scc->sccstep; + scc->prev = scc->next; + scc->next = calc(scc); + } + + scc->scctime -= scc->realstep; + scc->out = (e_int16)(((double)scc->next * (scc->sccstep - scc->scctime) + (double)scc->prev * scc->scctime) / + scc->sccstep); + + return (e_int16)(scc->out); +} + +EMU2212_API e_uint32 SCC_readReg(SCC *scc, e_uint32 adr) +{ + if (adr < 0xA0) + return scc->wave[adr >> 5][adr & 0x1f]; + else + return 0; +} + +EMU2212_API e_uint32 SCC_read(SCC *scc, e_uint32 adr) +{ + if (!scc->mode && scc->save_9000 == 0x3F && (adr & 0xF800) == 0x9800) + { + adr &= 0xff; + + if (adr < 0x80) + return scc->wave[adr >> 5][adr & 0x1f]; + else + return 0; + } + else if (scc->mode && scc->save_B000 == 0x80 && (adr & 0xF800) == 0xB800) + { + adr &= 0xff; + if (adr < 0x80) + return scc->wave[adr >> 5][adr & 0x1f]; + else if (0xA0 <= adr && adr <= 0xBF) + return scc->wave[4][adr & 0x1f]; + else + return 0; + } + else + return 0; +} + +EMU2212_API void SCC_writeReg(SCC *scc, e_uint32 adr, e_uint32 val) +{ + int ch; + e_uint32 freq; + + adr &= 0xFF; + + if (adr < 0xA0) + { + ch = (adr & 0xF0) >> 5; + if (!scc->rotate[ch]) + { + scc->wave[ch][adr & 0x1F] = (e_int8)val; + if (scc->mode == 0 && ch == 3) + scc->wave[4][adr & 0x1F] = (e_int8)val; + } + } + else if (0xC0 <= adr && adr <= 0xC9) + { + ch = (adr & 0x0F) >> 1; + if (adr & 1) + scc->freq[ch] = ((val & 0xF) << 8) | (scc->freq[ch] & 0xFF); + else + scc->freq[ch] = (scc->freq[ch] & 0xF00) | (val & 0xFF); + + if (scc->refresh) + scc->count[ch] = 0; + freq = scc->freq[ch]; + if (scc->cycle_8bit) + freq &= 0xFF; + if (scc->cycle_4bit) + freq >>= 8; + if (freq <= 8) + scc->incr[ch] = 0; + else + scc->incr[ch] = scc->base_incr / (freq + 1); + } + else if (0xD0 <= adr && adr <= 0xD4) + { + scc->volume[adr & 0x0F] = (e_uint8)(val & 0xF); + } + else if (adr == 0xE0) + { + scc->mode = (e_uint8)val & 1; + if (scc->mode) + scc->save_BFFE |= 0x20; + else + scc->save_BFFE &= 0xDF; /* Synchronized with BFFE */ + } + else if (adr == 0xE1) + { + scc->ch_enable_next = (e_uint8)val & 0x1F; + } + else if (adr == 0xE2) + { + scc->cycle_4bit = val & 1; + scc->cycle_8bit = val & 2; + scc->refresh = val & 32; + if (val & 64) + for (ch = 0; ch < 5; ch++) + scc->rotate[ch] = 0x1F; + else + for (ch = 0; ch < 5; ch++) + scc->rotate[ch] = 0; + if (val & 128) + scc->rotate[3] = scc->rotate[4] = 0x1F; + } + else if (0xF0 <= adr && adr <= 0xF4) + scc->ch_pan[adr & 0xF] = val; + + return; +} + +INLINE static void write_standard(SCC *scc, e_uint32 adr, e_uint32 val) +{ + if ((adr & 0xF800) != 0x9800) + return; + + adr &= 0xFF; + + if (adr < 0x80) /* wave */ + { + SCC_writeReg(scc, adr, val); + } + else if (adr < 0x8A) /* freq */ + { + SCC_writeReg(scc, adr + 0xC0 - 0x80, val); + } + else if (adr < 0x8F) /* volume */ + { + SCC_writeReg(scc, adr + 0xD0 - 0x8A, val); + } + else if (adr == 0x8F) /* ch enable */ + { + SCC_writeReg(scc, 0xE1, val); + } + else if (0xE0 <= adr) /* flags */ + { + SCC_writeReg(scc, 0xE2, val); + } +} + +INLINE static void write_compatible(SCC *scc, e_uint32 adr, e_uint32 val) +{ + if ((adr & 0xF800) != 0x9800) + return; + + adr &= 0xFF; + + if (adr < 0x80) /* wave */ + { + SCC_writeReg(scc, adr, val); + } + else if (adr < 0x8A) /* freq */ + { + SCC_writeReg(scc, adr + 0xC0 - 0x80, val); + } + else if (adr < 0x8F) /* volume */ + { + SCC_writeReg(scc, adr + 0xD0 - 0x8A, val); + } + else if (adr == 0x8F) /* ch enable */ + { + SCC_writeReg(scc, 0xE1, val); + } + else if (0xC0 <= adr && adr <= 0xDF) /* flags */ + { + SCC_writeReg(scc, 0xE2, val); + } +} + +INLINE static void write_enhanced(SCC *scc, e_uint32 adr, e_uint32 val) +{ + if ((adr & 0xF800) != 0xB800) + return; + + adr &= 0xFF; + + if (adr < 0x80) /* wave */ + { + SCC_writeReg(scc, adr, val); + } + else if (adr < 0x8A) /* freq */ + { + SCC_writeReg(scc, adr + 0xC0 - 0x80, val); + } + else if (adr < 0x8F) /* volume */ + { + SCC_writeReg(scc, adr + 0xD0 - 0x8A, val); + } + else if (adr == 0x8F) /* ch enable */ + { + SCC_writeReg(scc, 0xE1, val); + } + else if (0xA0 <= adr && adr <= 0xBF && scc->mode) /* ch[4].wave */ + { + SCC_writeReg(scc, adr + 0x80 - 0xA0, val); + } + else if (0xC0 <= adr && adr <= 0xDF) /* flags */ + { + SCC_writeReg(scc, 0xE2, val); + } +} + +INLINE static int BFFE_write(SCC *scc, e_uint32 adr, e_uint32 val) +{ + if ((adr & 0xFFFE) == 0xBFFE) + { + scc->save_BFFE = (e_uint8)val; + if (val & 0x20) + SCC_writeReg(scc, 0xE0, 1); + else + SCC_writeReg(scc, 0xE0, 0); + return 1; + } + else + { + return 0; + } +} + +INLINE static int mapper_write(SCC *scc, e_uint32 adr, e_uint32 val) +{ + if ((adr & 0xF800) == 0x9000) + { + scc->save_9000 = (e_uint8)(val & 0x3F); + return 1; + } + else if (scc->type == SCC_ENHANCED) + { + if (BFFE_write(scc, adr, val)) + { + return 1; + } + else if ((adr & 0xF800) == 0xB000) + { + scc->save_B000 = (e_uint8)(val & 0x80); + return 1; + } + } + return 0; +} + +EMU2212_API void SCC_write(SCC *scc, e_uint32 adr, e_uint32 val) +{ + val = val & 0xFF; + + if (scc->save_BFFE & 0x10) /* BFFE can only be accessed. */ + { + BFFE_write(scc, adr, val); + return; + } + else if ((scc->save_BFFE & 0x04) && (adr < 0xA000)) /* Ignore 8000H - 9FFFH */ + { + return; + } + else if (mapper_write(scc, adr, val)) + return; /* Mapper ctrl registers */ + + switch (scc->type) + { + case SCC_STANDARD: + if (scc->save_9000 == 0x3F) + write_standard(scc, adr, val); + break; + + case SCC_ENHANCED: + if (!scc->mode && scc->save_9000 == 0x3F) + write_compatible(scc, adr, val); + else if (scc->mode && scc->save_B000 == 0x80) + write_enhanced(scc, adr, val); + break; + + default: + break; + } + + return; +} + +EMU2212_API void SCC_set_type(SCC *scc, e_uint32 type) +{ + scc->type = type; +} + +EMU2212_API void SCC_calc_stereo(SCC *scc, e_int16 buf[2]) +{ + + int i; + e_int16 b; + buf[0] = buf[1] = 0; + + for (i = 0; i < 5; i++) + { + scc->count[i] = (scc->count[i] + scc->incr[i]); + + if (scc->count[i] & (1 << (GETA_BITS + 5))) + { + scc->count[i] &= ((1 << (GETA_BITS + 5)) - 1); + scc->offset[i] = (scc->offset[i] + 31) & scc->rotate[i]; + scc->ch_enable &= ~(1 << i); + scc->ch_enable |= scc->ch_enable_next & (1 << i); + } + + if (scc->ch_enable & (1 << i)) + { + scc->phase[i] = ((scc->count[i] >> (GETA_BITS)) + scc->offset[i]) & 0x1F; + if (!(scc->mask & SCC_MASK_CH(i))) + { + b = ((((e_int8)(scc->wave[i][scc->phase[i]]) * (e_int8)scc->volume[i]))) >> 4; + if (scc->ch_pan[i] == 1) + buf[0] += b; + else if (scc->ch_pan[i] == 2) + buf[1] += b; + else + buf[0] += b; + buf[1] += b; + } + } + } + + buf[0] <<= 3; + buf[1] <<= 3; +} diff --git a/libraries/libemidi/source/device/emu2212.h b/libraries/libemidi/source/device/emu2212.h new file mode 100644 index 000000000..8d840d69d --- /dev/null +++ b/libraries/libemidi/source/device/emu2212.h @@ -0,0 +1,82 @@ +#ifndef _EMU2212_H_ +#define _EMU2212_H_ + +#ifdef EMU2212_DLL_EXPORTS +#define EMU2212_API __declspec(dllexport) +#elif defined(EMU2212_DLL_IMPORTS) +#define EMU2212_API __declspec(dllimport) +#else +#define EMU2212_API +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "emutypes.h" + +#define SCC_STANDARD 0 +#define SCC_ENHANCED 1 + +#define SCC_MASK_CH(x) (1 << (x)) + + typedef struct __SCC + { + + e_uint32 clk, rate, base_incr, quality; + + e_int32 out, prev, next; + e_uint32 type; + e_uint32 mode; + e_uint32 mask; + + e_uint32 realstep; + e_uint32 scctime; + e_uint32 sccstep; + + e_uint32 incr[5]; + + e_uint8 save_9000; + e_uint8 save_B000; + e_uint8 save_BFFE; + + e_int8 wave[5][32]; + + e_uint32 count[5]; + e_uint32 freq[5]; + e_uint32 phase[5]; + e_uint32 volume[5]; + e_uint32 offset[5]; + + int ch_enable; + int ch_enable_next; + + int cycle_4bit; + int cycle_8bit; + int refresh; + int rotate[5]; + + int ch_pan[5]; + + } SCC; + + EMU2212_API SCC *SCC_new(e_uint32 c, e_uint32 r); + EMU2212_API void SCC_reset(SCC *scc); + EMU2212_API void SCC_set_rate(SCC *scc, e_uint32 r); + EMU2212_API void SCC_set_quality(SCC *scc, e_uint32 q); + EMU2212_API void SCC_set_type(SCC *scc, e_uint32 type); + EMU2212_API void SCC_delete(SCC *scc); + EMU2212_API e_int16 SCC_calc(SCC *scc); + EMU2212_API void SCC_calc_stereo(SCC *scc, e_int16 buf[2]); + EMU2212_API void SCC_write(SCC *scc, e_uint32 adr, e_uint32 val); + EMU2212_API void SCC_writeReg(SCC *scc, e_uint32 adr, e_uint32 val); + EMU2212_API e_uint32 SCC_read(SCC *scc, e_uint32 adr); + EMU2212_API e_uint32 SCC_setMask(SCC *scc, e_uint32 adr); + EMU2212_API e_uint32 SCC_toggleMask(SCC *scc, e_uint32 adr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libraries/libemidi/source/device/emu2413.c b/libraries/libemidi/source/device/emu2413.c new file mode 100644 index 000000000..005f50008 --- /dev/null +++ b/libraries/libemidi/source/device/emu2413.c @@ -0,0 +1,1779 @@ +/*********************************************************************************** + + emu2413.c -- YM2413 emulator written by Mitsutaka Okazaki 2001 + + 2001 01-08 : Version 0.10 -- 1st version. + 2001 01-15 : Version 0.20 -- semi-public version. + 2001 01-16 : Version 0.30 -- 1st public version. + 2001 01-17 : Version 0.31 -- Fixed bassdrum problem. + : Version 0.32 -- LPF implemented. + 2001 01-18 : Version 0.33 -- Fixed the drum problem, refine the mix-down method. + -- Fixed the LFO bug. + 2001 01-24 : Version 0.35 -- Fixed the drum problem, + support undocumented EG behavior. + 2001 02-02 : Version 0.38 -- Improved the performance. + Fixed the hi-hat and cymbal model. + Fixed the default percussive datas. + Noise reduction. + Fixed the feedback problem. + 2001 03-03 : Version 0.39 -- Fixed some drum bugs. + Improved the performance. + 2001 03-04 : Version 0.40 -- Improved the feedback. + Change the default table size. + Clock and Rate can be changed during play. + 2001 06-24 : Version 0.50 -- Improved the hi-hat and the cymbal tone. + Added VRC7 patch (OPLL_reset_patch is changed). + Fixed OPLL_reset() bug. + Added OPLL_setMask, OPLL_getMask and OPLL_toggleMask. + Added OPLL_writeIO. + 2001 09-28 : Version 0.51 -- Removed the noise table. + 2002 01-28 : Version 0.52 -- Added Stereo mode. + 2002 02-07 : Version 0.53 -- Fixed some drum bugs. + 2002 02-20 : Version 0.54 -- Added the best quality mode. + 2002 03-02 : Version 0.55 -- Removed OPLL_init & OPLL_close. + 2002 05-30 : Version 0.60 -- Fixed HH&CYM generator and all voice datas. + 2004 04-10 : Version 0.61 -- Added YMF281B tone (defined by Chabin). + + References: + fmopl.c -- 1999,2000 written by Tatsuyuki Satoh (MAME development). + fmopl.c(fixed) -- (C) 2002 Jarek Burczynski. + s_opl.c -- 2001 written by Mamiya (NEZplug development). + fmgen.cpp -- 1999,2000 written by cisc. + fmpac.ill -- 2000 created by NARUTO. + MSX-Datapack + YMU757 data sheet + YM2143 data sheet + +**************************************************************************************/ +#include "emu2413.h" + +#include +#include +#include +#include + +#ifdef EMU2413_COMPACTION +#define OPLL_TONE_NUM 1 +static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 16] = {{ +#include "2413tone.h" +}}; +#else +#define OPLL_TONE_NUM 3 +static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 16] = {{ +#include "2413tone.h" + }, + { +#include "vrc7tone.h" + }, + { +#include "281btone.h" + }}; +#endif + +/* Size of Sintable ( 8 -- 18 can be used. 9 recommended.) */ +#define PG_BITS 9 +#define PG_WIDTH (1 << PG_BITS) + +/* Phase increment counter */ +#define DP_BITS 18 +#define DP_WIDTH (1 << DP_BITS) +#define DP_BASE_BITS (DP_BITS - PG_BITS) + +/* Dynamic range (Accuracy of sin table) */ +#define DB_BITS 8 +#define DB_STEP (48.0 / (1 << DB_BITS)) +#define DB_MUTE (1 << DB_BITS) + +/* Dynamic range of envelope */ +#define EG_STEP 0.375 +#define EG_BITS 7 +#define EG_MUTE (1 << EG_BITS) + +/* Dynamic range of total level */ +#define TL_STEP 0.75 +#define TL_BITS 6 +#define TL_MUTE (1 << TL_BITS) + +/* Dynamic range of sustine level */ +#define SL_STEP 3.0 +#define SL_BITS 4 +#define SL_MUTE (1 << SL_BITS) + +#define EG2DB(d) ((d) * (e_int32)(EG_STEP / DB_STEP)) +#define TL2EG(d) ((d) * (e_int32)(TL_STEP / EG_STEP)) +#define SL2EG(d) ((d) * (e_int32)(SL_STEP / EG_STEP)) + +#define DB_POS(x) (e_uint32)((x) / DB_STEP) +#define DB_NEG(x) (e_uint32)(DB_MUTE + DB_MUTE + (x) / DB_STEP) + +/* Bits for liner value */ +#define DB2LIN_AMP_BITS 8 +#define SLOT_AMP_BITS (DB2LIN_AMP_BITS) + +/* Bits for envelope phase incremental counter */ +#define EG_DP_BITS 22 +#define EG_DP_WIDTH (1 << EG_DP_BITS) + +/* Bits for Pitch and Amp modulator */ +#define PM_PG_BITS 8 +#define PM_PG_WIDTH (1 << PM_PG_BITS) +#define PM_DP_BITS 16 +#define PM_DP_WIDTH (1 << PM_DP_BITS) +#define AM_PG_BITS 8 +#define AM_PG_WIDTH (1 << AM_PG_BITS) +#define AM_DP_BITS 16 +#define AM_DP_WIDTH (1 << AM_DP_BITS) + +/* PM table is calcurated by PM_AMP * pow(2,PM_DEPTH*sin(x)/1200) */ +#define PM_AMP_BITS 8 +#define PM_AMP (1 << PM_AMP_BITS) + +/* PM speed(Hz) and depth(cent) */ +#define PM_SPEED 6.4 +#define PM_DEPTH 13.75 + +/* AM speed(Hz) and depth(dB) */ +#define AM_SPEED 3.6413 +#define AM_DEPTH 4.875 + +/* Cut the lower b bit(s) off. */ +#define HIGHBITS(c, b) ((c) >> (b)) + +/* Leave the lower b bit(s). */ +#define LOWBITS(c, b) ((c) & ((1 << (b)) - 1)) + +/* Expand x which is s bits to d bits. */ +#define EXPAND_BITS(x, s, d) ((x) << ((d) - (s))) + +/* Expand x which is s bits to d bits and fill expanded bits '1' */ +#define EXPAND_BITS_X(x, s, d) (((x) << ((d) - (s))) | ((1 << ((d) - (s))) - 1)) + +/* Adjust envelope speed which depends on sampling rate. */ +#define RATE_ADJUST(x) \ + (rate == 49716 ? x : (e_uint32)((double)(x) * clk / 72 / rate + 0.5)) /* added 0.5 to round the value*/ + +#define MOD(o, x) (&(o)->slot[(x) << 1]) +#define CAR(o, x) (&(o)->slot[((x) << 1) | 1]) + +#define BIT(s, b) (((s) >> (b)) & 1) + +/* Input clock */ +static e_uint32 clk = 844451141; +/* Sampling rate */ +static e_uint32 rate = 3354932; + +/* WaveTable for each envelope amp */ +static e_uint16 fullsintable[PG_WIDTH]; +static e_uint16 halfsintable[PG_WIDTH]; + +static e_uint16 *waveform[2] = {fullsintable, halfsintable}; + +/* LFO Table */ +static e_int32 pmtable[PM_PG_WIDTH]; +static e_int32 amtable[AM_PG_WIDTH]; + +/* Phase delta for LFO */ +static e_uint32 pm_dphase; +static e_uint32 am_dphase; + +/* dB to Liner table */ +static e_int16 DB2LIN_TABLE[(DB_MUTE + DB_MUTE) * 2]; + +/* Liner to Log curve conversion table (for Attack rate). */ +static e_uint16 AR_ADJUST_TABLE[1 << EG_BITS]; + +/* Empty voice data */ +static OPLL_PATCH null_patch = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* Basic voice Data */ +static OPLL_PATCH default_patch[OPLL_TONE_NUM][(16 + 3) * 2]; + +/* Definition of envelope mode */ +enum OPLL_EG_STATE +{ + READY, + ATTACK, + DECAY, + SUSHOLD, + SUSTINE, + RELEASE, + SETTLE, + FINISH +}; + +/* Phase incr table for Attack */ +static e_uint32 dphaseARTable[16][16]; +/* Phase incr table for Decay and Release */ +static e_uint32 dphaseDRTable[16][16]; + +/* KSL + TL Table */ +static e_uint32 tllTable[16][8][1 << TL_BITS][4]; +static e_int32 rksTable[2][8][2]; + +/* Phase incr table for PG */ +static e_uint32 dphaseTable[512][8][16]; + +/*************************************************** + + Create tables + +****************************************************/ +INLINE static e_int32 Min(e_int32 i, e_int32 j) +{ + if (i < j) + return i; + else + return j; +} + +/* Table for AR to LogCurve. */ +static void makeAdjustTable(void) +{ + e_int32 i; + + AR_ADJUST_TABLE[0] = (1 << EG_BITS) - 1; + for (i = 1; i < (1 << EG_BITS); i++) + AR_ADJUST_TABLE[i] = (e_uint16)((double)(1 << EG_BITS) - 1 - ((1 << EG_BITS) - 1) * log(i) / log(127)); +} + +/* Table for dB(0 -- (1<= DB_MUTE) + DB2LIN_TABLE[i] = 0; + DB2LIN_TABLE[i + DB_MUTE + DB_MUTE] = (e_int16)(-DB2LIN_TABLE[i]); + } +} + +/* Liner(+0.0 - +1.0) to dB((1<> (20 - DP_BITS)); +} + +static void makeTllTable(void) +{ +#define dB2(x) ((x) * 2) + + static double kltable[16] = {dB2(0.000), dB2(9.000), dB2(12.000), dB2(13.875), dB2(15.000), dB2(16.125), + dB2(16.875), dB2(17.625), dB2(18.000), dB2(18.750), dB2(19.125), dB2(19.500), + dB2(19.875), dB2(20.250), dB2(20.625), dB2(21.000)}; + + e_int32 tmp; + e_int32 fnum, block, TL, KL; + + for (fnum = 0; fnum < 16; fnum++) + for (block = 0; block < 8; block++) + for (TL = 0; TL < 64; TL++) + for (KL = 0; KL < 4; KL++) + { + if (KL == 0) + { + tllTable[fnum][block][TL][KL] = TL2EG(TL); + } + else + { + tmp = (e_int32)(kltable[fnum] - dB2(3.000) * (7 - block)); + if (tmp <= 0) + tllTable[fnum][block][TL][KL] = TL2EG(TL); + else + tllTable[fnum][block][TL][KL] = (e_uint32)((tmp >> (3 - KL)) / EG_STEP) + TL2EG(TL); + } + } +} + +#ifdef USE_SPEC_ENV_SPEED +static double attacktime[16][4] = {{0, 0, 0, 0}, + {1730.15, 1400.60, 1153.43, 988.66}, + {865.08, 700.30, 576.72, 494.33}, + {432.54, 350.15, 288.36, 247.16}, + {216.27, 175.07, 144.18, 123.58}, + {108.13, 87.54, 72.09, 61.79}, + {54.07, 43.77, 36.04, 30.90}, + {27.03, 21.88, 18.02, 15.45}, + {13.52, 10.94, 9.01, 7.72}, + {6.76, 5.47, 4.51, 3.86}, + {3.38, 2.74, 2.25, 1.93}, + {1.69, 1.37, 1.13, 0.97}, + {0.84, 0.70, 0.60, 0.54}, + {0.50, 0.42, 0.34, 0.30}, + {0.28, 0.22, 0.18, 0.14}, + {0.00, 0.00, 0.00, 0.00}}; + +static double decaytime[16][4] = {{0, 0, 0, 0}, + {20926.60, 16807.20, 14006.00, 12028.60}, + {10463.30, 8403.58, 7002.98, 6014.32}, + {5231.64, 4201.79, 3501.49, 3007.16}, + {2615.82, 2100.89, 1750.75, 1503.58}, + {1307.91, 1050.45, 875.37, 751.79}, + {653.95, 525.22, 437.69, 375.90}, + {326.98, 262.61, 218.84, 187.95}, + {163.49, 131.31, 109.42, 93.97}, + {81.74, 65.65, 54.71, 46.99}, + {40.87, 32.83, 27.36, 23.49}, + {20.44, 16.41, 13.68, 11.75}, + {10.22, 8.21, 6.84, 5.87}, + {5.11, 4.10, 3.42, 2.94}, + {2.55, 2.05, 1.71, 1.47}, + {1.27, 1.27, 1.27, 1.27}}; +#endif + +/* Rate Table for Attack */ +static void makeDphaseARTable(void) +{ + e_int32 AR, Rks, RM, RL; + +#ifdef USE_SPEC_ENV_SPEED + e_uint32 attacktable[16][4]; + + for (RM = 0; RM < 16; RM++) + for (RL = 0; RL < 4; RL++) + { + if (RM == 0) + attacktable[RM][RL] = 0; + else if (RM == 15) + attacktable[RM][RL] = EG_DP_WIDTH; + else + attacktable[RM][RL] = (e_uint32)((double)(1 << EG_DP_BITS) / (attacktime[RM][RL] * 3579545 / 72000)); + } +#endif + + for (AR = 0; AR < 16; AR++) + for (Rks = 0; Rks < 16; Rks++) + { + RM = AR + (Rks >> 2); + RL = Rks & 3; + if (RM > 15) + RM = 15; + switch (AR) + { + case 0: + dphaseARTable[AR][Rks] = 0; + break; + case 15: + dphaseARTable[AR][Rks] = 0; /*EG_DP_WIDTH;*/ + break; + default: +#ifdef USE_SPEC_ENV_SPEED + dphaseARTable[AR][Rks] = RATE_ADJUST(attacktable[RM][RL]); +#else + dphaseARTable[AR][Rks] = RATE_ADJUST((3 * (RL + 4) << (RM + 1))); +#endif + break; + } + } +} + +/* Rate Table for Decay and Release */ +static void makeDphaseDRTable(void) +{ + e_int32 DR, Rks, RM, RL; + +#ifdef USE_SPEC_ENV_SPEED + e_uint32 decaytable[16][4]; + + for (RM = 0; RM < 16; RM++) + for (RL = 0; RL < 4; RL++) + if (RM == 0) + decaytable[RM][RL] = 0; + else + decaytable[RM][RL] = (e_uint32)((double)(1 << EG_DP_BITS) / (decaytime[RM][RL] * 3579545 / 72000)); +#endif + + for (DR = 0; DR < 16; DR++) + for (Rks = 0; Rks < 16; Rks++) + { + RM = DR + (Rks >> 2); + RL = Rks & 3; + if (RM > 15) + RM = 15; + switch (DR) + { + case 0: + dphaseDRTable[DR][Rks] = 0; + break; + default: +#ifdef USE_SPEC_ENV_SPEED + dphaseDRTable[DR][Rks] = RATE_ADJUST(decaytable[RM][RL]); +#else + dphaseDRTable[DR][Rks] = RATE_ADJUST((RL + 4) << (RM - 1)); +#endif + break; + } + } +} + +static void makeRksTable(void) +{ + + e_int32 fnum8, block, KR; + + for (fnum8 = 0; fnum8 < 2; fnum8++) + for (block = 0; block < 8; block++) + for (KR = 0; KR < 2; KR++) + { + if (KR != 0) + rksTable[fnum8][block][KR] = (block << 1) + fnum8; + else + rksTable[fnum8][block][KR] = block >> 1; + } +} + +void OPLL_dump2patch(const e_uint8 *dump, OPLL_PATCH *patch) +{ + patch[0].AM = (dump[0] >> 7) & 1; + patch[1].AM = (dump[1] >> 7) & 1; + patch[0].PM = (dump[0] >> 6) & 1; + patch[1].PM = (dump[1] >> 6) & 1; + patch[0].EG = (dump[0] >> 5) & 1; + patch[1].EG = (dump[1] >> 5) & 1; + patch[0].KR = (dump[0] >> 4) & 1; + patch[1].KR = (dump[1] >> 4) & 1; + patch[0].ML = (dump[0]) & 15; + patch[1].ML = (dump[1]) & 15; + patch[0].KL = (dump[2] >> 6) & 3; + patch[1].KL = (dump[3] >> 6) & 3; + patch[0].TL = (dump[2]) & 63; + patch[0].FB = (dump[3]) & 7; + patch[0].WF = (dump[3] >> 3) & 1; + patch[1].WF = (dump[3] >> 4) & 1; + patch[0].AR = (dump[4] >> 4) & 15; + patch[1].AR = (dump[5] >> 4) & 15; + patch[0].DR = (dump[4]) & 15; + patch[1].DR = (dump[5]) & 15; + patch[0].SL = (dump[6] >> 4) & 15; + patch[1].SL = (dump[7] >> 4) & 15; + patch[0].RR = (dump[6]) & 15; + patch[1].RR = (dump[7]) & 15; +} + +void OPLL_getDefaultPatch(e_int32 type, e_int32 num, OPLL_PATCH *patch) +{ + OPLL_dump2patch(default_inst[type] + num * 16, patch); +} + +static void makeDefaultPatch() +{ + e_int32 i, j; + + for (i = 0; i < OPLL_TONE_NUM; i++) + for (j = 0; j < 19; j++) + OPLL_getDefaultPatch(i, j, &default_patch[i][j * 2]); +} + +void OPLL_setPatch(OPLL *opll, const e_uint8 *dump) +{ + OPLL_PATCH patch[2]; + int i; + + for (i = 0; i < 19; i++) + { + OPLL_dump2patch(dump + i * 16, patch); + memcpy(&opll->patch[i * 2 + 0], &patch[0], sizeof(OPLL_PATCH)); + memcpy(&opll->patch[i * 2 + 1], &patch[1], sizeof(OPLL_PATCH)); + } +} + +void OPLL_patch2dump(const OPLL_PATCH *patch, e_uint8 *dump) +{ + dump[0] = + (e_uint8)((patch[0].AM << 7) + (patch[0].PM << 6) + (patch[0].EG << 5) + (patch[0].KR << 4) + patch[0].ML); + dump[1] = + (e_uint8)((patch[1].AM << 7) + (patch[1].PM << 6) + (patch[1].EG << 5) + (patch[1].KR << 4) + patch[1].ML); + dump[2] = (e_uint8)((patch[0].KL << 6) + patch[0].TL); + dump[3] = (e_uint8)((patch[1].KL << 6) + (patch[1].WF << 4) + (patch[0].WF << 3) + patch[0].FB); + dump[4] = (e_uint8)((patch[0].AR << 4) + patch[0].DR); + dump[5] = (e_uint8)((patch[1].AR << 4) + patch[1].DR); + dump[6] = (e_uint8)((patch[0].SL << 4) + patch[0].RR); + dump[7] = (e_uint8)((patch[1].SL << 4) + patch[1].RR); + dump[8] = 0; + dump[9] = 0; + dump[10] = 0; + dump[11] = 0; + dump[12] = 0; + dump[13] = 0; + dump[14] = 0; + dump[15] = 0; +} + +/************************************************************ + + Calc Parameters + +************************************************************/ + +INLINE static e_uint32 calc_eg_dphase(OPLL_SLOT *slot) +{ + + switch (slot->eg_mode) + { + case ATTACK: + return dphaseARTable[slot->patch->AR][slot->rks]; + + case DECAY: + return dphaseDRTable[slot->patch->DR][slot->rks]; + + case SUSHOLD: + return 0; + + case SUSTINE: + return dphaseDRTable[slot->patch->RR][slot->rks]; + + case RELEASE: + if (slot->sustine) + return dphaseDRTable[5][slot->rks]; + else if (slot->patch->EG) + return dphaseDRTable[slot->patch->RR][slot->rks]; + else + return dphaseDRTable[7][slot->rks]; + + case SETTLE: + return dphaseDRTable[15][0]; + + case FINISH: + return 0; + + default: + return 0; + } +} + +/************************************************************* + + OPLL internal interfaces + +*************************************************************/ +#define SLOT_BD1 12 +#define SLOT_BD2 13 +#define SLOT_HH 14 +#define SLOT_SD 15 +#define SLOT_TOM 16 +#define SLOT_CYM 17 + +#define UPDATE_PG(S) (S)->dphase = dphaseTable[(S)->fnum][(S)->block][(S)->patch->ML] +#define UPDATE_TLL(S) \ + (((S)->type == 0) ? ((S)->tll = tllTable[((S)->fnum) >> 5][(S)->block][(S)->patch->TL][(S)->patch->KL]) \ + : ((S)->tll = tllTable[((S)->fnum) >> 5][(S)->block][(S)->volume][(S)->patch->KL])) +#define UPDATE_RKS(S) (S)->rks = rksTable[((S)->fnum) >> 8][(S)->block][(S)->patch->KR] +#define UPDATE_WF(S) (S)->sintbl = waveform[(S)->patch->WF] +#define UPDATE_EG(S) (S)->eg_dphase = calc_eg_dphase(S) +#define UPDATE_ALL(S) \ + UPDATE_PG(S); \ + UPDATE_TLL(S); \ + UPDATE_RKS(S); \ + UPDATE_WF(S); \ + UPDATE_EG(S) /* EG should be updated last. */ + +/* Slot key on */ +INLINE static void slotOn(OPLL_SLOT *slot) +{ + slot->eg_mode = ATTACK; + slot->eg_phase = 0; + slot->phase = 0; + UPDATE_EG(slot); +} + +/* Slot key on without reseting the phase */ +INLINE static void slotOn2(OPLL_SLOT *slot) +{ + slot->eg_mode = ATTACK; + slot->eg_phase = 0; + UPDATE_EG(slot); +} + +/* Slot key off */ +INLINE static void slotOff(OPLL_SLOT *slot) +{ + if (slot->eg_mode == ATTACK) + slot->eg_phase = + EXPAND_BITS(AR_ADJUST_TABLE[HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS)], EG_BITS, EG_DP_BITS); + slot->eg_mode = RELEASE; + UPDATE_EG(slot); +} + +/* Channel key on */ +INLINE static void keyOn(OPLL *opll, e_int32 i) +{ + if (!opll->slot_on_flag[i * 2]) + slotOn(MOD(opll, i)); + if (!opll->slot_on_flag[i * 2 + 1]) + slotOn(CAR(opll, i)); + opll->key_status[i] = 1; +} + +/* Channel key off */ +INLINE static void keyOff(OPLL *opll, e_int32 i) +{ + if (opll->slot_on_flag[i * 2 + 1]) + slotOff(CAR(opll, i)); + opll->key_status[i] = 0; +} + +INLINE static void keyOn_BD(OPLL *opll) +{ + keyOn(opll, 6); +} +INLINE static void keyOn_SD(OPLL *opll) +{ + if (!opll->slot_on_flag[SLOT_SD]) + slotOn(CAR(opll, 7)); +} +INLINE static void keyOn_TOM(OPLL *opll) +{ + if (!opll->slot_on_flag[SLOT_TOM]) + slotOn(MOD(opll, 8)); +} +INLINE static void keyOn_HH(OPLL *opll) +{ + if (!opll->slot_on_flag[SLOT_HH]) + slotOn2(MOD(opll, 7)); +} +INLINE static void keyOn_CYM(OPLL *opll) +{ + if (!opll->slot_on_flag[SLOT_CYM]) + { + slotOn2(CAR(opll, 8)); + } +} + +/* Drum key off */ +INLINE static void keyOff_BD(OPLL *opll) +{ + keyOff(opll, 6); +} +INLINE static void keyOff_SD(OPLL *opll) +{ + if (opll->slot_on_flag[SLOT_SD]) + slotOff(CAR(opll, 7)); +} +INLINE static void keyOff_TOM(OPLL *opll) +{ + if (opll->slot_on_flag[SLOT_TOM]) + slotOff(MOD(opll, 8)); +} +INLINE static void keyOff_HH(OPLL *opll) +{ + if (opll->slot_on_flag[SLOT_HH]) + slotOff(MOD(opll, 7)); +} +INLINE static void keyOff_CYM(OPLL *opll) +{ + if (opll->slot_on_flag[SLOT_CYM]) + { + CAR(opll, 8)->sustine = 1; + slotOff(CAR(opll, 8)); + } +} + +/* Change a voice */ +INLINE static void setPatch(OPLL *opll, e_int32 i, e_int32 num) +{ + opll->patch_number[i] = num; + MOD(opll, i)->patch = &opll->patch[num * 2 + 0]; + CAR(opll, i)->patch = &opll->patch[num * 2 + 1]; +} + +/* Change a rhythm voice */ +INLINE static void setSlotPatch(OPLL_SLOT *slot, OPLL_PATCH *patch) +{ + slot->patch = patch; +} + +/* Set sustine parameter */ +INLINE static void setSustine(OPLL *opll, e_int32 c, e_int32 sustine) +{ + CAR(opll, c)->sustine = sustine; + if (MOD(opll, c)->type) + MOD(opll, c)->sustine = sustine; +} + +/* Volume : 6bit ( Volume register << 2 ) */ +INLINE static void setVolume(OPLL *opll, e_int32 c, e_int32 volume) +{ + CAR(opll, c)->volume = volume; +} + +INLINE static void setSlotVolume(OPLL_SLOT *slot, e_int32 volume) +{ + slot->volume = volume; +} + +/* Set F-Number ( fnum : 9bit ) */ +INLINE static void setFnumber(OPLL *opll, e_int32 c, e_int32 fnum) +{ + CAR(opll, c)->fnum = fnum; + MOD(opll, c)->fnum = fnum; +} + +/* Set Block data (block : 3bit ) */ +INLINE static void setBlock(OPLL *opll, e_int32 c, e_int32 block) +{ + CAR(opll, c)->block = block; + MOD(opll, c)->block = block; +} + +/* Change Rhythm Mode */ +INLINE static void update_rhythm_mode(OPLL *opll) +{ + if (opll->patch_number[6] & 0x10) + { + if (!(opll->slot_on_flag[SLOT_BD2] | (opll->reg[0x0e] & 32))) + { + opll->slot[SLOT_BD1].eg_mode = FINISH; + opll->slot[SLOT_BD2].eg_mode = FINISH; + setPatch(opll, 6, opll->reg[0x36] >> 4); + } + } + else if (opll->reg[0x0e] & 32) + { + opll->patch_number[6] = 16; + opll->slot[SLOT_BD1].eg_mode = FINISH; + opll->slot[SLOT_BD2].eg_mode = FINISH; + setSlotPatch(&opll->slot[SLOT_BD1], &opll->patch[16 * 2 + 0]); + setSlotPatch(&opll->slot[SLOT_BD2], &opll->patch[16 * 2 + 1]); + } + + if (opll->patch_number[7] & 0x10) + { + if (!((opll->slot_on_flag[SLOT_HH] && opll->slot_on_flag[SLOT_SD]) | (opll->reg[0x0e] & 32))) + { + opll->slot[SLOT_HH].type = 0; + opll->slot[SLOT_HH].eg_mode = FINISH; + opll->slot[SLOT_SD].eg_mode = FINISH; + setPatch(opll, 7, opll->reg[0x37] >> 4); + } + } + else if (opll->reg[0x0e] & 32) + { + opll->patch_number[7] = 17; + opll->slot[SLOT_HH].type = 1; + opll->slot[SLOT_HH].eg_mode = FINISH; + opll->slot[SLOT_SD].eg_mode = FINISH; + setSlotPatch(&opll->slot[SLOT_HH], &opll->patch[17 * 2 + 0]); + setSlotPatch(&opll->slot[SLOT_SD], &opll->patch[17 * 2 + 1]); + } + + if (opll->patch_number[8] & 0x10) + { + if (!((opll->slot_on_flag[SLOT_CYM] && opll->slot_on_flag[SLOT_TOM]) | (opll->reg[0x0e] & 32))) + { + opll->slot[SLOT_TOM].type = 0; + opll->slot[SLOT_TOM].eg_mode = FINISH; + opll->slot[SLOT_CYM].eg_mode = FINISH; + setPatch(opll, 8, opll->reg[0x38] >> 4); + } + } + else if (opll->reg[0x0e] & 32) + { + opll->patch_number[8] = 18; + opll->slot[SLOT_TOM].type = 1; + opll->slot[SLOT_TOM].eg_mode = FINISH; + opll->slot[SLOT_CYM].eg_mode = FINISH; + setSlotPatch(&opll->slot[SLOT_TOM], &opll->patch[18 * 2 + 0]); + setSlotPatch(&opll->slot[SLOT_CYM], &opll->patch[18 * 2 + 1]); + } +} + +INLINE static void update_key_status(OPLL *opll) +{ + int ch; + + for (ch = 0; ch < 9; ch++) + opll->slot_on_flag[ch * 2] = opll->slot_on_flag[ch * 2 + 1] = (opll->reg[0x20 + ch]) & 0x10; + + if (opll->reg[0x0e] & 32) + { + opll->slot_on_flag[SLOT_BD1] |= (opll->reg[0x0e] & 0x10); + opll->slot_on_flag[SLOT_BD2] |= (opll->reg[0x0e] & 0x10); + opll->slot_on_flag[SLOT_SD] |= (opll->reg[0x0e] & 0x08); + opll->slot_on_flag[SLOT_HH] |= (opll->reg[0x0e] & 0x01); + opll->slot_on_flag[SLOT_TOM] |= (opll->reg[0x0e] & 0x04); + opll->slot_on_flag[SLOT_CYM] |= (opll->reg[0x0e] & 0x02); + } +} + +void OPLL_copyPatch(OPLL *opll, e_int32 num, OPLL_PATCH *patch) +{ + memcpy(&opll->patch[num], patch, sizeof(OPLL_PATCH)); +} + +/*********************************************************** + + Initializing + +***********************************************************/ + +static void OPLL_SLOT_reset(OPLL_SLOT *slot, int type) +{ + slot->type = type; + slot->sintbl = waveform[0]; + slot->phase = 0; + slot->dphase = 0; + slot->output[0] = 0; + slot->output[1] = 0; + slot->feedback = 0; + slot->eg_mode = FINISH; + slot->eg_phase = EG_DP_WIDTH; + slot->eg_dphase = 0; + slot->rks = 0; + slot->tll = 0; + slot->sustine = 0; + slot->fnum = 0; + slot->block = 0; + slot->volume = 0; + slot->pgout = 0; + slot->egout = 0; + slot->patch = &null_patch; +} + +static void internal_refresh(void) +{ + makeDphaseTable(); + makeDphaseARTable(); + makeDphaseDRTable(); + pm_dphase = (e_uint32)RATE_ADJUST(PM_SPEED * PM_DP_WIDTH / (clk / 72)); + am_dphase = (e_uint32)RATE_ADJUST(AM_SPEED * AM_DP_WIDTH / (clk / 72)); +} + +static void maketables(e_uint32 c, e_uint32 r) +{ + if (c != clk) + { + clk = c; + makePmTable(); + makeAmTable(); + makeDB2LinTable(); + makeAdjustTable(); + makeTllTable(); + makeRksTable(); + makeSinTable(); + makeDefaultPatch(); + } + + if (r != rate) + { + rate = r; + internal_refresh(); + } +} + +OPLL *OPLL_new(e_uint32 clk, e_uint32 rate) +{ + OPLL *opll; + e_int32 i; + + maketables(clk, rate); + + opll = (OPLL *)calloc(sizeof(OPLL), 1); + if (opll == NULL) + return NULL; + + for (i = 0; i < 19 * 2; i++) + memcpy(&opll->patch[i], &null_patch, sizeof(OPLL_PATCH)); + + opll->mask = 0; + + OPLL_reset(opll); + OPLL_reset_patch(opll, 0); + + return opll; +} + +void OPLL_delete(OPLL *opll) +{ + free(opll); +} + +/* Reset patch datas by system default. */ +void OPLL_reset_patch(OPLL *opll, e_int32 type) +{ + e_int32 i; + + for (i = 0; i < 19 * 2; i++) + OPLL_copyPatch(opll, i, &default_patch[type % OPLL_TONE_NUM][i]); +} + +/* Reset whole of OPLL except patch datas. */ +void OPLL_reset(OPLL *opll) +{ + e_int32 i; + + if (!opll) + return; + + opll->adr = 0; + opll->out = 0; + + opll->pm_phase = 0; + opll->am_phase = 0; + + opll->noise_seed = 0xffff; + opll->mask = 0; + + for (i = 0; i < 18; i++) + OPLL_SLOT_reset(&opll->slot[i], i % 2); + + for (i = 0; i < 9; i++) + { + opll->key_status[i] = 0; + setPatch(opll, i, 0); + } + + for (i = 0; i < 0x40; i++) + OPLL_writeReg(opll, i, 0); + +#ifndef EMU2413_COMPACTION + opll->realstep = (e_uint32)((1 << 31) / rate); + opll->opllstep = (e_uint32)((1 << 31) / (clk / 72)); + opll->oplltime = 0; + for (i = 0; i < 14; i++) + opll->pan[i] = 3; + opll->sprev[0] = opll->sprev[1] = 0; + opll->snext[0] = opll->snext[1] = 0; +#endif +} + +/* Force Refresh (When external program changes some parameters). */ +void OPLL_forceRefresh(OPLL *opll) +{ + e_int32 i; + + if (opll == NULL) + return; + + for (i = 0; i < 9; i++) + setPatch(opll, i, opll->patch_number[i]); + + for (i = 0; i < 18; i++) + { + UPDATE_PG(&opll->slot[i]); + UPDATE_RKS(&opll->slot[i]); + UPDATE_TLL(&opll->slot[i]); + UPDATE_WF(&opll->slot[i]); + UPDATE_EG(&opll->slot[i]); + } +} + +void OPLL_set_rate(OPLL *opll, e_uint32 r) +{ + if (opll->quality) + rate = 49716; + else + rate = r; + internal_refresh(); + rate = r; +} + +void OPLL_set_quality(OPLL *opll, e_uint32 q) +{ + opll->quality = q; + OPLL_set_rate(opll, rate); +} + +/********************************************************* + + Generate wave data + +*********************************************************/ +/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 2PI). */ +#if (SLOT_AMP_BITS - PG_BITS) > 0 +#define wave2_2pi(e) ((e) >> (SLOT_AMP_BITS - PG_BITS)) +#else +#define wave2_2pi(e) ((e) << (PG_BITS - SLOT_AMP_BITS)) +#endif + +/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 4PI). */ +#if (SLOT_AMP_BITS - PG_BITS - 1) == 0 +#define wave2_4pi(e) (e) +#elif (SLOT_AMP_BITS - PG_BITS - 1) > 0 +#define wave2_4pi(e) ((e) >> (SLOT_AMP_BITS - PG_BITS - 1)) +#else +#define wave2_4pi(e) ((e) << (1 + PG_BITS - SLOT_AMP_BITS)) +#endif + +/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI). */ +#if (SLOT_AMP_BITS - PG_BITS - 2) == 0 +#define wave2_8pi(e) (e) +#elif (SLOT_AMP_BITS - PG_BITS - 2) > 0 +#define wave2_8pi(e) ((e) >> (SLOT_AMP_BITS - PG_BITS - 2)) +#else +#define wave2_8pi(e) ((e) << (2 + PG_BITS - SLOT_AMP_BITS)) +#endif + +/* Update AM, PM unit */ +static void update_ampm(OPLL *opll) +{ + opll->pm_phase = (opll->pm_phase + pm_dphase) & (PM_DP_WIDTH - 1); + opll->am_phase = (opll->am_phase + am_dphase) & (AM_DP_WIDTH - 1); + opll->lfo_am = amtable[HIGHBITS(opll->am_phase, AM_DP_BITS - AM_PG_BITS)]; + opll->lfo_pm = pmtable[HIGHBITS(opll->pm_phase, PM_DP_BITS - PM_PG_BITS)]; +} + +/* PG */ +INLINE static void calc_phase(OPLL_SLOT *slot, e_int32 lfo) +{ + if (slot->patch->PM) + slot->phase += (slot->dphase * lfo) >> PM_AMP_BITS; + else + slot->phase += slot->dphase; + + slot->phase &= (DP_WIDTH - 1); + + slot->pgout = HIGHBITS(slot->phase, DP_BASE_BITS); +} + +/* Update Noise unit */ +static void update_noise(OPLL *opll) +{ + if (opll->noise_seed & 1) + opll->noise_seed ^= 0x8003020; + opll->noise_seed >>= 1; +} + +/* EG */ +static void calc_envelope(OPLL_SLOT *slot, e_int32 lfo) +{ +#define S2E(x) (SL2EG((e_int32)(x / SL_STEP)) << (EG_DP_BITS - EG_BITS)) + + static e_uint32 SL[16] = {S2E(0.0), S2E(3.0), S2E(6.0), S2E(9.0), S2E(12.0), S2E(15.0), S2E(18.0), S2E(21.0), + S2E(24.0), S2E(27.0), S2E(30.0), S2E(33.0), S2E(36.0), S2E(39.0), S2E(42.0), S2E(48.0)}; + + e_uint32 egout; + + switch (slot->eg_mode) + { + case ATTACK: + egout = AR_ADJUST_TABLE[HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS)]; + slot->eg_phase += slot->eg_dphase; + if ((EG_DP_WIDTH & slot->eg_phase) || (slot->patch->AR == 15)) + { + egout = 0; + slot->eg_phase = 0; + slot->eg_mode = DECAY; + UPDATE_EG(slot); + } + break; + + case DECAY: + egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS); + slot->eg_phase += slot->eg_dphase; + if (slot->eg_phase >= SL[slot->patch->SL]) + { + if (slot->patch->EG) + { + slot->eg_phase = SL[slot->patch->SL]; + slot->eg_mode = SUSHOLD; + UPDATE_EG(slot); + } + else + { + slot->eg_phase = SL[slot->patch->SL]; + slot->eg_mode = SUSTINE; + UPDATE_EG(slot); + } + } + break; + + case SUSHOLD: + egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS); + if (slot->patch->EG == 0) + { + slot->eg_mode = SUSTINE; + UPDATE_EG(slot); + } + break; + + case SUSTINE: + case RELEASE: + egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS); + slot->eg_phase += slot->eg_dphase; + if (egout >= (1 << EG_BITS)) + { + slot->eg_mode = FINISH; + egout = (1 << EG_BITS) - 1; + } + break; + + case SETTLE: + egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS); + slot->eg_phase += slot->eg_dphase; + if (egout >= (1 << EG_BITS)) + { + slot->eg_mode = ATTACK; + egout = (1 << EG_BITS) - 1; + UPDATE_EG(slot); + } + break; + + case FINISH: + egout = (1 << EG_BITS) - 1; + break; + + default: + egout = (1 << EG_BITS) - 1; + break; + } + + if (slot->patch->AM) + egout = EG2DB(egout + slot->tll) + lfo; + else + egout = EG2DB(egout + slot->tll); + + if (egout >= DB_MUTE) + egout = DB_MUTE - 1; + + slot->egout = egout | 3; +} + +/* CARRIOR */ +INLINE static e_int32 calc_slot_car(OPLL_SLOT *slot, e_int32 fm) +{ + if (slot->egout >= (DB_MUTE - 1)) + { + slot->output[0] = 0; + } + else + { + slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout + wave2_8pi(fm)) & (PG_WIDTH - 1)] + slot->egout]; + } + + slot->output[1] = (slot->output[1] + slot->output[0]) >> 1; + return slot->output[1]; +} + +/* MODULATOR */ +INLINE static e_int32 calc_slot_mod(OPLL_SLOT *slot) +{ + e_int32 fm; + + slot->output[1] = slot->output[0]; + + if (slot->egout >= (DB_MUTE - 1)) + { + slot->output[0] = 0; + } + else if (slot->patch->FB != 0) + { + fm = wave2_4pi(slot->feedback) >> (7 - slot->patch->FB); + slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout + fm) & (PG_WIDTH - 1)] + slot->egout]; + } + else + { + slot->output[0] = DB2LIN_TABLE[slot->sintbl[slot->pgout] + slot->egout]; + } + + slot->feedback = (slot->output[1] + slot->output[0]) >> 1; + + return slot->feedback; +} + +/* TOM */ +INLINE static e_int32 calc_slot_tom(OPLL_SLOT *slot) +{ + if (slot->egout >= (DB_MUTE - 1)) + return 0; + + return DB2LIN_TABLE[slot->sintbl[slot->pgout] + slot->egout]; +} + +/* SNARE */ +INLINE static e_int32 calc_slot_snare(OPLL_SLOT *slot, e_uint32 noise) +{ + if (slot->egout >= (DB_MUTE - 1)) + return 0; + + if (BIT(slot->pgout, 7)) + return DB2LIN_TABLE[(noise ? DB_POS(0.0) : DB_POS(15.0)) + slot->egout]; + else + return DB2LIN_TABLE[(noise ? DB_NEG(0.0) : DB_NEG(15.0)) + slot->egout]; +} + +/* + TOP-CYM + */ +INLINE static e_int32 calc_slot_cym(OPLL_SLOT *slot, e_uint32 pgout_hh) +{ + e_uint32 dbout; + + if (slot->egout >= (DB_MUTE - 1)) + return 0; + else if ( + /* the same as fmopl.c */ + ((BIT(pgout_hh, PG_BITS - 8) ^ BIT(pgout_hh, PG_BITS - 1)) | BIT(pgout_hh, PG_BITS - 7)) ^ + /* different from fmopl.c */ + (BIT(slot->pgout, PG_BITS - 7) & !BIT(slot->pgout, PG_BITS - 5))) + dbout = DB_NEG(3.0); + else + dbout = DB_POS(3.0); + + return DB2LIN_TABLE[dbout + slot->egout]; +} + +/* + HI-HAT +*/ +INLINE static e_int32 calc_slot_hat(OPLL_SLOT *slot, e_int32 pgout_cym, e_uint32 noise) +{ + e_uint32 dbout; + + if (slot->egout >= (DB_MUTE - 1)) + return 0; + else if ( + /* the same as fmopl.c */ + ((BIT(slot->pgout, PG_BITS - 8) ^ BIT(slot->pgout, PG_BITS - 1)) | BIT(slot->pgout, PG_BITS - 7)) ^ + /* different from fmopl.c */ + (BIT(pgout_cym, PG_BITS - 7) & !BIT(pgout_cym, PG_BITS - 5))) + { + if (noise) + dbout = DB_NEG(12.0); + else + dbout = DB_NEG(24.0); + } + else + { + if (noise) + dbout = DB_POS(12.0); + else + dbout = DB_POS(24.0); + } + + return DB2LIN_TABLE[dbout + slot->egout]; +} + +static e_int16 calc(OPLL *opll) +{ + e_int32 inst = 0, perc = 0, out = 0; + e_int32 i; + + update_ampm(opll); + update_noise(opll); + + for (i = 0; i < 18; i++) + { + calc_phase(&opll->slot[i], opll->lfo_pm); + calc_envelope(&opll->slot[i], opll->lfo_am); + } + + for (i = 0; i < 6; i++) + if (!(opll->mask & OPLL_MASK_CH(i)) && (CAR(opll, i)->eg_mode != FINISH)) + inst += calc_slot_car(CAR(opll, i), calc_slot_mod(MOD(opll, i))); + + /* CH6 */ + if (opll->patch_number[6] <= 15) + { + if (!(opll->mask & OPLL_MASK_CH(6)) && (CAR(opll, 6)->eg_mode != FINISH)) + inst += calc_slot_car(CAR(opll, 6), calc_slot_mod(MOD(opll, 6))); + } + else + { + if (!(opll->mask & OPLL_MASK_BD) && (CAR(opll, 6)->eg_mode != FINISH)) + perc += calc_slot_car(CAR(opll, 6), calc_slot_mod(MOD(opll, 6))); + } + + /* CH7 */ + if (opll->patch_number[7] <= 15) + { + if (!(opll->mask & OPLL_MASK_CH(7)) && (CAR(opll, 7)->eg_mode != FINISH)) + inst += calc_slot_car(CAR(opll, 7), calc_slot_mod(MOD(opll, 7))); + } + else + { + if (!(opll->mask & OPLL_MASK_HH) && (MOD(opll, 7)->eg_mode != FINISH)) + perc += calc_slot_hat(MOD(opll, 7), CAR(opll, 8)->pgout, opll->noise_seed & 1); + if (!(opll->mask & OPLL_MASK_SD) && (CAR(opll, 7)->eg_mode != FINISH)) + perc -= calc_slot_snare(CAR(opll, 7), opll->noise_seed & 1); + } + + /* CH8 */ + if (opll->patch_number[8] <= 15) + { + if (!(opll->mask & OPLL_MASK_CH(8)) && (CAR(opll, 8)->eg_mode != FINISH)) + inst += calc_slot_car(CAR(opll, 8), calc_slot_mod(MOD(opll, 8))); + } + else + { + if (!(opll->mask & OPLL_MASK_TOM) && (MOD(opll, 8)->eg_mode != FINISH)) + perc += calc_slot_tom(MOD(opll, 8)); + if (!(opll->mask & OPLL_MASK_CYM) && (CAR(opll, 8)->eg_mode != FINISH)) + perc -= calc_slot_cym(CAR(opll, 8), MOD(opll, 7)->pgout); + } + + out = inst + (perc << 1); + return (e_int16)out << 3; +} + +#ifdef EMU2413_COMPACTION +e_int16 OPLL_calc(OPLL *opll) +{ + return calc(opll); +} +#else +e_int16 OPLL_calc(OPLL *opll) +{ + if (!opll->quality) + return calc(opll); + + while (opll->realstep > opll->oplltime) + { + opll->oplltime += opll->opllstep; + opll->prev = opll->next; + opll->next = calc(opll); + } + + opll->oplltime -= opll->realstep; + opll->out = + (e_int16)(((double)opll->next * (opll->opllstep - opll->oplltime) + (double)opll->prev * opll->oplltime) / + opll->opllstep); + + return (e_int16)opll->out; +} +#endif + +e_uint32 OPLL_setMask(OPLL *opll, e_uint32 mask) +{ + e_uint32 ret; + + if (opll) + { + ret = opll->mask; + opll->mask = mask; + return ret; + } + else + return 0; +} + +e_uint32 OPLL_toggleMask(OPLL *opll, e_uint32 mask) +{ + e_uint32 ret; + + if (opll) + { + ret = opll->mask; + opll->mask ^= mask; + return ret; + } + else + return 0; +} + +/**************************************************** + + I/O Ctrl + +*****************************************************/ + +void OPLL_writeReg(OPLL *opll, e_uint32 reg, e_uint32 data) +{ + + e_int32 i, v, ch; + + data = data & 0xff; + reg = reg & 0x3f; + opll->reg[reg] = (e_uint8)data; + + switch (reg) + { + case 0x00: + opll->patch[0].AM = (data >> 7) & 1; + opll->patch[0].PM = (data >> 6) & 1; + opll->patch[0].EG = (data >> 5) & 1; + opll->patch[0].KR = (data >> 4) & 1; + opll->patch[0].ML = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_PG(MOD(opll, i)); + UPDATE_RKS(MOD(opll, i)); + UPDATE_EG(MOD(opll, i)); + } + } + break; + + case 0x01: + opll->patch[1].AM = (data >> 7) & 1; + opll->patch[1].PM = (data >> 6) & 1; + opll->patch[1].EG = (data >> 5) & 1; + opll->patch[1].KR = (data >> 4) & 1; + opll->patch[1].ML = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_PG(CAR(opll, i)); + UPDATE_RKS(CAR(opll, i)); + UPDATE_EG(CAR(opll, i)); + } + } + break; + + case 0x02: + opll->patch[0].KL = (data >> 6) & 3; + opll->patch[0].TL = (data) & 63; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_TLL(MOD(opll, i)); + } + } + break; + + case 0x03: + opll->patch[1].KL = (data >> 6) & 3; + opll->patch[1].WF = (data >> 4) & 1; + opll->patch[0].WF = (data >> 3) & 1; + opll->patch[0].FB = (data) & 7; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_WF(MOD(opll, i)); + UPDATE_WF(CAR(opll, i)); + } + } + break; + + case 0x04: + opll->patch[0].AR = (data >> 4) & 15; + opll->patch[0].DR = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_EG(MOD(opll, i)); + } + } + break; + + case 0x05: + opll->patch[1].AR = (data >> 4) & 15; + opll->patch[1].DR = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_EG(CAR(opll, i)); + } + } + break; + + case 0x06: + opll->patch[0].SL = (data >> 4) & 15; + opll->patch[0].RR = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_EG(MOD(opll, i)); + } + } + break; + + case 0x07: + opll->patch[1].SL = (data >> 4) & 15; + opll->patch[1].RR = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_EG(CAR(opll, i)); + } + } + break; + + case 0x0e: + update_rhythm_mode(opll); + if (data & 32) + { + if (data & 0x10) + keyOn_BD(opll); + else + keyOff_BD(opll); + if (data & 0x8) + keyOn_SD(opll); + else + keyOff_SD(opll); + if (data & 0x4) + keyOn_TOM(opll); + else + keyOff_TOM(opll); + if (data & 0x2) + keyOn_CYM(opll); + else + keyOff_CYM(opll); + if (data & 0x1) + keyOn_HH(opll); + else + keyOff_HH(opll); + } + update_key_status(opll); + + UPDATE_ALL(MOD(opll, 6)); + UPDATE_ALL(CAR(opll, 6)); + UPDATE_ALL(MOD(opll, 7)); + UPDATE_ALL(CAR(opll, 7)); + UPDATE_ALL(MOD(opll, 8)); + UPDATE_ALL(CAR(opll, 8)); + + break; + + case 0x0f: + break; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + ch = reg - 0x10; + setFnumber(opll, ch, data + ((opll->reg[0x20 + ch] & 1) << 8)); + UPDATE_ALL(MOD(opll, ch)); + UPDATE_ALL(CAR(opll, ch)); + break; + + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + ch = reg - 0x20; + setFnumber(opll, ch, ((data & 1) << 8) + opll->reg[0x10 + ch]); + setBlock(opll, ch, (data >> 1) & 7); + setSustine(opll, ch, (data >> 5) & 1); + if (data & 0x10) + keyOn(opll, ch); + else + keyOff(opll, ch); + UPDATE_ALL(MOD(opll, ch)); + UPDATE_ALL(CAR(opll, ch)); + update_key_status(opll); + update_rhythm_mode(opll); + break; + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + i = (data >> 4) & 15; + v = data & 15; + if ((opll->reg[0x0e] & 32) && (reg >= 0x36)) + { + switch (reg) + { + case 0x37: + setSlotVolume(MOD(opll, 7), i << 2); + break; + case 0x38: + setSlotVolume(MOD(opll, 8), i << 2); + break; + default: + break; + } + } + else + { + setPatch(opll, reg - 0x30, i); + } + setVolume(opll, reg - 0x30, v << 2); + UPDATE_ALL(MOD(opll, reg - 0x30)); + UPDATE_ALL(CAR(opll, reg - 0x30)); + break; + + default: + break; + } +} + +void OPLL_writeIO(OPLL *opll, e_uint32 adr, e_uint32 val) +{ + if (adr & 1) + OPLL_writeReg(opll, opll->adr, val); + else + opll->adr = val; +} + +#ifndef EMU2413_COMPACTION +/* STEREO MODE (OPT) */ +void OPLL_set_pan(OPLL *opll, e_uint32 ch, e_uint32 pan) +{ + opll->pan[ch & 15] = pan & 3; +} + +static void calc_stereo(OPLL *opll, e_int32 out[2]) +{ + e_int32 b[4] = {0, 0, 0, 0}; /* Ignore, Right, Left, Center */ + e_int32 r[4] = {0, 0, 0, 0}; /* Ignore, Right, Left, Center */ + e_int32 i; + + update_ampm(opll); + update_noise(opll); + + for (i = 0; i < 18; i++) + { + calc_phase(&opll->slot[i], opll->lfo_pm); + calc_envelope(&opll->slot[i], opll->lfo_am); + } + + for (i = 0; i < 6; i++) + if (!(opll->mask & OPLL_MASK_CH(i)) && (CAR(opll, i)->eg_mode != FINISH)) + b[opll->pan[i]] += calc_slot_car(CAR(opll, i), calc_slot_mod(MOD(opll, i))); + + if (opll->patch_number[6] <= 15) + { + if (!(opll->mask & OPLL_MASK_CH(6)) && (CAR(opll, 6)->eg_mode != FINISH)) + b[opll->pan[6]] += calc_slot_car(CAR(opll, 6), calc_slot_mod(MOD(opll, 6))); + } + else + { + if (!(opll->mask & OPLL_MASK_BD) && (CAR(opll, 6)->eg_mode != FINISH)) + r[opll->pan[9]] += calc_slot_car(CAR(opll, 6), calc_slot_mod(MOD(opll, 6))); + } + + if (opll->patch_number[7] <= 15) + { + if (!(opll->mask & OPLL_MASK_CH(7)) && (CAR(opll, 7)->eg_mode != FINISH)) + b[opll->pan[7]] += calc_slot_car(CAR(opll, 7), calc_slot_mod(MOD(opll, 7))); + } + else + { + if (!(opll->mask & OPLL_MASK_HH) && (MOD(opll, 7)->eg_mode != FINISH)) + r[opll->pan[10]] += calc_slot_hat(MOD(opll, 7), CAR(opll, 8)->pgout, opll->noise_seed & 1); + if (!(opll->mask & OPLL_MASK_SD) && (CAR(opll, 7)->eg_mode != FINISH)) + r[opll->pan[11]] -= calc_slot_snare(CAR(opll, 7), opll->noise_seed & 1); + } + + if (opll->patch_number[8] <= 15) + { + if (!(opll->mask & OPLL_MASK_CH(8)) && (CAR(opll, 8)->eg_mode != FINISH)) + b[opll->pan[8]] += calc_slot_car(CAR(opll, 8), calc_slot_mod(MOD(opll, 8))); + } + else + { + if (!(opll->mask & OPLL_MASK_TOM) && (MOD(opll, 8)->eg_mode != FINISH)) + r[opll->pan[12]] += calc_slot_tom(MOD(opll, 8)); + if (!(opll->mask & OPLL_MASK_CYM) && (CAR(opll, 8)->eg_mode != FINISH)) + r[opll->pan[13]] -= calc_slot_cym(CAR(opll, 8), MOD(opll, 7)->pgout); + } + + out[1] = (b[1] + b[3] + ((r[1] + r[3]) << 1)) << 3; + out[0] = (b[2] + b[3] + ((r[2] + r[3]) << 1)) << 3; +} + +void OPLL_calc_stereo(OPLL *opll, e_int32 out[2]) +{ + if (!opll->quality) + { + calc_stereo(opll, out); + return; + } + + while (opll->realstep > opll->oplltime) + { + opll->oplltime += opll->opllstep; + opll->sprev[0] = opll->snext[0]; + opll->sprev[1] = opll->snext[1]; + calc_stereo(opll, opll->snext); + } + + opll->oplltime -= opll->realstep; + out[0] = (e_int16)(((double)opll->snext[0] * (opll->opllstep - opll->oplltime) + + (double)opll->sprev[0] * opll->oplltime) / + opll->opllstep); + out[1] = (e_int16)(((double)opll->snext[1] * (opll->opllstep - opll->oplltime) + + (double)opll->sprev[1] * opll->oplltime) / + opll->opllstep); +} +#endif /* EMU2413_COMPACTION */ diff --git a/libraries/libemidi/source/device/emu2413.h b/libraries/libemidi/source/device/emu2413.h new file mode 100644 index 000000000..2ffae7ad7 --- /dev/null +++ b/libraries/libemidi/source/device/emu2413.h @@ -0,0 +1,161 @@ +#ifndef _EMU2413_H_ +#define _EMU2413_H_ + +#include "emutypes.h" + +#ifdef EMU2413_DLL_EXPORTS +#define EMU2413_API __declspec(dllexport) +#elif defined(EMU2413_DLL_IMPORTS) +#define EMU2413_API __declspec(dllimport) +#else +#define EMU2413_API +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define PI 3.14159265358979323846 + + enum OPLL_TONE_ENUM + { + OPLL_2413_TONE = 0, + OPLL_VRC7_TONE = 1, + OPLL_281B_TONE = 2 + }; + + /* voice data */ + typedef struct __OPLL_PATCH + { + e_uint32 TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, WF; + } OPLL_PATCH; + + /* slot */ + typedef struct __OPLL_SLOT + { + + OPLL_PATCH *patch; + + e_int32 type; /* 0 : modulator 1 : carrier */ + + /* OUTPUT */ + e_int32 feedback; + e_int32 output[2]; /* Output value of slot */ + + /* for Phase Generator (PG) */ + e_uint16 *sintbl; /* Wavetable */ + e_uint32 phase; /* Phase */ + e_uint32 dphase; /* Phase increment amount */ + e_uint32 pgout; /* output */ + + /* for Envelope Generator (EG) */ + e_int32 fnum; /* F-Number */ + e_int32 block; /* Block */ + e_int32 volume; /* Current volume */ + e_int32 sustine; /* Sustine 1 = ON, 0 = OFF */ + e_uint32 tll; /* Total Level + Key scale level*/ + e_uint32 rks; /* Key scale offset (Rks) */ + e_int32 eg_mode; /* Current state */ + e_uint32 eg_phase; /* Phase */ + e_uint32 eg_dphase; /* Phase increment amount */ + e_uint32 egout; /* output */ + + } OPLL_SLOT; + +/* Mask */ +#define OPLL_MASK_CH(x) (1 << (x)) +#define OPLL_MASK_HH (1 << (9)) +#define OPLL_MASK_CYM (1 << (10)) +#define OPLL_MASK_TOM (1 << (11)) +#define OPLL_MASK_SD (1 << (12)) +#define OPLL_MASK_BD (1 << (13)) +#define OPLL_MASK_RHYTHM (OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD) + + /* opll */ + typedef struct __OPLL + { + + e_uint32 adr; + e_int32 out; + +#ifndef EMU2413_COMPACTION + e_uint32 realstep; + e_uint32 oplltime; + e_uint32 opllstep; + e_int32 prev, next; + e_int32 sprev[2], snext[2]; + e_uint32 pan[16]; +#endif + + /* Register */ + e_uint8 reg[0x40]; + e_int32 slot_on_flag[18]; + + /* Pitch Modulator */ + e_uint32 pm_phase; + e_int32 lfo_pm; + + /* Amp Modulator */ + e_int32 am_phase; + e_int32 lfo_am; + + e_uint32 quality; + + /* Noise Generator */ + e_uint32 noise_seed; + + /* Channel Data */ + e_int32 patch_number[9]; + e_int32 key_status[9]; + + /* Slot */ + OPLL_SLOT slot[18]; + + /* Voice Data */ + OPLL_PATCH patch[19 * 2]; + e_int32 patch_update[2]; /* flag for check patch update */ + + e_uint32 mask; + + } OPLL; + + /* Create Object */ + EMU2413_API OPLL *OPLL_new(e_uint32 clk, e_uint32 rate); + EMU2413_API void OPLL_delete(OPLL *); + + /* Setup */ + EMU2413_API void OPLL_reset(OPLL *); + EMU2413_API void OPLL_reset_patch(OPLL *, e_int32); + EMU2413_API void OPLL_set_rate(OPLL *opll, e_uint32 r); + EMU2413_API void OPLL_set_quality(OPLL *opll, e_uint32 q); + EMU2413_API void OPLL_set_pan(OPLL *, e_uint32 ch, e_uint32 pan); + + /* Port/Register access */ + EMU2413_API void OPLL_writeIO(OPLL *, e_uint32 reg, e_uint32 val); + EMU2413_API void OPLL_writeReg(OPLL *, e_uint32 reg, e_uint32 val); + + /* Synthsize */ + EMU2413_API e_int16 OPLL_calc(OPLL *); + EMU2413_API void OPLL_calc_stereo(OPLL *, e_int32 out[2]); + + /* Misc */ + EMU2413_API void OPLL_setPatch(OPLL *, const e_uint8 *dump); + EMU2413_API void OPLL_copyPatch(OPLL *, e_int32, OPLL_PATCH *); + EMU2413_API void OPLL_forceRefresh(OPLL *); + /* Utility */ + EMU2413_API void OPLL_dump2patch(const e_uint8 *dump, OPLL_PATCH *patch); + EMU2413_API void OPLL_patch2dump(const OPLL_PATCH *patch, e_uint8 *dump); + EMU2413_API void OPLL_getDefaultPatch(e_int32 type, e_int32 num, OPLL_PATCH *); + + /* Channel Mask */ + EMU2413_API e_uint32 OPLL_setMask(OPLL *, e_uint32 mask); + EMU2413_API e_uint32 OPLL_toggleMask(OPLL *, e_uint32 mask); + +#define dump2patch OPLL_dump2patch + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libraries/libemidi/source/device/emutypes.h b/libraries/libemidi/source/device/emutypes.h new file mode 100644 index 000000000..11e3c8d18 --- /dev/null +++ b/libraries/libemidi/source/device/emutypes.h @@ -0,0 +1,42 @@ +#ifndef _EMUTYPES_H_ +#define _EMUTYPES_H_ + +#if defined(_MSC_VER) +#define INLINE __forceinline +#elif defined(__GNUC__) +#define INLINE __inline__ +#elif defined(_MWERKS_) +#define INLINE inline +#else +#define INLINE +#endif + +#if defined(EMU_DLL_IMPORTS) +#define EMU2149_DLL_IMPORTS +#define EMU2212_DLL_IMPORTS +#define EMU2413_DLL_IMPORTS +#define EMU8950_DLL_IMPORTS +#define EMU76489_DLL_IMPORTS +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef unsigned int e_uint; + typedef signed int e_int; + + typedef unsigned char e_uint8; + typedef signed char e_int8; + + typedef unsigned short e_uint16; + typedef signed short e_int16; + + typedef unsigned int e_uint32; + typedef signed int e_int32; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libraries/libemidi/source/device/vrc7tone.h b/libraries/libemidi/source/device/vrc7tone.h new file mode 100644 index 000000000..4aadfb1e0 --- /dev/null +++ b/libraries/libemidi/source/device/vrc7tone.h @@ -0,0 +1,17 @@ +/* VRC7 TONES by okazaki@angel.ne.jp */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x01, 0x09, 0x0e, + 0x94, 0x90, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x41, 0x0f, 0x0d, 0xce, 0xd3, 0x43, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x1b, 0x06, 0xff, 0xd2, 0x00, 0x32, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x61, 0x1b, 0x07, 0xaf, 0x63, 0x20, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x21, 0x1e, 0x06, 0xf0, 0x76, 0x08, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x21, 0x15, 0x00, 0x93, 0x94, 0x20, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x61, 0x1c, + 0x07, 0x82, 0x81, 0x10, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x21, 0x20, 0x1f, 0xc0, 0x71, + 0x07, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x31, 0x26, 0x05, 0x64, 0x41, 0x18, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x21, 0x28, 0x07, 0xff, 0x83, 0x02, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x97, 0x81, 0x25, 0x07, 0xcf, 0xc8, 0x02, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x21, 0x21, 0x54, 0x0f, 0x80, 0x7f, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x56, 0x03, 0xd3, 0xb2, 0x43, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x21, 0x0c, 0x03, 0x82, + 0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x0c, 0x03, 0xd4, 0xd3, 0x40, 0x84, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x21, 0x14, 0x00, 0xee, 0xf8, 0xff, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x31, 0x00, 0x00, 0xf8, 0xf7, 0xf8, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x25, 0x11, 0x00, 0x00, 0xf8, 0xfa, 0xf8, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/source_files/edge/CMakeLists.txt b/source_files/edge/CMakeLists.txt index a656f141b..7b1da5c29 100644 --- a/source_files/edge/CMakeLists.txt +++ b/source_files/edge/CMakeLists.txt @@ -85,6 +85,7 @@ set (EDGE_SOURCE_FILES s_mp3.cc s_music.cc s_ogg.cc + s_emidi.cc s_fluid.cc s_fmm.cc s_m4p.cc @@ -143,6 +144,7 @@ set (EDGE_LINK_LIBRARIES fluidlite fmmidi HandmadeMath + libemidi libRAD libvwad lua diff --git a/source_files/edge/m_option.cc b/source_files/edge/m_option.cc index 62f614082..1e2f55c6f 100644 --- a/source_files/edge/m_option.cc +++ b/source_files/edge/m_option.cc @@ -107,6 +107,7 @@ #include "r_wipe.h" #include "s_blit.h" #include "s_cache.h" +#include "s_emidi.h" #include "s_fluid.h" #include "s_fmm.h" #include "s_music.h" @@ -527,7 +528,7 @@ static OptionMenuItem soundoptions[] = { {kOptionMenuItemTypePlain, "", nullptr, 0, nullptr, nullptr, nullptr}, {kOptionMenuItemTypeSwitch, "Stereo", "Off/On/Swapped", 3, &var_sound_stereo, nullptr, "NeedRestart"}, {kOptionMenuItemTypePlain, "", nullptr, 0, nullptr, nullptr, nullptr}, - {kOptionMenuItemTypeSwitch, "MIDI Player", "Fluidlite/Opal/FMMIDI", 3, &var_midi_player, OptionMenuChangeMidiPlayer, + {kOptionMenuItemTypeSwitch, "MIDI Player", "Fluidlite/Opal/FMMIDI/Emu de MIDI", 4, &var_midi_player, OptionMenuChangeMidiPlayer, nullptr}, {kOptionMenuItemTypeFunction, "Fluidlite Soundfont", nullptr, 0, nullptr, OptionMenuChangeSoundfont, nullptr}, {kOptionMenuItemTypeBoolean, "PC Speaker Mode", YesNo, 2, &pc_speaker_mode, OptionMenuChangePCSpeakerMode, @@ -2041,8 +2042,10 @@ static void OptionMenuChangeMidiPlayer(int key_pressed, ConsoleVariable *console RestartOpal(); else if (var_midi_player == 0) RestartFluid(); - else + else if (var_midi_player == 2) RestartFMM(); + else + RestartEMIDI(); } // diff --git a/source_files/edge/s_emidi.cc b/source_files/edge/s_emidi.cc new file mode 100644 index 000000000..a58a3e69e --- /dev/null +++ b/source_files/edge/s_emidi.cc @@ -0,0 +1,343 @@ +//---------------------------------------------------------------------------- +// EDGE Emu de MIDI Music Player +//---------------------------------------------------------------------------- +// +// Copyright (c) 2024 The EDGE Team. +// +// 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 3 +// 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. +// +//---------------------------------------------------------------------------- + +#include "s_emidi.h" + +#include +#include + +#include "CSMFPlay.hpp" +#include "dm_state.h" +#include "epi_file.h" +#include "epi_filesystem.h" +#include "epi_str_util.h" +#include "i_system.h" +#include "m_misc.h" +// clang-format off +#define MidiFraction EMIDIFraction +#define MidiSequencer EMIDISequencer +typedef struct MidiRealTimeInterface EMIDIInterface; +#include "midi_sequencer_impl.hpp" +// clang-format on +#include "s_blit.h" + +extern int sound_device_frequency; + +// Should only be invoked when switching MIDI players +void RestartEMIDI(void) +{ + int old_entry = entry_playing; + + StopMusic(); + + ChangeMusic(old_entry, true); // Restart track that was kPlaying when switched + + return; // OK! +} + +class EMIDIPlayer : public AbstractMusicPlayer +{ + private: + private: + enum status_ + { + kNotLoaded, + kPlaying, + kPaused, + kStopped + }; + + int status_; + bool looping_; + + EMIDIInterface *emidi_interface_; + + public: + EMIDIPlayer(uint8_t *data, int length, bool looping) : status_(kNotLoaded), looping_(looping) + { + SequencerInit(); + } + + ~EMIDIPlayer() + { + Close(); + } + + public: + EMIDISequencer *emidi_sequencer_; + dsa::CSMFPlay *emidi_synth_; + + static void rtNoteOn(void *userdata, uint8_t channel, uint8_t note, uint8_t velocity) + { + EMIDIPlayer *player = (EMIDIPlayer *)userdata; + player->emidi_synth_->SendMIDIMessage({dsa::CMIDIMsg::NOTE_ON, channel, note, velocity}); + } + + static void rtNoteOff(void *userdata, uint8_t channel, uint8_t note) + { + EMIDIPlayer *player = (EMIDIPlayer *)userdata; + player->emidi_synth_->SendMIDIMessage({dsa::CMIDIMsg::NOTE_OFF, channel, note}); + } + + static void rtNoteAfterTouch(void *userdata, uint8_t channel, uint8_t note, uint8_t atVal) + { + (void)userdata; + (void)channel; + (void)note; + (void)atVal; + } + + static void rtChannelAfterTouch(void *userdata, uint8_t channel, uint8_t atVal) + { + EMIDIPlayer *player = (EMIDIPlayer *)userdata; + player->emidi_synth_->SendMIDIMessage({dsa::CMIDIMsg::CHANNEL_PRESSURE, channel, atVal}); + } + + static void rtControllerChange(void *userdata, uint8_t channel, uint8_t type, uint8_t value) + { + EMIDIPlayer *player = (EMIDIPlayer *)userdata; + player->emidi_synth_->SendMIDIMessage({dsa::CMIDIMsg::CONTROL_CHANGE, channel, type, value}); + } + + static void rtPatchChange(void *userdata, uint8_t channel, uint8_t patch) + { + EMIDIPlayer *player = (EMIDIPlayer *)userdata; + player->emidi_synth_->SendMIDIMessage({dsa::CMIDIMsg::PROGRAM_CHANGE, channel, patch}); + } + + static void rtPitchBend(void *userdata, uint8_t channel, uint8_t msb, uint8_t lsb) + { + EMIDIPlayer *player = (EMIDIPlayer *)userdata; + player->emidi_synth_->SendMIDIMessage({dsa::CMIDIMsg::PITCH_BEND_CHANGE, channel, msb, lsb}); + } + + static void rtSysEx(void *userdata, const uint8_t *msg, size_t size) + { + (void)userdata; + (void)msg; + (void)size; + } + + static void rtDeviceSwitch(void *userdata, size_t track, const char *data, size_t length) + { + (void)userdata; + (void)track; + (void)data; + (void)length; + } + + static size_t rtCurrentDevice(void *userdata, size_t track) + { + (void)userdata; + (void)track; + return 0; + } + + static void playSynth(void *userdata, uint8_t *stream, size_t length) + { + EMIDIPlayer *player = (EMIDIPlayer *)userdata; + player->emidi_synth_->Render16((int16_t *)stream, length / 4); + } + + void SequencerInit() + { + emidi_sequencer_ = new EMIDISequencer; + emidi_interface_ = new EMIDIInterface; + memset(emidi_interface_, 0, sizeof(MidiRealTimeInterface)); + + emidi_interface_->rtUserData = this; + emidi_interface_->rt_noteOn = rtNoteOn; + emidi_interface_->rt_noteOff = rtNoteOff; + emidi_interface_->rt_noteAfterTouch = rtNoteAfterTouch; + emidi_interface_->rt_channelAfterTouch = rtChannelAfterTouch; + emidi_interface_->rt_controllerChange = rtControllerChange; + emidi_interface_->rt_patchChange = rtPatchChange; + emidi_interface_->rt_pitchBend = rtPitchBend; + emidi_interface_->rt_systemExclusive = rtSysEx; + + emidi_interface_->onPcmRender = playSynth; + emidi_interface_->onPcmRender_userdata = this; + + emidi_interface_->pcmSampleRate = sound_device_frequency; + emidi_interface_->pcmFrameSize = 2 /*channels*/ * 2 /*size of one sample*/; + + emidi_interface_->rt_deviceSwitch = rtDeviceSwitch; + emidi_interface_->rt_currentDevice = rtCurrentDevice; + + emidi_sequencer_->SetInterface(emidi_interface_); + } + + bool LoadTrack(const uint8_t *data, int length) + { + return emidi_sequencer_->LoadMidi(data, length); + } + + void Close(void) + { + if (status_ == kNotLoaded) + return; + + // Stop playback + if (status_ != kStopped) + Stop(); + + if (emidi_sequencer_) + { + delete emidi_sequencer_; + emidi_sequencer_ = nullptr; + } + if (emidi_interface_) + { + delete emidi_interface_; + emidi_interface_ = nullptr; + } + if (emidi_synth_) + { + delete emidi_synth_; + emidi_synth_ = nullptr; + } + + status_ = kNotLoaded; + } + + void Play(bool loop) + { + if (!(status_ == kNotLoaded || status_ == kStopped)) + return; + + status_ = kPlaying; + looping_ = loop; + + // Load up initial buffer data + Ticker(); + } + + void Stop(void) + { + if (!(status_ == kPlaying || status_ == kPaused)) + return; + + SoundQueueStop(); + + status_ = kStopped; + } + + void Pause(void) + { + if (status_ != kPlaying) + return; + + status_ = kPaused; + } + + void Resume(void) + { + if (status_ != kPaused) + return; + + status_ = kPlaying; + } + + void Ticker(void) + { + while (status_ == kPlaying && !pc_speaker_mode) + { + SoundData *buf = SoundQueueGetFreeBuffer(kMusicBuffer); + + if (!buf) + break; + + if (StreamIntoBuffer(buf)) + { + SoundQueueAddBuffer(buf, sound_device_frequency); + } + else + { + // finished playing + SoundQueueReturnBuffer(buf); + + Stop(); + } + } + } + + private: + bool StreamIntoBuffer(SoundData *buf) + { + int16_t *data_buf = buf->data_; + + bool song_done = false; + + int played = emidi_sequencer_->PlayStream((uint8_t *)data_buf, kMusicBuffer); + + if (emidi_sequencer_->PositionAtEnd()) + song_done = true; + + buf->length_ = played / 4; + + if (song_done) /* EOF */ + { + if (!looping_) + return false; + emidi_sequencer_->Rewind(); + return true; + } + + return true; + } +}; + +AbstractMusicPlayer *PlayEMIDIMusic(uint8_t *data, int length, bool loop) +{ + EMIDIPlayer *player = new EMIDIPlayer(data, length, loop); + + if (!player) + { + LogDebug("Emu de MIDI player: error initializing!\n"); + delete[] data; + return nullptr; + } + + player->emidi_synth_ = new dsa::CSMFPlay(sound_device_frequency); + if (!player->emidi_synth_) + { + LogDebug("Emu de MIDI player: error initializing!\n"); + delete[] data; + delete player; + return nullptr; + } + + if (!player->LoadTrack(data, length)) // Lobo: quietly log it instead of completely exiting EDGE + { + LogDebug("Emu de MIDI player: failed to load MIDI file!\n"); + delete[] data; + delete player; + return nullptr; + } + + delete[] data; + + player->emidi_synth_->Start(); + + player->Play(loop); + + return player; +} + +//--- editor settings --- +// vi:ts=4:sw=4:noexpandtab diff --git a/source_files/edge/s_emidi.h b/source_files/edge/s_emidi.h new file mode 100644 index 000000000..60dd282e9 --- /dev/null +++ b/source_files/edge/s_emidi.h @@ -0,0 +1,28 @@ +//---------------------------------------------------------------------------- +// EDGE Emu de MIDI Music Player +//---------------------------------------------------------------------------- +// +// Copyright (c) 2024 The EDGE Team. +// +// 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 3 +// 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. +// +//---------------------------------------------------------------------------- + +#pragma once + +#include "s_music.h" + +void RestartEMIDI(void); + +AbstractMusicPlayer *PlayEMIDIMusic(uint8_t *data, int length, bool loop); + +//--- editor settings --- +// vi:ts=4:sw=4:noexpandtab diff --git a/source_files/edge/s_fmm.cc b/source_files/edge/s_fmm.cc index 3a259b6f9..cc616ec70 100644 --- a/source_files/edge/s_fmm.cc +++ b/source_files/edge/s_fmm.cc @@ -149,7 +149,7 @@ class FMMPlayer : public AbstractMusicPlayer static void playSynth(void *userdata, uint8_t *stream, size_t length) { FMMPlayer *player = (FMMPlayer *)userdata; - player->fmm_synth_->synthesize(reinterpret_cast(stream), length / 4, sound_device_frequency); + player->fmm_synth_->synthesize((int_least16_t *)stream, length / 4, sound_device_frequency); } void SequencerInit() diff --git a/source_files/edge/s_music.cc b/source_files/edge/s_music.cc index a2c6e4cfe..9197c81cb 100644 --- a/source_files/edge/s_music.cc +++ b/source_files/edge/s_music.cc @@ -30,6 +30,7 @@ #include "epi_str_util.h" #include "i_system.h" #include "m_misc.h" +#include "s_emidi.h" #include "s_flac.h" #include "s_fluid.h" #include "s_fmm.h" @@ -220,10 +221,14 @@ void ChangeMusic(int entry_number, bool loop) { music_player = PlayOPLMusic(data, length, loop, play->type_); } - else + else if (var_midi_player == 2) { music_player = PlayFMMMusic(data, length, loop); } + else + { + music_player = PlayEMIDIMusic(data, length, loop); + } break; default: