Skip to content

Commit

Permalink
Merge pull request vgmtrans#87 from ElectronBlast/akaosnes-drumkit-su…
Browse files Browse the repository at this point in the history
…pport

Added drumkit/percussion support to the AkaoSnes format.
  • Loading branch information
mikelow authored Feb 19, 2017
2 parents b0e71fd + 2a74c4a commit 1bbdb97
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 18 deletions.
13 changes: 12 additions & 1 deletion src/main/SF2File.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,18 @@ SF2File::SF2File(SynthFile *synthfile)
memset(&presetHdr, 0, sizeof(sfPresetHeader));
memcpy(presetHdr.achPresetName, instr->name.c_str(), min((unsigned long) instr->name.length(), (unsigned long) 20));
presetHdr.wPreset = (uint16_t) instr->ulInstrument;
presetHdr.wBank = (uint16_t) instr->ulBank;

// Despite being a 16-bit value, SF2 only supports banks up to 127. Since
// it's pretty common to have either MSB or LSB be 0, we'll use whatever
// one is not zero, with preference for MSB.
uint16_t bank16 = (uint16_t)instr->ulBank;

if ((bank16 & 0xFF00) == 0) {
presetHdr.wBank = bank16 & 0x7F;
}
else {
presetHdr.wBank = (bank16 >> 8) & 0x7F;
}
presetHdr.wPresetBagNdx = (uint16_t) i;
presetHdr.dwLibrary = 0;
presetHdr.dwGenre = 0;
Expand Down
169 changes: 160 additions & 9 deletions src/main/formats/AkaoSnesInstr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ AkaoSnesInstrSet::AkaoSnesInstrSet(RawFile *file,
uint32_t spcDirAddr,
uint16_t addrTuningTable,
uint16_t addrADSRTable,
uint16_t addrDrumKitTable,
const std::wstring &name) :
VGMInstrSet(AkaoSnesFormat::name, file, addrTuningTable, 0, name), version(ver),
spcDirAddr(spcDirAddr),
addrTuningTable(addrTuningTable),
addrADSRTable(addrADSRTable) {
addrADSRTable(addrADSRTable),
addrDrumKitTable(addrDrumKitTable) {
}

AkaoSnesInstrSet::~AkaoSnesInstrSet() {
Expand All @@ -35,7 +37,18 @@ bool AkaoSnesInstrSet::GetInstrPointers() {
}

uint16_t addrSampStart = GetShort(addrDIRentry);
if (addrSampStart < spcDirAddr) {
uint16_t instrumentMinOffset;

if (version == AKAOSNES_V4) {
// Some instruments can be placed before the DIR table. At least a few
// songs from Chrono Trigger ("World Revolution", "Last Battle") will do
// this.
instrumentMinOffset = 0x200;
}
else {
instrumentMinOffset = spcDirAddr;
}
if (addrSampStart < instrumentMinOffset) {
continue;
}

Expand Down Expand Up @@ -73,6 +86,12 @@ bool AkaoSnesInstrSet::GetInstrPointers() {
return false;
}

if (addrDrumKitTable) {
// One percussion instrument covers all percussion sounds
AkaoSnesDrumKit *newDrumKitInstr = new AkaoSnesDrumKit(this, version, DRUMKIT_PROGRAM, spcDirAddr, addrTuningTable, addrADSRTable, addrDrumKitTable, L"Drum Kit");
aInstrs.push_back(newDrumKitInstr);
}

std::sort(usedSRCNs.begin(), usedSRCNs.end());
SNESSampColl *newSampColl = new SNESSampColl(AkaoSnesFormat::name, this->rawfile, spcDirAddr, usedSRCNs);
if (!newSampColl->LoadVGMFile()) {
Expand Down Expand Up @@ -111,26 +130,107 @@ bool AkaoSnesInstr::LoadInstr() {

uint16_t addrSampStart = GetShort(offDirEnt);

AkaoSnesRgn *rgn = new AkaoSnesRgn(this, version, instrNum, spcDirAddr, addrTuningTable, addrADSRTable);
AkaoSnesRgn *rgn = new AkaoSnesRgn(this, version, addrTuningTable);
rgn->sampOffset = addrSampStart - spcDirAddr;
if (!rgn->InitializeRegion(instrNum, spcDirAddr, addrADSRTable) || !rgn->LoadRgn()) {
delete rgn;
return false;
}
aRgns.push_back(rgn);

SetGuessedLength();
return true;
}

// *************
// AkaoSnesDrumKit
// *************

AkaoSnesDrumKit::AkaoSnesDrumKit(VGMInstrSet *instrSet,
AkaoSnesVersion ver,
uint32_t programNum,
uint32_t spcDirAddr,
uint16_t addrTuningTable,
uint16_t addrADSRTable,
uint16_t addrDrumKitTable,
const std::wstring &name) :
VGMInstr(instrSet, addrTuningTable, 0, ((programNum >> 6) & 0x7F00) | ((programNum >> 7) & 0x7F), programNum & 0xFF, name), version(ver),
spcDirAddr(spcDirAddr),
addrTuningTable(addrTuningTable),
addrADSRTable(addrADSRTable),
addrDrumKitTable(addrDrumKitTable) {
}

AkaoSnesDrumKit::~AkaoSnesDrumKit() {
}

bool AkaoSnesDrumKit::LoadInstr() {
uint32_t offDirEnt = spcDirAddr + (instrNum * 4);
if (offDirEnt + 4 > 0x10000) {
return false;
}

uint16_t addrSampStart = GetShort(offDirEnt);

uint8_t NOTE_DUR_TABLE_SIZE;
switch (version) {
case AKAOSNES_V1:
case AKAOSNES_V2:
case AKAOSNES_V3:
NOTE_DUR_TABLE_SIZE = 15;
break;

default:
NOTE_DUR_TABLE_SIZE = 14;
break;
}

// A new region for every instrument
for (uint8_t i = 0; i < NOTE_DUR_TABLE_SIZE; ++i) {
AkaoSnesDrumKitRgn *rgn = new AkaoSnesDrumKitRgn(this, version, addrTuningTable);

if (!rgn->InitializePercussionRegion(i, spcDirAddr, addrADSRTable, addrDrumKitTable)) {
delete rgn;
continue;
}
if (!rgn->LoadRgn()) {
delete rgn;
return false;
}

uint32_t offDirEnt = spcDirAddr + (rgn->sampNum * 4);
if (offDirEnt + 4 > 0x10000) {
delete rgn;
return false;
}

uint16_t addrSampStart = GetShort(offDirEnt);

rgn->sampOffset = addrSampStart - spcDirAddr;

aRgns.push_back(rgn);
}

SetGuessedLength();
return true;
}

// ***********
// AkaoSnesRgn
// ***********

AkaoSnesRgn::AkaoSnesRgn(AkaoSnesInstr *instr,
AkaoSnesRgn::AkaoSnesRgn(VGMInstr *instr,
AkaoSnesVersion ver,
uint8_t srcn,
uint32_t spcDirAddr,
uint16_t addrTuningTable,
uint16_t addrADSRTable) :
uint16_t addrTuningTable) :
VGMRgn(instr, addrTuningTable, 0),
version(ver) {
}

bool AkaoSnesRgn::InitializeRegion(uint8_t srcn,
uint32_t spcDirAddr,
uint16_t addrADSRTable)
{
uint16_t addrTuningTable = dwOffset;
uint8_t adsr1;
uint8_t adsr2;
if (version == AKAOSNES_V1) {
Expand Down Expand Up @@ -188,12 +288,63 @@ AkaoSnesRgn::AkaoSnesRgn(AkaoSnesInstr *instr,
fineTune = (int16_t) (fine_tuning * 100.0);
SNESConvADSR<VGMRgn>(this, adsr1, adsr2, 0xa0);

SetGuessedLength();
return true;
}

AkaoSnesRgn::~AkaoSnesRgn() {
}

bool AkaoSnesRgn::LoadRgn() {
SetGuessedLength();

return true;
}

// ***********
// AkaoSnesDrumKitRgn
// ***********

AkaoSnesDrumKitRgn::AkaoSnesDrumKitRgn(AkaoSnesDrumKit *instr,
AkaoSnesVersion ver,
uint16_t addrTuningTable) :
AkaoSnesRgn(instr, ver, addrTuningTable) {
}

bool AkaoSnesDrumKitRgn::InitializePercussionRegion(uint8_t percussionIndex,
uint32_t spcDirAddr,
uint16_t addrADSRTable,
uint16_t addrDrumKitTable)
{
wostringstream newName;

newName << L"Drum " << percussionIndex;
name = newName.str();

uint32_t srcnOffset = addrDrumKitTable + percussionIndex * 3;
uint32_t keyOffset = srcnOffset + 1;
uint32_t panOffset = srcnOffset + 2;

uint8_t instrumentIndex = GetByte(srcnOffset);

if (instrumentIndex == 0 || instrumentIndex == 0xFF || !InitializeRegion(instrumentIndex, spcDirAddr, addrADSRTable)) {
return false;
}

keyLow = keyHigh = percussionIndex + KEY_BIAS;

// sampNum is absolute key to play
unityKey = (unityKey + KEY_BIAS) - GetByte(keyOffset) + percussionIndex;

AddSampNum(sampNum, srcnOffset);
AddSimpleItem(keyOffset, 1, L"Key");

uint8_t panValue = GetByte(panOffset);
if (panValue < 0x80) {
AddPan(panValue, panOffset);
}

return true;
}

AkaoSnesDrumKitRgn::~AkaoSnesDrumKitRgn() {
}
69 changes: 64 additions & 5 deletions src/main/formats/AkaoSnesInstr.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
class AkaoSnesInstrSet:
public VGMInstrSet {
public:
static const uint32_t DRUMKIT_PROGRAM = (0x7F << 14);

AkaoSnesInstrSet(RawFile *file,
AkaoSnesVersion ver,
uint32_t spcDirAddr,
uint16_t addrTuningTable,
uint16_t addrADSRTable,
uint16_t addrDrumKitTable,
const std::wstring &name = L"AkaoSnesInstrSet");
virtual ~AkaoSnesInstrSet(void);

Expand All @@ -28,6 +31,7 @@ class AkaoSnesInstrSet:
uint32_t spcDirAddr;
uint16_t addrTuningTable;
uint16_t addrADSRTable;
uint16_t addrDrumKitTable;
std::vector<uint8_t> usedSRCNs;
};

Expand Down Expand Up @@ -57,22 +61,77 @@ class AkaoSnesInstr
uint16_t addrADSRTable;
};

// *************
// AkaoSnesDrumKit
// *************

class AkaoSnesDrumKit
: public VGMInstr {
public:
AkaoSnesDrumKit(VGMInstrSet *instrSet,
AkaoSnesVersion ver,
uint32_t programNum,
uint32_t spcDirAddr,
uint16_t addrTuningTable,
uint16_t addrADSRTable,
uint16_t addrDrumKitTable,
const std::wstring &name = L"AkaoSnesDrumKit");
virtual ~AkaoSnesDrumKit(void);

virtual bool LoadInstr();

AkaoSnesVersion version;

protected:
uint32_t spcDirAddr;
uint16_t addrTuningTable;
uint16_t addrADSRTable;
uint16_t addrDrumKitTable;
};

// ***********
// AkaoSnesRgn
// ***********

class AkaoSnesRgn
: public VGMRgn {
public:
AkaoSnesRgn(AkaoSnesInstr *instr,
AkaoSnesRgn(VGMInstr *instr,
AkaoSnesVersion ver,
uint8_t srcn,
uint32_t spcDirAddr,
uint16_t addrTuningTable,
uint16_t addrADSRTable);
uint16_t addrTuningTable);
virtual ~AkaoSnesRgn(void);

bool InitializeRegion(uint8_t srcn,
uint32_t spcDirAddr,
uint16_t addrADSRTable);
virtual bool LoadRgn();

AkaoSnesVersion version;
};

// ***********
// AkaoSnesDrumKitRgn
// ***********

class AkaoSnesDrumKitRgn
: public AkaoSnesRgn {
public:
// We need some space to move for unityKey, since we might need it to go
// less than the current note, so we add this to all the percussion notes.
// This value can be anything from 0 to 127, but being near the middle of
// the range gives it a decent chance of not going out of range in either
// direction.
static const uint8_t KEY_BIAS = 60;

AkaoSnesDrumKitRgn(AkaoSnesDrumKit *instr,
AkaoSnesVersion ver,
uint16_t addrTuningTable);
virtual ~AkaoSnesDrumKitRgn(void);

bool InitializePercussionRegion(uint8_t srcn,
uint32_t spcDirAddr,
uint16_t addrADSRTable,
uint16_t addrDrumKitTable);

uint8_t srcn;
};
Loading

0 comments on commit 1bbdb97

Please sign in to comment.