diff --git a/CMakeLists.txt b/CMakeLists.txt index ccb0d0e..0f4a85d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,9 @@ set(FILEFORMATS_SOURCES "src/FileFormats/format_opm.cpp" "src/FileFormats/format_mucom88_dat.cpp" "src/FileFormats/format_vgi.cpp" - "src/FileFormats/format_wohlstand_opn2.cpp") + "src/FileFormats/format_wohlstand_opn2.cpp" + "src/FileFormats/ym2612_to_wopi.cpp" + "src/FileFormats/ym2151_to_wopi.cpp") add_library(FileFormats STATIC ${FILEFORMATS_SOURCES}) target_include_directories(FileFormats PUBLIC "src") target_link_libraries(FileFormats PUBLIC Common) diff --git a/FMBankEdit.pro b/FMBankEdit.pro index 2cdd084..18e35a9 100644 --- a/FMBankEdit.pro +++ b/FMBankEdit.pro @@ -119,6 +119,8 @@ SOURCES += \ src/FileFormats/format_mucom88_dat.cpp \ src/FileFormats/format_vgi.cpp \ src/FileFormats/format_wohlstand_opn2.cpp \ + src/FileFormats/ym2612_to_wopi.cpp \ + src/FileFormats/ym2151_to_wopi.cpp \ src/formats_sup.cpp \ src/importer.cpp \ src/latency.cpp \ @@ -155,6 +157,8 @@ HEADERS += \ src/FileFormats/format_mucom88_dat.h \ src/FileFormats/format_vgi.h \ src/FileFormats/format_wohlstand_opn2.h \ + src/FileFormats/ym2612_to_wopi.h \ + src/FileFormats/ym2151_to_wopi.h \ src/formats_sup.h \ src/importer.h \ src/latency.h \ @@ -168,8 +172,7 @@ HEADERS += \ src/opl/realtime/ring_buffer.h \ src/opl/realtime/ring_buffer.tcc \ src/piano.h \ - src/version.h \ - src/FileFormats/ym2612_to_wopi.hpp + src/version.h FORMS += \ src/bank_editor.ui \ diff --git a/src/FileFormats/format_gym_import.cpp b/src/FileFormats/format_gym_import.cpp index 5386e89..abf496b 100644 --- a/src/FileFormats/format_gym_import.cpp +++ b/src/FileFormats/format_gym_import.cpp @@ -17,7 +17,7 @@ */ #include "format_gym_import.h" -#include "ym2612_to_wopi.hpp" +#include "ym2612_to_wopi.h" #include "../common.h" #include diff --git a/src/FileFormats/format_s98_import.cpp b/src/FileFormats/format_s98_import.cpp index a17a271..8c13c61 100644 --- a/src/FileFormats/format_s98_import.cpp +++ b/src/FileFormats/format_s98_import.cpp @@ -17,7 +17,7 @@ */ #include "format_s98_import.h" -#include "ym2612_to_wopi.hpp" +#include "ym2612_to_wopi.h" #include "../common.h" #include diff --git a/src/FileFormats/format_vgm_import.cpp b/src/FileFormats/format_vgm_import.cpp index b0f6942..363a14c 100644 --- a/src/FileFormats/format_vgm_import.cpp +++ b/src/FileFormats/format_vgm_import.cpp @@ -17,12 +17,16 @@ */ #include "format_vgm_import.h" -#include "ym2612_to_wopi.hpp" +#include "ym2612_to_wopi.h" +#include "ym2151_to_wopi.h" #include "../common.h" #include #include #include +#include + +static void make_size_table(uint8_t *table, unsigned version); const char magic_vgm[4] = {0x56, 0x67, 0x6D, 0x20}; @@ -35,7 +39,9 @@ FfmtErrCode VGM_Importer::loadFile(QString filePath, FmBank &bank) { RawYm2612ToWopi pseudoChip; RawYm2612ToWopi pseudoChip2608; + RawYm2151ToWopi pseudoChip2151; pseudoChip2608.shareInstruments(pseudoChip); + pseudoChip2151.shareInstruments(pseudoChip); char magic[4]; uint8_t numb[4]; @@ -51,13 +57,22 @@ FfmtErrCode VGM_Importer::loadFile(QString filePath, FmBank &bank) if(memcmp(magic, magic_vgm, 4) != 0) return FfmtErrCode::ERR_BADFORMAT; - file.seek(0x34); - file.read(char_p(numb), 4); + file.seek(0x8); + if(file.read(char_p(numb), 4) != 4) + return FfmtErrCode::ERR_BADFORMAT; + + uint32_t vgm_version = toUint32LE(numb); + uint8_t vgm_sizetable[0x100]; + make_size_table(vgm_sizetable, vgm_version); - uint32_t data_offset = toUint32LE(numb); - if(data_offset == 0x0C) - data_offset = 0x40; - file.seek(data_offset); + uint32_t data_offset = 0xC; + if(vgm_version >= 0x150) + { + file.seek(0x34); + file.read(char_p(numb), 4); + data_offset = toUint32LE(numb); + } + file.seek(0x34 + data_offset); bank.Ins_Melodic_box.clear(); @@ -71,6 +86,18 @@ FfmtErrCode VGM_Importer::loadFile(QString filePath, FmBank &bank) file.read(char_p(&cmd), 1); switch(cmd) { + default: { + uint8_t toSkip = vgm_sizetable[cmd]; + if(toSkip != 0xFF) + file.seek(file.pos() + toSkip); + else + { + //Unrecognized command + end = true; + } + break; + } + case 0x52://Write YM2612 port 0 file.read(char_p(®), 1); file.read(char_p(&val), 1); @@ -82,6 +109,12 @@ FfmtErrCode VGM_Importer::loadFile(QString filePath, FmBank &bank) pseudoChip.passReg(1, reg, val); break; + case 0x54://Write YM2151 port + file.read(char_p(®), 1); + file.read(char_p(&val), 1); + pseudoChip2151.passReg(reg, val); + break; + case 0x56://Write YM2608 port 0 file.read(char_p(®), 1); file.read(char_p(&val), 1); @@ -116,6 +149,7 @@ FfmtErrCode VGM_Importer::loadFile(QString filePath, FmBank &bank) file.seek(file.pos() + 2); pseudoChip.doAnalyzeState(); pseudoChip2608.doAnalyzeState(); + pseudoChip2151.doAnalyzeState(); break; case 0x66://End of sound data @@ -186,3 +220,93 @@ BankFormats VGM_Importer::formatId() const { return BankFormats::FORMAT_VGM_IMPORTER; } + +static void make_size_table(uint8_t *table, unsigned version) +{ + std::memset(table, 0xFF, 0x100); + table[0x4F] = 1; // Game Gear PSG stereo, write dd to port 0x06 + table[0x50] = 1; // PSG (SN76489/SN76496) write value dd + table[0x51] = 2; // YM2413, write value dd to register aa + table[0x52] = 2; // YM2612 port 0, write value dd to register aa + table[0x53] = 2; // YM2612 port 1, write value dd to register aa + table[0x54] = 2; // YM2151, write value dd to register aa + table[0x55] = 2; // YM2203, write value dd to register aa + table[0x56] = 2; // YM2608 port 0, write value dd to register aa + table[0x57] = 2; // YM2608 port 1, write value dd to register aa + table[0x58] = 2; // YM2610 port 0, write value dd to register aa + table[0x59] = 2; // YM2610 port 1, write value dd to register aa + table[0x5A] = 2; // YM3812, write value dd to register aa + table[0x5B] = 2; // YM3526, write value dd to register aa + table[0x5C] = 2; // Y8950, write value dd to register aa + table[0x5D] = 2; // YMZ280B, write value dd to register aa + table[0x5E] = 2; // YMF262 port 0, write value dd to register aa + table[0x5F] = 2; // YMF262 port 1, write value dd to register aa + table[0x61] = 2; // Wait n samples, n can range from 0 to 65535 (approx 1.49 seconds). Longer pauses than this are represented by multiple wait commands. + table[0x62] = 0; // wait 735 samples (60th of a second), a shortcut for 0x61 0xdf 0x02 + table[0x63] = 0; // wait 882 samples (50th of a second), a shortcut for 0x61 0x72 0x03 + table[0x66] = 0; // end of sound data + table[0x67] = /* variable size */0xFF; // data block: see below + table[0x68] = 11; // PCM RAM write: see below + for(unsigned a = 0x70; a <= 0x7F; ++a) + table[a] = 0; // wait n+1 samples, n can range from 0 to 15. + for(unsigned a = 0x80; a <= 0x8F; ++a) + table[a] = 0; // YM2612 port 0 address 2A write from the data bank, then wait n samples; n can range from 0 to 15. Note that the wait is n, NOT n+1. See also command 0xE0. + table[0x90] = 4; // Setup Stream Control + table[0x91] = 4; // Set Stream Data + table[0x92] = 5; // Set Stream Frequency + table[0x93] = 10; // Start Stream + table[0x94] = 1; // Stop Stream + table[0x95] = 4; // Start Stream (fast call) + table[0xA0] = 2; // AY8910, write value dd to register aa + table[0xB0] = 2; // RF5C68, write value dd to register aa + table[0xB1] = 2; // RF5C164, write value dd to register aa + table[0xB2] = 2; // PWM, write value ddd to register a (d is MSB, dd is LSB) + table[0xB3] = 2; // GameBoy DMG, write value dd to register aa + table[0xB4] = 2; // NES APU, write value dd to register aa + table[0xB5] = 2; // MultiPCM, write value dd to register aa + table[0xB6] = 2; // uPD7759, write value dd to register aa + table[0xB7] = 2; // OKIM6258, write value dd to register aa + table[0xB8] = 2; // OKIM6295, write value dd to register aa + table[0xB9] = 2; // HuC6280, write value dd to register aa + table[0xBA] = 2; // K053260, write value dd to register aa + table[0xBB] = 2; // Pokey, write value dd to register aa + table[0xBC] = 2; // WonderSwan, write value dd to register aa + table[0xBD] = 2; // SAA1099, write value dd to register aa + table[0xBE] = 2; // ES5506, write value dd to register aa + table[0xBF] = 2; // GA20, write value dd to register aa + table[0xC0] = 3; // Sega PCM, write value dd to memory offset aabb + table[0xC1] = 3; // RF5C68, write value dd to memory offset aabb + table[0xC2] = 3; // RF5C164, write value dd to memory offset aabb + table[0xC3] = 3; // MultiPCM, write set bank offset aabb to channel cc + table[0xC4] = 3; // QSound, write value mmll to register rr (mm - data MSB, ll - data LSB) + table[0xC5] = 3; // SCSP, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB) + table[0xC6] = 3; // WonderSwan, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB) + table[0xC7] = 3; // VSU, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB) + table[0xC8] = 3; // X1-010, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB) + table[0xD0] = 3; // YMF278B, port pp, write value dd to register aa + table[0xD1] = 3; // YMF271, port pp, write value dd to register aa + table[0xD2] = 3; // SCC1, port pp, write value dd to register aa + table[0xD3] = 3; // K054539, write value dd to register ppaa + table[0xD4] = 3; // C140, write value dd to register ppaa + table[0xD5] = 3; // ES5503, write value dd to register ppaa + table[0xD6] = 3; // ES5506, write value aadd to register pp + table[0xE0] = 4; // Seek to offset dddddddd (Intel byte order) in PCM data bank of data block type 0 (YM2612). + table[0xE1] = 4; // C352, write value aadd to register mmll + for(unsigned a = 0x30; a <= 0x3F; ++a) + table[a] = 1; // one operand, reserved for future use + for(unsigned a = 0x40; a <= 0x4E; ++a) + { + if(version >= 0x160) + table[a] = 2; // two operands, reserved for future use + else + table[a] = 1; // was one operand only til v1.60 + } + for(unsigned a = 0xA1; a <= 0xAF; ++a) + table[a] = 2; // two operands, reserved for future use + for(unsigned a = 0xC9; a <= 0xCF; ++a) + table[a] = 3; // three operands, reserved for future use + for(unsigned a = 0xD7; a <= 0xDF; ++a) + table[a] = 3; // three operands, reserved for future use + for(unsigned a = 0xE2; a <= 0xFF; ++a) + table[a] = 4; // three operands, reserved for future use +} diff --git a/src/FileFormats/ym2151_to_wopi.cpp b/src/FileFormats/ym2151_to_wopi.cpp new file mode 100644 index 0000000..6d54b72 --- /dev/null +++ b/src/FileFormats/ym2151_to_wopi.cpp @@ -0,0 +1,144 @@ +/* + * OPN2 Bank Editor by Wohlstand, a free tool for music bank editing + * Copyright (c) 2017-2019 Vitaly Novichkov + * + * 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ym2151_to_wopi.h" +#include + +RawYm2151ToWopi::RawYm2151ToWopi() +{ + m_insdata.reset(new InstrumentData); + reset(); +} + +void RawYm2151ToWopi::reset() +{ + InstrumentData &insdata = *m_insdata; + insdata.cache.clear(); + insdata.caughtInstruments.clear(); + + std::memset(m_keys, 0, sizeof(m_keys)); + std::memset(m_ymram, 0, sizeof(m_ymram)); +} + +void RawYm2151ToWopi::passReg(uint8_t reg, uint8_t val) +{ + if(reg == 0x08) + m_keys[val & 3] = (val >> 3) & 15; + + m_ymram[reg] = val; +} + +void RawYm2151ToWopi::doAnalyzeState() +{ + InstrumentData &insdata = *m_insdata; + + /* Analyze dumps and take the instruments */ + for(uint8_t ch = 0; ch < 8; ch++) + { + if(m_keys[ch] == 0) + continue;//Skip if key is not pressed + + QByteArray insRaw;//Raw instrument + FmBank::Instrument ins = FmBank::emptyInst(); + for(uint8_t op = 0; op < 4; op++) + { + ins.setRegDUMUL(op, m_ymram[0x40 + (op * 8) + ch]); + ins.setRegLevel(op, m_ymram[0x60 + (op * 8) + ch]); + ins.setRegRSAt(op, m_ymram[0x80 + (op * 8) + ch]); + ins.setRegAMD1(op, m_ymram[0xa0 + (op * 8) + ch]); + ins.setRegD2(op, m_ymram[0xc0 + (op * 8) + ch]); + ins.setRegSysRel(op,m_ymram[0xe0 + (op * 8) + ch]); + insRaw.push_back((char)ins.getRegDUMUL(op)); + insRaw.push_back((char)ins.getRegLevel(op)); + insRaw.push_back((char)ins.getRegRSAt(op)); + insRaw.push_back((char)ins.getRegAMD1(op)); + insRaw.push_back((char)ins.getRegD2(op)); + insRaw.push_back((char)ins.getRegSysRel(op)); + insRaw.push_back((char)ins.getRegSsgEg(op)); + } + + ins.am = m_ymram[0x38] & 3; + ins.fm = (m_ymram[0x38] >> 4) & 7; + ins.algorithm = m_ymram[0x20] & 7; + ins.feedback = (m_ymram[0x20] >> 3) & 7; + insRaw.push_back((char)ins.getRegLfoSens()); + insRaw.push_back((char)ins.getRegFbAlg()); + + /* Maximize key volume */ + uint8_t olevels[4] = + { + ins.OP[OPERATOR1].level, + ins.OP[OPERATOR2].level, + ins.OP[OPERATOR3].level, + ins.OP[OPERATOR4].level + }; + + uint8_t dec = 0; + switch(ins.algorithm) + { + case 0:case 1: case 2: case 3: + ins.OP[OPERATOR4].level = 0; + break; + case 4: + dec = std::min({olevels[OPERATOR3], olevels[OPERATOR4]}); + ins.OP[OPERATOR3].level = olevels[OPERATOR3] - dec; + ins.OP[OPERATOR4].level = olevels[OPERATOR4] - dec; + break; + case 5: + dec = std::min({olevels[OPERATOR2], olevels[OPERATOR3], olevels[OPERATOR4]}); + ins.OP[OPERATOR2].level = olevels[OPERATOR2] - dec; + ins.OP[OPERATOR3].level = olevels[OPERATOR3] - dec; + ins.OP[OPERATOR4].level = olevels[OPERATOR4] - dec; + break; + case 6: + dec = std::min({olevels[OPERATOR2], olevels[OPERATOR3], olevels[OPERATOR4]}); + ins.OP[OPERATOR2].level = olevels[OPERATOR2] - dec; + ins.OP[OPERATOR3].level = olevels[OPERATOR3] - dec; + ins.OP[OPERATOR4].level = olevels[OPERATOR4] - dec; + break; + case 7: + dec = std::min({olevels[OPERATOR1], olevels[OPERATOR2], olevels[OPERATOR3], olevels[OPERATOR4]}); + ins.OP[OPERATOR1].level = olevels[OPERATOR1] - dec; + ins.OP[OPERATOR2].level = olevels[OPERATOR2] - dec; + ins.OP[OPERATOR3].level = olevels[OPERATOR3] - dec; + ins.OP[OPERATOR4].level = olevels[OPERATOR4] - dec; + break; + } + + //Encode volume bytes back + insRaw[1 + OPERATOR1*7] = (char)ins.getRegLevel(OPERATOR1); + insRaw[1 + OPERATOR2*7] = (char)ins.getRegLevel(OPERATOR2); + insRaw[1 + OPERATOR3*7] = (char)ins.getRegLevel(OPERATOR3); + insRaw[1 + OPERATOR4*7] = (char)ins.getRegLevel(OPERATOR4); + + if(!insdata.cache.contains(insRaw)) + { + std::snprintf(ins.name, 32, + "Ins %d, channel %d", + insdata.caughtInstruments.size(), + (int)ch); + insdata.caughtInstruments.push_back(ins); + insdata.cache.insert(insRaw); + } + } +} + +const QList &RawYm2151ToWopi::caughtInstruments() +{ + return m_insdata->caughtInstruments; +} diff --git a/src/FileFormats/ym2151_to_wopi.h b/src/FileFormats/ym2151_to_wopi.h new file mode 100644 index 0000000..4ffe025 --- /dev/null +++ b/src/FileFormats/ym2151_to_wopi.h @@ -0,0 +1,44 @@ +/* + * OPN2 Bank Editor by Wohlstand, a free tool for music bank editing + * Copyright (c) 2017-2019 Vitaly Novichkov + * + * 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef YM2151_TO_WOPI_H +#define YM2151_TO_WOPI_H + +#include +#include + +#include "../bank.h" +#include "ym2612_to_wopi.h" // instrument sharing + +class RawYm2151ToWopi +{ + typedef RawYm2612ToWopi::InstrumentData InstrumentData; + uint8_t m_keys[8]; + uint8_t m_ymram[0xFF]; + std::shared_ptr m_insdata; + +public: + RawYm2151ToWopi(); + void reset(); + template void shareInstruments(Ym &other) { m_insdata = other.m_insdata; } + void passReg(uint8_t reg, uint8_t val); + void doAnalyzeState(); + const QList &caughtInstruments(); +}; + +#endif // YM2151_TO_WOPI_H diff --git a/src/FileFormats/ym2612_to_wopi.cpp b/src/FileFormats/ym2612_to_wopi.cpp new file mode 100644 index 0000000..0d2077d --- /dev/null +++ b/src/FileFormats/ym2612_to_wopi.cpp @@ -0,0 +1,184 @@ +/* + * OPN2 Bank Editor by Wohlstand, a free tool for music bank editing + * Copyright (c) 2017-2019 Vitaly Novichkov + * + * 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ym2612_to_wopi.h" +#include + +RawYm2612ToWopi::RawYm2612ToWopi() +{ + m_insdata.reset(new InstrumentData); + reset(); +} + +void RawYm2612ToWopi::reset() +{ + InstrumentData &insdata = *m_insdata; + insdata.cache.clear(); + insdata.caughtInstruments.clear(); + + std::memset(m_magic, 0, 4); + std::memset(m_ymram[0], 0, 0xFF); + std::memset(m_ymram[1], 0, 0xFF); + std::memset(m_keys, 0, sizeof(bool) * 6); + m_lfoVal = 0; + m_dacOn = false; +} + +void RawYm2612ToWopi::shareInstruments(RawYm2612ToWopi &other) +{ + m_insdata = other.m_insdata; +} + +void RawYm2612ToWopi::passReg(uint8_t port, uint8_t reg, uint8_t val) +{ + if(port == 0) + { + //Get useful-only registers + if( ((reg >= 0x30) && (reg <= 0x9F)) || + ((reg >= 0xB0) && (reg <= 0xB6)) ) + m_ymram[port][reg] = val; + if(reg == 0x28) + { + switch(val&0x0F) + { + case 0: case 1: case 2: case 3: + m_keys[val&0x0F] = ((val>>4)&0x0F) != 0; + break; + case 4: case 5: case 6: + m_keys[(val&0x0F) - 1] = ((val>>4)&0x0F) != 0; + break; + } + } + if(reg == 0x22) + m_lfoVal = val; + if(reg == 0x2B) + m_dacOn = (val != 0); + } + else + if(port == 1) + { + //Get useful-only registers + if( ((reg >= 0x30) && (reg <= 0x9F)) || + ((reg >= 0xB0) && (reg <= 0xB6)) ) + m_ymram[1][reg] = val; + } +} + +void RawYm2612ToWopi::doAnalyzeState() +{ + InstrumentData &insdata = *m_insdata; + + /* Analyze dumps and take the instruments */ + for(size_t i = 0; i < 2; i++) + { + for(uint8_t ch = 0; ch < 3; ch++) + { + if(!m_keys[ch + 3*i]) + continue;//Skip if key is not pressed + if(m_dacOn && (ch+ 3*i == 5)) + continue;//Skip DAC channel + QByteArray insRaw;//Raw instrument + FmBank::Instrument ins = FmBank::emptyInst(); + for(uint8_t op = 0; op < 4; op++) + { + ins.setRegDUMUL(op, m_ymram[i][0x30 + (op * 4) + ch]); + ins.setRegLevel(op, m_ymram[i][0x40 + (op * 4) + ch]); + ins.setRegRSAt(op, m_ymram[i][0x50 + (op * 4) + ch]); + ins.setRegAMD1(op, m_ymram[i][0x60 + (op * 4) + ch]); + ins.setRegD2(op, m_ymram[i][0x70 + (op * 4) + ch]); + ins.setRegSysRel(op,m_ymram[i][0x80 + (op * 4) + ch]); + ins.setRegSsgEg(op, m_ymram[i][0x90 + (op * 4) + ch]); + insRaw.push_back((char)ins.getRegDUMUL(op)); + insRaw.push_back((char)ins.getRegLevel(op)); + insRaw.push_back((char)ins.getRegRSAt(op)); + insRaw.push_back((char)ins.getRegAMD1(op)); + insRaw.push_back((char)ins.getRegD2(op)); + insRaw.push_back((char)ins.getRegSysRel(op)); + insRaw.push_back((char)ins.getRegSsgEg(op)); + } + + ins.setRegLfoSens(m_ymram[i][0xB4 + ch]); + ins.setRegFbAlg(m_ymram[i][0xB0 + ch]); + + insRaw.push_back((char)ins.getRegLfoSens()); + insRaw.push_back((char)ins.getRegFbAlg()); + + /* Maximize key volume */ + uint8_t olevels[4] = + { + ins.OP[OPERATOR1].level, + ins.OP[OPERATOR2].level, + ins.OP[OPERATOR3].level, + ins.OP[OPERATOR4].level + }; + + uint8_t dec = 0; + switch(ins.algorithm) + { + case 0:case 1: case 2: case 3: + ins.OP[OPERATOR4].level = 0; + break; + case 4: + dec = std::min({olevels[OPERATOR3], olevels[OPERATOR4]}); + ins.OP[OPERATOR3].level = olevels[OPERATOR3] - dec; + ins.OP[OPERATOR4].level = olevels[OPERATOR4] - dec; + break; + case 5: + dec = std::min({olevels[OPERATOR2], olevels[OPERATOR3], olevels[OPERATOR4]}); + ins.OP[OPERATOR2].level = olevels[OPERATOR2] - dec; + ins.OP[OPERATOR3].level = olevels[OPERATOR3] - dec; + ins.OP[OPERATOR4].level = olevels[OPERATOR4] - dec; + break; + case 6: + dec = std::min({olevels[OPERATOR2], olevels[OPERATOR3], olevels[OPERATOR4]}); + ins.OP[OPERATOR2].level = olevels[OPERATOR2] - dec; + ins.OP[OPERATOR3].level = olevels[OPERATOR3] - dec; + ins.OP[OPERATOR4].level = olevels[OPERATOR4] - dec; + break; + case 7: + dec = std::min({olevels[OPERATOR1], olevels[OPERATOR2], olevels[OPERATOR3], olevels[OPERATOR4]}); + ins.OP[OPERATOR1].level = olevels[OPERATOR1] - dec; + ins.OP[OPERATOR2].level = olevels[OPERATOR2] - dec; + ins.OP[OPERATOR3].level = olevels[OPERATOR3] - dec; + ins.OP[OPERATOR4].level = olevels[OPERATOR4] - dec; + break; + } + + //Encode volume bytes back + insRaw[1 + OPERATOR1*7] = (char)ins.getRegLevel(OPERATOR1); + insRaw[1 + OPERATOR2*7] = (char)ins.getRegLevel(OPERATOR2); + insRaw[1 + OPERATOR3*7] = (char)ins.getRegLevel(OPERATOR3); + insRaw[1 + OPERATOR4*7] = (char)ins.getRegLevel(OPERATOR4); + + if(!insdata.cache.contains(insRaw)) + { + std::snprintf(ins.name, 32, + "Ins %d, channel %d", + insdata.caughtInstruments.size(), + (int)(ch + (3 * i))); + insdata.caughtInstruments.push_back(ins); + insdata.cache.insert(insRaw); + } + } + } +} + +const QList &RawYm2612ToWopi::caughtInstruments() +{ + return m_insdata->caughtInstruments; +} diff --git a/src/FileFormats/ym2612_to_wopi.h b/src/FileFormats/ym2612_to_wopi.h new file mode 100644 index 0000000..3bcf445 --- /dev/null +++ b/src/FileFormats/ym2612_to_wopi.h @@ -0,0 +1,56 @@ +/* + * OPN2 Bank Editor by Wohlstand, a free tool for music bank editing + * Copyright (c) 2017-2019 Vitaly Novichkov + * + * 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef YM2612_TO_WOPI_H +#define YM2612_TO_WOPI_H + +#include +#include +#include +#include +#include + +#include "../bank.h" + +class RawYm2612ToWopi +{ + struct InstrumentData + { + QSet cache; + QList caughtInstruments; + }; + + uint8_t m_ymram[2][0xFF]; + char m_magic[4]; + bool m_keys[6]; + uint8_t m_lfoVal = 0; + bool m_dacOn = false; + std::shared_ptr m_insdata; + + friend class RawYm2151ToWopi; // instrument sharing + +public: + RawYm2612ToWopi(); + void reset(); + void shareInstruments(RawYm2612ToWopi &other); + void passReg(uint8_t port, uint8_t reg, uint8_t val); + void doAnalyzeState(); + const QList &caughtInstruments(); +}; + +#endif // YM2612_TO_WOPI_H diff --git a/src/FileFormats/ym2612_to_wopi.hpp b/src/FileFormats/ym2612_to_wopi.hpp deleted file mode 100644 index 44fb0d6..0000000 --- a/src/FileFormats/ym2612_to_wopi.hpp +++ /dev/null @@ -1,195 +0,0 @@ - -#include -#include -#include -#include -#include - -#include "../bank.h" - -#ifndef YM2612_TO_WOPI_HPP -#define YM2612_TO_WOPI_HPP - -class RawYm2612ToWopi -{ - struct InstrumentData - { - QSet cache; - QList caughtInstruments; - }; - - uint8_t m_ymram[2][0xFF]; - char m_magic[4]; - bool m_keys[6]; - uint8_t m_lfoVal = 0; - bool m_dacOn = false; - std::shared_ptr m_insdata; - -public: - RawYm2612ToWopi() - { - m_insdata.reset(new InstrumentData); - reset(); - } - - void reset() - { - InstrumentData &insdata = *m_insdata; - insdata.cache.clear(); - insdata.caughtInstruments.clear(); - - std::memset(m_magic, 0, 4); - std::memset(m_ymram[0], 0, 0xFF); - std::memset(m_ymram[1], 0, 0xFF); - std::memset(m_keys, 0, sizeof(bool) * 6); - m_lfoVal = 0; - m_dacOn = false; - } - - void shareInstruments(RawYm2612ToWopi &other) - { - m_insdata = other.m_insdata; - } - - void passReg(uint8_t port, uint8_t reg, uint8_t val) - { - if(port == 0) - { - //Get useful-only registers - if( ((reg >= 0x30) && (reg <= 0x9F)) || - ((reg >= 0xB0) && (reg <= 0xB6)) ) - m_ymram[port][reg] = val; - if(reg == 0x28) - { - switch(val&0x0F) - { - case 0: case 1: case 2: case 3: - m_keys[val&0x0F] = ((val>>4)&0x0F) != 0; - break; - case 4: case 5: case 6: - m_keys[(val&0x0F) - 1] = ((val>>4)&0x0F) != 0; - break; - } - } - if(reg == 0x22) - m_lfoVal = val; - if(reg == 0x2B) - m_dacOn = (val != 0); - } - else - if(port == 1) - { - //Get useful-only registers - if( ((reg >= 0x30) && (reg <= 0x9F)) || - ((reg >= 0xB0) && (reg <= 0xB6)) ) - m_ymram[1][reg] = val; - } - } - - void doAnalyzeState() - { - InstrumentData &insdata = *m_insdata; - - /* Analyze dumps and take the instruments */ - for(size_t i = 0; i < 2; i++) - { - for(uint8_t ch = 0; ch < 3; ch++) - { - if(!m_keys[ch + 3*i]) - continue;//Skip if key is not pressed - if(m_dacOn && (ch+ 3*i == 5)) - continue;//Skip DAC channel - QByteArray insRaw;//Raw instrument - FmBank::Instrument ins = FmBank::emptyInst(); - for(uint8_t op = 0; op < 4; op++) - { - ins.setRegDUMUL(op, m_ymram[i][0x30 + (op * 4) + ch]); - ins.setRegLevel(op, m_ymram[i][0x40 + (op * 4) + ch]); - ins.setRegRSAt(op, m_ymram[i][0x50 + (op * 4) + ch]); - ins.setRegAMD1(op, m_ymram[i][0x60 + (op * 4) + ch]); - ins.setRegD2(op, m_ymram[i][0x70 + (op * 4) + ch]); - ins.setRegSysRel(op,m_ymram[i][0x80 + (op * 4) + ch]); - ins.setRegSsgEg(op, m_ymram[i][0x90 + (op * 4) + ch]); - insRaw.push_back((char)ins.getRegDUMUL(op)); - insRaw.push_back((char)ins.getRegLevel(op)); - insRaw.push_back((char)ins.getRegRSAt(op)); - insRaw.push_back((char)ins.getRegAMD1(op)); - insRaw.push_back((char)ins.getRegD2(op)); - insRaw.push_back((char)ins.getRegSysRel(op)); - insRaw.push_back((char)ins.getRegSsgEg(op)); - } - - ins.setRegLfoSens(m_ymram[i][0xB4 + ch]); - ins.setRegFbAlg(m_ymram[i][0xB0 + ch]); - - insRaw.push_back((char)ins.getRegLfoSens()); - insRaw.push_back((char)ins.getRegFbAlg()); - - /* Maximize key volume */ - uint8_t olevels[4] = - { - ins.OP[OPERATOR1].level, - ins.OP[OPERATOR2].level, - ins.OP[OPERATOR3].level, - ins.OP[OPERATOR4].level - }; - - uint8_t dec = 0; - switch(ins.algorithm) - { - case 0:case 1: case 2: case 3: - ins.OP[OPERATOR4].level = 0; - break; - case 4: - dec = std::min({olevels[OPERATOR3], olevels[OPERATOR4]}); - ins.OP[OPERATOR3].level = olevels[OPERATOR3] - dec; - ins.OP[OPERATOR4].level = olevels[OPERATOR4] - dec; - break; - case 5: - dec = std::min({olevels[OPERATOR2], olevels[OPERATOR3], olevels[OPERATOR4]}); - ins.OP[OPERATOR2].level = olevels[OPERATOR2] - dec; - ins.OP[OPERATOR3].level = olevels[OPERATOR3] - dec; - ins.OP[OPERATOR4].level = olevels[OPERATOR4] - dec; - break; - case 6: - dec = std::min({olevels[OPERATOR2], olevels[OPERATOR3], olevels[OPERATOR4]}); - ins.OP[OPERATOR2].level = olevels[OPERATOR2] - dec; - ins.OP[OPERATOR3].level = olevels[OPERATOR3] - dec; - ins.OP[OPERATOR4].level = olevels[OPERATOR4] - dec; - break; - case 7: - dec = std::min({olevels[OPERATOR1], olevels[OPERATOR2], olevels[OPERATOR3], olevels[OPERATOR4]}); - ins.OP[OPERATOR1].level = olevels[OPERATOR1] - dec; - ins.OP[OPERATOR2].level = olevels[OPERATOR2] - dec; - ins.OP[OPERATOR3].level = olevels[OPERATOR3] - dec; - ins.OP[OPERATOR4].level = olevels[OPERATOR4] - dec; - break; - } - - //Encode volume bytes back - insRaw[1 + OPERATOR1*7] = (char)ins.getRegLevel(OPERATOR1); - insRaw[1 + OPERATOR2*7] = (char)ins.getRegLevel(OPERATOR2); - insRaw[1 + OPERATOR3*7] = (char)ins.getRegLevel(OPERATOR3); - insRaw[1 + OPERATOR4*7] = (char)ins.getRegLevel(OPERATOR4); - - if(!insdata.cache.contains(insRaw)) - { - std::snprintf(ins.name, 32, - "Ins %d, channel %d", - insdata.caughtInstruments.size(), - (int)(ch + (3 * i)) - ); - insdata.caughtInstruments.push_back(ins); - insdata.cache.insert(insRaw); - } - } - } - } - - const QList &caughtInstruments() - { - return m_insdata->caughtInstruments; - } -}; - -#endif // YM2612_TO_WOPI_HPP