Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

YM2151 raw importer #74

Merged
merged 5 commits into from
Apr 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 5 additions & 2 deletions FMBankEdit.pro
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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 \
Expand All @@ -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 \
Expand Down
2 changes: 1 addition & 1 deletion src/FileFormats/format_gym_import.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/

#include "format_gym_import.h"
#include "ym2612_to_wopi.hpp"
#include "ym2612_to_wopi.h"
#include "../common.h"

#include <QSet>
Expand Down
2 changes: 1 addition & 1 deletion src/FileFormats/format_s98_import.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/

#include "format_s98_import.h"
#include "ym2612_to_wopi.hpp"
#include "ym2612_to_wopi.h"
#include "../common.h"
#include <memory>

Expand Down
138 changes: 131 additions & 7 deletions src/FileFormats/format_vgm_import.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <QSet>
#include <QByteArray>
#include <algorithm>
#include <cstring>

static void make_size_table(uint8_t *table, unsigned version);

const char magic_vgm[4] = {0x56, 0x67, 0x6D, 0x20};

Expand All @@ -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];
Expand All @@ -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();

Expand All @@ -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(&reg), 1);
file.read(char_p(&val), 1);
Expand All @@ -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(&reg), 1);
file.read(char_p(&val), 1);
pseudoChip2151.passReg(reg, val);
break;

case 0x56://Write YM2608 port 0
file.read(char_p(&reg), 1);
file.read(char_p(&val), 1);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
144 changes: 144 additions & 0 deletions src/FileFormats/ym2151_to_wopi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* OPN2 Bank Editor by Wohlstand, a free tool for music bank editing
* Copyright (c) 2017-2019 Vitaly Novichkov <[email protected]>
*
* 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 <http://www.gnu.org/licenses/>.
*/

#include "ym2151_to_wopi.h"
#include <cstring>

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<FmBank::Instrument> &RawYm2151ToWopi::caughtInstruments()
{
return m_insdata->caughtInstruments;
}
Loading