From a609f4c1f73a99deddfa69cb25a1b5e33ffcca78 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Thu, 28 Mar 2019 21:27:18 +0100 Subject: [PATCH 1/5] reorganize the code of RawYm2612 --- CMakeLists.txt | 3 +- FMBankEdit.pro | 5 +- src/FileFormats/format_gym_import.cpp | 2 +- src/FileFormats/format_s98_import.cpp | 2 +- src/FileFormats/format_vgm_import.cpp | 2 +- src/FileFormats/ym2612_to_wopi.cpp | 184 ++++++++++++++++++++++++ src/FileFormats/ym2612_to_wopi.h | 53 +++++++ src/FileFormats/ym2612_to_wopi.hpp | 195 -------------------------- 8 files changed, 245 insertions(+), 201 deletions(-) create mode 100644 src/FileFormats/ym2612_to_wopi.cpp create mode 100644 src/FileFormats/ym2612_to_wopi.h delete mode 100644 src/FileFormats/ym2612_to_wopi.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ccb0d0e..17fda0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,8 @@ 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") 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..e5f05e1 100644 --- a/FMBankEdit.pro +++ b/FMBankEdit.pro @@ -119,6 +119,7 @@ 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/formats_sup.cpp \ src/importer.cpp \ src/latency.cpp \ @@ -155,6 +156,7 @@ 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/formats_sup.h \ src/importer.h \ src/latency.h \ @@ -168,8 +170,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..b3cbb3b 100644 --- a/src/FileFormats/format_vgm_import.cpp +++ b/src/FileFormats/format_vgm_import.cpp @@ -17,7 +17,7 @@ */ #include "format_vgm_import.h" -#include "ym2612_to_wopi.hpp" +#include "ym2612_to_wopi.h" #include "../common.h" #include 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..11c577a --- /dev/null +++ b/src/FileFormats/ym2612_to_wopi.h @@ -0,0 +1,53 @@ +/* + * 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 "../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; + +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 From 3f2e5c1619441d34816df78850ac5608d39280f7 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Thu, 28 Mar 2019 21:52:01 +0100 Subject: [PATCH 2/5] add raw YM2151 --- CMakeLists.txt | 3 +- FMBankEdit.pro | 2 + src/FileFormats/format_vgm_import.cpp | 10 ++ src/FileFormats/ym2151_to_wopi.cpp | 144 ++++++++++++++++++++++++++ src/FileFormats/ym2151_to_wopi.h | 46 ++++++++ src/FileFormats/ym2612_to_wopi.h | 2 + 6 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 src/FileFormats/ym2151_to_wopi.cpp create mode 100644 src/FileFormats/ym2151_to_wopi.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 17fda0c..0f4a85d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,7 +83,8 @@ set(FILEFORMATS_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/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 e5f05e1..18e35a9 100644 --- a/FMBankEdit.pro +++ b/FMBankEdit.pro @@ -120,6 +120,7 @@ SOURCES += \ 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 \ @@ -157,6 +158,7 @@ HEADERS += \ 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 \ diff --git a/src/FileFormats/format_vgm_import.cpp b/src/FileFormats/format_vgm_import.cpp index b3cbb3b..6474e66 100644 --- a/src/FileFormats/format_vgm_import.cpp +++ b/src/FileFormats/format_vgm_import.cpp @@ -18,6 +18,7 @@ #include "format_vgm_import.h" #include "ym2612_to_wopi.h" +#include "ym2151_to_wopi.h" #include "../common.h" #include @@ -35,7 +36,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]; @@ -82,6 +85,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 +125,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 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..ccde086 --- /dev/null +++ b/src/FileFormats/ym2151_to_wopi.h @@ -0,0 +1,46 @@ +/* + * 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 +#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.h b/src/FileFormats/ym2612_to_wopi.h index 11c577a..b08389b 100644 --- a/src/FileFormats/ym2612_to_wopi.h +++ b/src/FileFormats/ym2612_to_wopi.h @@ -41,6 +41,8 @@ class RawYm2612ToWopi bool m_dacOn = false; std::shared_ptr m_insdata; + friend class RawYm2151ToWopi; // instrument sharing + public: RawYm2612ToWopi(); void reset(); From ede18e9aa67f9a10115ae290301009062a1bd46f Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Thu, 28 Mar 2019 23:34:06 +0100 Subject: [PATCH 3/5] fix support of vgm files --- src/FileFormats/format_vgm_import.cpp | 113 ++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/src/FileFormats/format_vgm_import.cpp b/src/FileFormats/format_vgm_import.cpp index 6474e66..22506b0 100644 --- a/src/FileFormats/format_vgm_import.cpp +++ b/src/FileFormats/format_vgm_import.cpp @@ -24,6 +24,9 @@ #include #include #include +#include + +static void make_size_table(uint8_t *table, unsigned version); const char magic_vgm[4] = {0x56, 0x67, 0x6D, 0x20}; @@ -54,6 +57,14 @@ FfmtErrCode VGM_Importer::loadFile(QString filePath, FmBank &bank) if(memcmp(magic, magic_vgm, 4) != 0) return FfmtErrCode::ERR_BADFORMAT; + 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); + file.seek(0x34); file.read(char_p(numb), 4); @@ -74,6 +85,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); @@ -196,3 +219,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 +} From 13129c1892c2061a66104a6d8a64ac94015b8273 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Thu, 28 Mar 2019 23:48:41 +0100 Subject: [PATCH 4/5] fix includes which are missing --- src/FileFormats/ym2151_to_wopi.h | 2 -- src/FileFormats/ym2612_to_wopi.h | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/FileFormats/ym2151_to_wopi.h b/src/FileFormats/ym2151_to_wopi.h index ccde086..4ffe025 100644 --- a/src/FileFormats/ym2151_to_wopi.h +++ b/src/FileFormats/ym2151_to_wopi.h @@ -21,8 +21,6 @@ #include #include -#include -#include #include "../bank.h" #include "ym2612_to_wopi.h" // instrument sharing diff --git a/src/FileFormats/ym2612_to_wopi.h b/src/FileFormats/ym2612_to_wopi.h index b08389b..3bcf445 100644 --- a/src/FileFormats/ym2612_to_wopi.h +++ b/src/FileFormats/ym2612_to_wopi.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "../bank.h" From 9b40a64bb7e4206c0fee6ceb725cb4291d8eae49 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Fri, 29 Mar 2019 00:41:05 +0100 Subject: [PATCH 5/5] support loading VGZ files with zlib --- CMakeLists.txt | 5 +- FMBankEdit.pro | 1 + src/FileFormats/format_vgm_import.cpp | 83 ++++++++++++++++++++++++--- src/FileFormats/format_vgm_import.h | 5 ++ 4 files changed, 85 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f4a85d..0ed5424 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ find_package(Qt5Concurrent REQUIRED) find_package(Qt5LinguistTools REQUIRED) set(QWT_NAMES qwt-qt5 qwt) # find versions for Qt5 only find_package(Qwt) +find_package(ZLIB REQUIRED) find_package(Threads REQUIRED) if(QWT_FOUND) @@ -86,8 +87,8 @@ set(FILEFORMATS_SOURCES "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) +target_include_directories(FileFormats PUBLIC "src" PRIVATE ${ZLIB_INCLUDE_DIRS}) +target_link_libraries(FileFormats PUBLIC Common PRIVATE ${ZLIB_LIBRARIES}) include(src/opl/chips/chipset.cmake) add_library(Chips STATIC ${CHIPS_SOURCES}) diff --git a/FMBankEdit.pro b/FMBankEdit.pro index 18e35a9..62e6ae7 100644 --- a/FMBankEdit.pro +++ b/FMBankEdit.pro @@ -45,6 +45,7 @@ win32 { CONFIG += rtmidi CONFIG += rtaudio #CONFIG += plots +LIBS += -lz !macx:{ QMAKE_CXXFLAGS += -fopenmp diff --git a/src/FileFormats/format_vgm_import.cpp b/src/FileFormats/format_vgm_import.cpp index 22506b0..6edba0a 100644 --- a/src/FileFormats/format_vgm_import.cpp +++ b/src/FileFormats/format_vgm_import.cpp @@ -23,19 +23,83 @@ #include #include +#include +#include #include #include static void make_size_table(uint8_t *table, unsigned version); +static gzFile gzopen_q(const QString &path, const char *mode); const char magic_vgm[4] = {0x56, 0x67, 0x6D, 0x20}; +const unsigned char magic_gzip[2] = {0x1F, 0x8B}; -bool VGM_Importer::detect(const QString &, char *magic) +bool VGM_Importer::detect(const QString &filePath, char *magic) { - return (memcmp(magic_vgm, magic, 4) == 0); + if(memcmp(magic_vgm, magic, 4) == 0) + return true; + + //Try as compressed VGM file + if(memcmp(magic_gzip, magic, 2) == 0) + { + gzFile vgz = gzopen_q(filePath, "rb"); + if(!vgz) + return false; + + char compMagic[4]; + int count = (int)gzread(vgz, compMagic, 4); + gzclose(vgz); + if(count == 4 && memcmp(compMagic, magic_vgm, 4) == 0) + return true; + } + + return false; } FfmtErrCode VGM_Importer::loadFile(QString filePath, FmBank &bank) +{ + QFile file(filePath); + if(!file.open(QIODevice::ReadOnly)) + return FfmtErrCode::ERR_NOFILE; + + char magic[4]; + if(file.read(magic, 4) != 4) + return FfmtErrCode::ERR_BADFORMAT; + + if(memcmp(magic_vgm, magic, 4) == 0) + { + file.seek(0); + return load(file, bank); + } + + file.close(); + + //Try as compressed VGM file + if(memcmp(magic_gzip, magic, 2) == 0) + { + gzFile vgz = gzopen_q(filePath, "rb"); + if(!vgz) + return FfmtErrCode::ERR_NOFILE; + + QBuffer buffer; + buffer.open(QBuffer::ReadWrite); + + int n; + char buf[1024]; + while((n = (int)gzread(vgz, buf, sizeof(buf))) > 0) + buffer.write(buf, n); + if(n == -1) + return FfmtErrCode::ERR_BADFORMAT; + gzclose(vgz); + + buffer.open(QIODevice::ReadOnly); + return load(buffer, bank); + } + + return FfmtErrCode::ERR_BADFORMAT; +} + +FfmtErrCode VGM_Importer::load(QIODevice &file, FmBank &bank) { RawYm2612ToWopi pseudoChip; RawYm2612ToWopi pseudoChip2608; @@ -46,10 +110,6 @@ FfmtErrCode VGM_Importer::loadFile(QString filePath, FmBank &bank) char magic[4]; uint8_t numb[4]; - QFile file(filePath); - if(!file.open(QIODevice::ReadOnly)) - return FfmtErrCode::ERR_NOFILE; - bank.reset(); if(file.read(magic, 4) != 4) return FfmtErrCode::ERR_BADFORMAT; @@ -212,7 +272,7 @@ QString VGM_Importer::formatModuleName() const QString VGM_Importer::formatExtensionMask() const { - return "*.vgm"; + return "*.vgm *.vgz"; } BankFormats VGM_Importer::formatId() const @@ -309,3 +369,12 @@ static void make_size_table(uint8_t *table, unsigned version) for(unsigned a = 0xE2; a <= 0xFF; ++a) table[a] = 4; // three operands, reserved for future use } + +static gzFile gzopen_q(const QString &path, const char *mode) +{ +#ifndef _WIN32 + return gzopen(path.toStdString().c_str(), mode); +#else + return gzopen_w(path.toStdWString().c_str(), mode); +#endif +} diff --git a/src/FileFormats/format_vgm_import.h b/src/FileFormats/format_vgm_import.h index b07adf6..2cbe4c4 100644 --- a/src/FileFormats/format_vgm_import.h +++ b/src/FileFormats/format_vgm_import.h @@ -21,6 +21,8 @@ #include "ffmt_base.h" +class QIODevice; + /** * @brief Import from VGM files */ @@ -34,6 +36,9 @@ class VGM_Importer final : public FmBankFormatBase QString formatModuleName() const override; QString formatExtensionMask() const override; BankFormats formatId() const override; + +private: + FfmtErrCode load(QIODevice &file, FmBank &bank); }; #endif // VGM_IMPORT_H