ZSM export: move to ROM export framework
This commit is contained in:
parent
875ef08256
commit
39923742ab
|
@ -717,8 +717,6 @@ src/engine/wavetable.cpp
|
||||||
src/engine/waveSynth.cpp
|
src/engine/waveSynth.cpp
|
||||||
src/engine/wavOps.cpp
|
src/engine/wavOps.cpp
|
||||||
src/engine/vgmOps.cpp
|
src/engine/vgmOps.cpp
|
||||||
src/engine/zsmOps.cpp
|
|
||||||
src/engine/zsm.cpp
|
|
||||||
|
|
||||||
src/engine/platform/abstract.cpp
|
src/engine/platform/abstract.cpp
|
||||||
src/engine/platform/genesis.cpp
|
src/engine/platform/genesis.cpp
|
||||||
|
@ -797,6 +795,7 @@ src/engine/platform/dummy.cpp
|
||||||
src/engine/export/abstract.cpp
|
src/engine/export/abstract.cpp
|
||||||
src/engine/export/amigaValidation.cpp
|
src/engine/export/amigaValidation.cpp
|
||||||
src/engine/export/tiuna.cpp
|
src/engine/export/tiuna.cpp
|
||||||
|
src/engine/export/zsm.cpp
|
||||||
|
|
||||||
src/engine/effect/abstract.cpp
|
src/engine/effect/abstract.cpp
|
||||||
src/engine/effect/dummy.cpp
|
src/engine/effect/dummy.cpp
|
||||||
|
|
|
@ -669,6 +669,7 @@ class DivEngine {
|
||||||
friend class DivROMExport;
|
friend class DivROMExport;
|
||||||
friend class DivExportAmigaValidation;
|
friend class DivExportAmigaValidation;
|
||||||
friend class DivExportTiuna;
|
friend class DivExportTiuna;
|
||||||
|
friend class DivExportZSM;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DivSong song;
|
DivSong song;
|
||||||
|
@ -721,8 +722,6 @@ class DivEngine {
|
||||||
// - -1 to auto-determine trailing
|
// - -1 to auto-determine trailing
|
||||||
// - -2 to add a whole loop of trailing
|
// - -2 to add a whole loop of trailing
|
||||||
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false, int trailingTicks=-1);
|
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false, int trailingTicks=-1);
|
||||||
// dump to ZSM.
|
|
||||||
SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true, bool optimize=true);
|
|
||||||
// dump to TIunA.
|
// dump to TIunA.
|
||||||
SafeWriter* saveTiuna(const bool* sysToExport, const char* baseLabel, int firstBankSize, int otherBankSize);
|
SafeWriter* saveTiuna(const bool* sysToExport, const char* baseLabel, int firstBankSize, int otherBankSize);
|
||||||
// dump command stream.
|
// dump command stream.
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "export/amigaValidation.h"
|
#include "export/amigaValidation.h"
|
||||||
#include "export/tiuna.h"
|
#include "export/tiuna.h"
|
||||||
|
#include "export/zsm.h"
|
||||||
|
|
||||||
DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) {
|
DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) {
|
||||||
DivROMExport* exporter=NULL;
|
DivROMExport* exporter=NULL;
|
||||||
|
@ -31,6 +32,9 @@ DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) {
|
||||||
case DIV_ROM_TIUNA:
|
case DIV_ROM_TIUNA:
|
||||||
exporter=new DivExportTiuna;
|
exporter=new DivExportTiuna;
|
||||||
break;
|
break;
|
||||||
|
case DIV_ROM_ZSM:
|
||||||
|
exporter=new DivExportZSM;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
exporter=new DivROMExport;
|
exporter=new DivROMExport;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -18,9 +18,77 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "zsm.h"
|
#include "zsm.h"
|
||||||
|
#include "../engine.h"
|
||||||
#include "../ta-log.h"
|
#include "../ta-log.h"
|
||||||
#include "../utfutils.h"
|
#include <fmt/printf.h>
|
||||||
#include "song.h"
|
|
||||||
|
/// DivZSM definitions
|
||||||
|
|
||||||
|
#define ZSM_HEADER_SIZE 16
|
||||||
|
#define ZSM_VERSION 1
|
||||||
|
#define ZSM_YM_CMD 0x40
|
||||||
|
#define ZSM_DELAY_CMD 0x80
|
||||||
|
#define ZSM_YM_MAX_WRITES 63
|
||||||
|
#define ZSM_SYNC_MAX_WRITES 31
|
||||||
|
#define ZSM_DELAY_MAX 127
|
||||||
|
#define ZSM_EOF ZSM_DELAY_CMD
|
||||||
|
|
||||||
|
#define ZSM_EXT ZSM_YM_CMD
|
||||||
|
#define ZSM_EXT_PCM 0x00
|
||||||
|
#define ZSM_EXT_CHIP 0x40
|
||||||
|
#define ZSM_EXT_SYNC 0x80
|
||||||
|
#define ZSM_EXT_CUSTOM 0xC0
|
||||||
|
|
||||||
|
enum YM_STATE { ym_PREV, ym_NEW, ym_STATES };
|
||||||
|
enum PSG_STATE { psg_PREV, psg_NEW, psg_STATES };
|
||||||
|
|
||||||
|
class DivZSM {
|
||||||
|
private:
|
||||||
|
struct S_pcmInst {
|
||||||
|
int geometry;
|
||||||
|
unsigned int offset, length, loopPoint;
|
||||||
|
bool isLooped;
|
||||||
|
};
|
||||||
|
SafeWriter* w;
|
||||||
|
int ymState[ym_STATES][256];
|
||||||
|
int psgState[psg_STATES][64];
|
||||||
|
int pcmRateCache;
|
||||||
|
int pcmCtrlRVCache;
|
||||||
|
int pcmCtrlDCCache;
|
||||||
|
unsigned int pcmLoopPointCache;
|
||||||
|
bool pcmIsLooped;
|
||||||
|
std::vector<DivRegWrite> ymwrites;
|
||||||
|
std::vector<DivRegWrite> pcmMeta;
|
||||||
|
std::vector<unsigned char> pcmData;
|
||||||
|
std::vector<unsigned char> pcmCache;
|
||||||
|
std::vector<S_pcmInst> pcmInsts;
|
||||||
|
std::vector<DivRegWrite> syncCache;
|
||||||
|
int loopOffset;
|
||||||
|
int numWrites;
|
||||||
|
int ticks;
|
||||||
|
int tickRate;
|
||||||
|
int ymMask;
|
||||||
|
int psgMask;
|
||||||
|
bool optimize;
|
||||||
|
public:
|
||||||
|
DivZSM();
|
||||||
|
~DivZSM();
|
||||||
|
void init(unsigned int rate = 60);
|
||||||
|
int getoffset();
|
||||||
|
void writeYM(unsigned char a, unsigned char v);
|
||||||
|
void writePSG(unsigned char a, unsigned char v);
|
||||||
|
void writePCM(unsigned char a, unsigned char v);
|
||||||
|
void writeSync(unsigned char a, unsigned char v);
|
||||||
|
void setOptimize(bool o);
|
||||||
|
void tick(int numticks = 1);
|
||||||
|
void setLoopPoint();
|
||||||
|
SafeWriter* finish();
|
||||||
|
private:
|
||||||
|
void flushWrites();
|
||||||
|
void flushTicks();
|
||||||
|
};
|
||||||
|
|
||||||
|
/// DivZSM implementation
|
||||||
|
|
||||||
DivZSM::DivZSM() {
|
DivZSM::DivZSM() {
|
||||||
w=NULL;
|
w=NULL;
|
||||||
|
@ -447,3 +515,240 @@ void DivZSM::flushTicks() {
|
||||||
}
|
}
|
||||||
ticks=0;
|
ticks=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ZSM export
|
||||||
|
|
||||||
|
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
|
||||||
|
constexpr int MASTER_CLOCK_MASK=(sizeof(void*)==8)?0xff:0;
|
||||||
|
|
||||||
|
void DivExportZSM::run() {
|
||||||
|
// settings
|
||||||
|
unsigned int zsmrate=conf.getInt("zsmrate",60);
|
||||||
|
bool loop=conf.getBool("loop",true);
|
||||||
|
bool optimize=conf.getBool("optimize",true);
|
||||||
|
|
||||||
|
// system IDs
|
||||||
|
int VERA=-1;
|
||||||
|
int YM=-1;
|
||||||
|
int IGNORED=0;
|
||||||
|
|
||||||
|
// find indexes for YM and VERA. Ignore other systems.
|
||||||
|
for (int i=0; i<e->song.systemLen; i++) {
|
||||||
|
switch (e->song.system[i]) {
|
||||||
|
case DIV_SYSTEM_VERA:
|
||||||
|
if (VERA>=0) {
|
||||||
|
IGNORED++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
VERA=i;
|
||||||
|
logAppendf("VERA detected as chip id %d",i);
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_YM2151:
|
||||||
|
if (YM>=0) {
|
||||||
|
IGNORED++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
YM=i;
|
||||||
|
logAppendf("YM detected as chip id %d",i);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
IGNORED++;
|
||||||
|
logAppendf("Ignoring chip %d systemID %d",i,(int)e->song.system[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (VERA<0 && YM<0) {
|
||||||
|
logAppend("ERROR: No supported systems for ZSM");
|
||||||
|
failed=true;
|
||||||
|
running=false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (IGNORED>0) {
|
||||||
|
logAppendf("ZSM export ignoring %d unsupported system%c",IGNORED,IGNORED>1?'s':' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
DivZSM zsm;
|
||||||
|
|
||||||
|
e->stop();
|
||||||
|
e->repeatPattern=false;
|
||||||
|
e->setOrder(0);
|
||||||
|
e->synchronizedSoft([&]() {
|
||||||
|
double origRate=e->got.rate;
|
||||||
|
e->got.rate=zsmrate&0xffff;
|
||||||
|
|
||||||
|
// determine loop point
|
||||||
|
int loopOrder=0;
|
||||||
|
int loopRow=0;
|
||||||
|
int loopEnd=0;
|
||||||
|
e->walkSong(loopOrder,loopRow,loopEnd);
|
||||||
|
logAppendf("loop point: %d %d",loopOrder,loopRow);
|
||||||
|
|
||||||
|
zsm.init(zsmrate);
|
||||||
|
|
||||||
|
// reset the playback state
|
||||||
|
e->curOrder=0;
|
||||||
|
e->freelance=false;
|
||||||
|
e->playing=false;
|
||||||
|
e->extValuePresent=false;
|
||||||
|
e->remainingLoops=-1;
|
||||||
|
|
||||||
|
// Prepare to write song data
|
||||||
|
e->playSub(false);
|
||||||
|
//size_t tickCount=0;
|
||||||
|
bool done=false;
|
||||||
|
bool loopNow=false;
|
||||||
|
int loopPos=-1;
|
||||||
|
int fracWait=0; // accumulates fractional ticks
|
||||||
|
if (VERA>=0) e->disCont[VERA].dispatch->toggleRegisterDump(true);
|
||||||
|
if (YM>=0) {
|
||||||
|
e->disCont[YM].dispatch->toggleRegisterDump(true);
|
||||||
|
// emit LFO initialization commands
|
||||||
|
zsm.writeYM(0x18,0); // freq=0
|
||||||
|
zsm.writeYM(0x19,0x7F); // AMD =7F
|
||||||
|
zsm.writeYM(0x19,0xFF); // PMD =7F
|
||||||
|
// TODO: incorporate the Furnace meta-command for init data and filter
|
||||||
|
// out writes to otherwise-unused channels.
|
||||||
|
}
|
||||||
|
// Indicate the song's tuning as a sync meta-event
|
||||||
|
// specified in terms of how many 1/256th semitones
|
||||||
|
// the song is offset from standard A-440 tuning.
|
||||||
|
// This is mainly to benefit visualizations in players
|
||||||
|
// for non-standard tunings so that they can avoid
|
||||||
|
// displaying the entire song held in pitch bend.
|
||||||
|
// Tunings offsets that exceed a half semitone
|
||||||
|
// will simply be represented in a different key
|
||||||
|
// by nature of overflowing the signed char value
|
||||||
|
signed char tuningoffset=(signed char)(round(3072*(log(e->song.tuning/440.0)/log(2))))&0xff;
|
||||||
|
zsm.writeSync(0x01,tuningoffset);
|
||||||
|
// Set optimize flag, which mainly buffers PSG writes
|
||||||
|
// whenever the channel is silent
|
||||||
|
zsm.setOptimize(optimize);
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
if (loopPos==-1) {
|
||||||
|
if (loopOrder==e->curOrder && loopRow==e->curRow && loop)
|
||||||
|
loopNow=true;
|
||||||
|
if (loopNow) {
|
||||||
|
// If Virtual Tempo is in use, our exact loop point
|
||||||
|
// might be skipped due to quantization error.
|
||||||
|
// If this happens, the tick immediately following is our loop point.
|
||||||
|
if (e->ticks==1 || !(loopOrder==e->curOrder && loopRow==e->curRow)) {
|
||||||
|
loopPos=zsm.getoffset();
|
||||||
|
zsm.setLoopPoint();
|
||||||
|
loopNow=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e->nextTick() || !e->playing) {
|
||||||
|
done=true;
|
||||||
|
if (!loop) {
|
||||||
|
for (int i=0; i<e->song.systemLen; i++) {
|
||||||
|
e->disCont[i].dispatch->getRegisterWrites().clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!e->playing) {
|
||||||
|
loopPos=-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// get register dumps
|
||||||
|
for (int j=0; j<2; j++) {
|
||||||
|
int i=0;
|
||||||
|
// dump YM writes first
|
||||||
|
if (j==0) {
|
||||||
|
if (YM<0) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
i=YM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// dump VERA writes second
|
||||||
|
if (j==1) {
|
||||||
|
if (VERA<0) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
i=VERA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<DivRegWrite>& writes=e->disCont[i].dispatch->getRegisterWrites();
|
||||||
|
if (writes.size()>0)
|
||||||
|
logD("zsmOps: Writing %d messages to chip %d",writes.size(),i);
|
||||||
|
for (DivRegWrite& write: writes) {
|
||||||
|
if (i==YM) zsm.writeYM(write.addr&0xff,write.val);
|
||||||
|
if (i==VERA) {
|
||||||
|
if (done && write.addr>=64) continue; // don't process any PCM or sync events on the loop lookahead
|
||||||
|
zsm.writePSG(write.addr&0xff,write.val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// write wait
|
||||||
|
int totalWait=e->cycles>>MASTER_CLOCK_PREC;
|
||||||
|
fracWait+=e->cycles&MASTER_CLOCK_MASK;
|
||||||
|
totalWait+=fracWait>>MASTER_CLOCK_PREC;
|
||||||
|
fracWait&=MASTER_CLOCK_MASK;
|
||||||
|
if (totalWait>0 && !done) {
|
||||||
|
zsm.tick(totalWait);
|
||||||
|
//tickCount+=totalWait;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end of song
|
||||||
|
|
||||||
|
// done - close out.
|
||||||
|
e->got.rate=origRate;
|
||||||
|
if (VERA>=0) e->disCont[VERA].dispatch->toggleRegisterDump(false);
|
||||||
|
if (YM>=0) e->disCont[YM].dispatch->toggleRegisterDump(false);
|
||||||
|
|
||||||
|
e->remainingLoops=-1;
|
||||||
|
e->playing=false;
|
||||||
|
e->freelance=false;
|
||||||
|
e->extValuePresent=false;
|
||||||
|
});
|
||||||
|
|
||||||
|
progress[0].amount=1.0f;
|
||||||
|
|
||||||
|
logAppend("finished!");
|
||||||
|
|
||||||
|
output.push_back(DivROMExportOutput("out.zsm",zsm.finish()));
|
||||||
|
running=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DivExpottZSM - FRONTEND
|
||||||
|
|
||||||
|
bool DivExportZSM::go(DivEngine* eng) {
|
||||||
|
progress[0].name="Generate";
|
||||||
|
progress[0].amount=0.0f;
|
||||||
|
|
||||||
|
e=eng;
|
||||||
|
running=true;
|
||||||
|
failed=false;
|
||||||
|
mustAbort=false;
|
||||||
|
exportThread=new std::thread(&DivExportZSM::run,this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivExportZSM::wait() {
|
||||||
|
if (exportThread!=NULL) {
|
||||||
|
exportThread->join();
|
||||||
|
delete exportThread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivExportZSM::abort() {
|
||||||
|
mustAbort=true;
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivExportZSM::isRunning() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivExportZSM::hasFailed() {
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
DivROMExportProgress DivExportZSM::getProgress(int index) {
|
||||||
|
if (index<0 || index>1) return progress[1];
|
||||||
|
return progress[index];
|
||||||
|
}
|
38
src/engine/export/zsm.h
Normal file
38
src/engine/export/zsm.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2024 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* 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 2 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../export.h"
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class DivExportZSM: public DivROMExport {
|
||||||
|
DivEngine* e;
|
||||||
|
std::thread* exportThread;
|
||||||
|
DivROMExportProgress progress[2];
|
||||||
|
bool running, failed, mustAbort;
|
||||||
|
void run();
|
||||||
|
public:
|
||||||
|
bool go(DivEngine* e);
|
||||||
|
bool isRunning();
|
||||||
|
bool hasFailed();
|
||||||
|
void abort();
|
||||||
|
void wait();
|
||||||
|
DivROMExportProgress getProgress(int index=0);
|
||||||
|
~DivExportZSM() {}
|
||||||
|
};
|
|
@ -1,92 +0,0 @@
|
||||||
/**
|
|
||||||
* Furnace Tracker - multi-system chiptune tracker
|
|
||||||
* Copyright (C) 2021-2024 tildearrow and contributors
|
|
||||||
*
|
|
||||||
* 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 2 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _ZSM_H
|
|
||||||
#define _ZSM_H
|
|
||||||
|
|
||||||
//#include "engine.h"
|
|
||||||
#include "safeWriter.h"
|
|
||||||
#include "dispatch.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#define ZSM_HEADER_SIZE 16
|
|
||||||
#define ZSM_VERSION 1
|
|
||||||
#define ZSM_YM_CMD 0x40
|
|
||||||
#define ZSM_DELAY_CMD 0x80
|
|
||||||
#define ZSM_YM_MAX_WRITES 63
|
|
||||||
#define ZSM_SYNC_MAX_WRITES 31
|
|
||||||
#define ZSM_DELAY_MAX 127
|
|
||||||
#define ZSM_EOF ZSM_DELAY_CMD
|
|
||||||
|
|
||||||
#define ZSM_EXT ZSM_YM_CMD
|
|
||||||
#define ZSM_EXT_PCM 0x00
|
|
||||||
#define ZSM_EXT_CHIP 0x40
|
|
||||||
#define ZSM_EXT_SYNC 0x80
|
|
||||||
#define ZSM_EXT_CUSTOM 0xC0
|
|
||||||
|
|
||||||
enum YM_STATE { ym_PREV, ym_NEW, ym_STATES };
|
|
||||||
enum PSG_STATE { psg_PREV, psg_NEW, psg_STATES };
|
|
||||||
|
|
||||||
class DivZSM {
|
|
||||||
private:
|
|
||||||
struct S_pcmInst {
|
|
||||||
int geometry;
|
|
||||||
unsigned int offset, length, loopPoint;
|
|
||||||
bool isLooped;
|
|
||||||
};
|
|
||||||
SafeWriter* w;
|
|
||||||
int ymState[ym_STATES][256];
|
|
||||||
int psgState[psg_STATES][64];
|
|
||||||
int pcmRateCache;
|
|
||||||
int pcmCtrlRVCache;
|
|
||||||
int pcmCtrlDCCache;
|
|
||||||
unsigned int pcmLoopPointCache;
|
|
||||||
bool pcmIsLooped;
|
|
||||||
std::vector<DivRegWrite> ymwrites;
|
|
||||||
std::vector<DivRegWrite> pcmMeta;
|
|
||||||
std::vector<unsigned char> pcmData;
|
|
||||||
std::vector<unsigned char> pcmCache;
|
|
||||||
std::vector<S_pcmInst> pcmInsts;
|
|
||||||
std::vector<DivRegWrite> syncCache;
|
|
||||||
int loopOffset;
|
|
||||||
int numWrites;
|
|
||||||
int ticks;
|
|
||||||
int tickRate;
|
|
||||||
int ymMask;
|
|
||||||
int psgMask;
|
|
||||||
bool optimize;
|
|
||||||
public:
|
|
||||||
DivZSM();
|
|
||||||
~DivZSM();
|
|
||||||
void init(unsigned int rate = 60);
|
|
||||||
int getoffset();
|
|
||||||
void writeYM(unsigned char a, unsigned char v);
|
|
||||||
void writePSG(unsigned char a, unsigned char v);
|
|
||||||
void writePCM(unsigned char a, unsigned char v);
|
|
||||||
void writeSync(unsigned char a, unsigned char v);
|
|
||||||
void setOptimize(bool o);
|
|
||||||
void tick(int numticks = 1);
|
|
||||||
void setLoopPoint();
|
|
||||||
SafeWriter* finish();
|
|
||||||
private:
|
|
||||||
void flushWrites();
|
|
||||||
void flushTicks();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,208 +0,0 @@
|
||||||
/**
|
|
||||||
* Furnace Tracker - multi-system chiptune tracker
|
|
||||||
* Copyright (C) 2021-2024 tildearrow and contributors
|
|
||||||
*
|
|
||||||
* 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 2 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "engine.h"
|
|
||||||
#include "../ta-log.h"
|
|
||||||
#include "../utfutils.h"
|
|
||||||
#include "song.h"
|
|
||||||
#include "zsm.h"
|
|
||||||
|
|
||||||
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
|
|
||||||
constexpr int MASTER_CLOCK_MASK=(sizeof(void*)==8)?0xff:0;
|
|
||||||
|
|
||||||
SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop, bool optimize) {
|
|
||||||
int VERA=-1;
|
|
||||||
int YM=-1;
|
|
||||||
int IGNORED=0;
|
|
||||||
|
|
||||||
// find indexes for YM and VERA. Ignore other systems.
|
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
|
||||||
switch (song.system[i]) {
|
|
||||||
case DIV_SYSTEM_VERA:
|
|
||||||
if (VERA>=0) {
|
|
||||||
IGNORED++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
VERA=i;
|
|
||||||
logD("VERA detected as chip id %d",i);
|
|
||||||
break;
|
|
||||||
case DIV_SYSTEM_YM2151:
|
|
||||||
if (YM>=0) {
|
|
||||||
IGNORED++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
YM=i;
|
|
||||||
logD("YM detected as chip id %d",i);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
IGNORED++;
|
|
||||||
logD("Ignoring chip %d systemID %d",i,(int)song.system[i]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (VERA<0 && YM<0) {
|
|
||||||
logE("No supported systems for ZSM");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (IGNORED>0) {
|
|
||||||
logW("ZSM export ignoring %d unsupported system%c",IGNORED,IGNORED>1?'s':' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
stop();
|
|
||||||
repeatPattern=false;
|
|
||||||
setOrder(0);
|
|
||||||
BUSY_BEGIN_SOFT;
|
|
||||||
|
|
||||||
double origRate=got.rate;
|
|
||||||
got.rate=zsmrate&0xffff;
|
|
||||||
|
|
||||||
// determine loop point
|
|
||||||
int loopOrder=0;
|
|
||||||
int loopRow=0;
|
|
||||||
int loopEnd=0;
|
|
||||||
walkSong(loopOrder,loopRow,loopEnd);
|
|
||||||
logI("loop point: %d %d",loopOrder,loopRow);
|
|
||||||
warnings="";
|
|
||||||
|
|
||||||
DivZSM zsm;
|
|
||||||
zsm.init(zsmrate);
|
|
||||||
|
|
||||||
// reset the playback state
|
|
||||||
curOrder=0;
|
|
||||||
freelance=false;
|
|
||||||
playing=false;
|
|
||||||
extValuePresent=false;
|
|
||||||
remainingLoops=-1;
|
|
||||||
|
|
||||||
// Prepare to write song data
|
|
||||||
playSub(false);
|
|
||||||
//size_t tickCount=0;
|
|
||||||
bool done=false;
|
|
||||||
bool loopNow=false;
|
|
||||||
int loopPos=-1;
|
|
||||||
int fracWait=0; // accumulates fractional ticks
|
|
||||||
if (VERA>=0) disCont[VERA].dispatch->toggleRegisterDump(true);
|
|
||||||
if (YM>=0) {
|
|
||||||
disCont[YM].dispatch->toggleRegisterDump(true);
|
|
||||||
// emit LFO initialization commands
|
|
||||||
zsm.writeYM(0x18,0); // freq=0
|
|
||||||
zsm.writeYM(0x19,0x7F); // AMD =7F
|
|
||||||
zsm.writeYM(0x19,0xFF); // PMD =7F
|
|
||||||
// TODO: incorporate the Furnace meta-command for init data and filter
|
|
||||||
// out writes to otherwise-unused channels.
|
|
||||||
}
|
|
||||||
// Indicate the song's tuning as a sync meta-event
|
|
||||||
// specified in terms of how many 1/256th semitones
|
|
||||||
// the song is offset from standard A-440 tuning.
|
|
||||||
// This is mainly to benefit visualizations in players
|
|
||||||
// for non-standard tunings so that they can avoid
|
|
||||||
// displaying the entire song held in pitch bend.
|
|
||||||
// Tunings offsets that exceed a half semitone
|
|
||||||
// will simply be represented in a different key
|
|
||||||
// by nature of overflowing the signed char value
|
|
||||||
signed char tuningoffset=(signed char)(round(3072*(log(song.tuning/440.0)/log(2))))&0xff;
|
|
||||||
zsm.writeSync(0x01,tuningoffset);
|
|
||||||
// Set optimize flag, which mainly buffers PSG writes
|
|
||||||
// whenever the channel is silent
|
|
||||||
zsm.setOptimize(optimize);
|
|
||||||
|
|
||||||
while (!done) {
|
|
||||||
if (loopPos==-1) {
|
|
||||||
if (loopOrder==curOrder && loopRow==curRow && loop)
|
|
||||||
loopNow=true;
|
|
||||||
if (loopNow) {
|
|
||||||
// If Virtual Tempo is in use, our exact loop point
|
|
||||||
// might be skipped due to quantization error.
|
|
||||||
// If this happens, the tick immediately following is our loop point.
|
|
||||||
if (ticks==1 || !(loopOrder==curOrder && loopRow==curRow)) {
|
|
||||||
loopPos=zsm.getoffset();
|
|
||||||
zsm.setLoopPoint();
|
|
||||||
loopNow=false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nextTick() || !playing) {
|
|
||||||
done=true;
|
|
||||||
if (!loop) {
|
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
|
||||||
disCont[i].dispatch->getRegisterWrites().clear();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!playing) {
|
|
||||||
loopPos=-1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// get register dumps
|
|
||||||
for (int j=0; j<2; j++) {
|
|
||||||
int i=0;
|
|
||||||
// dump YM writes first
|
|
||||||
if (j==0) {
|
|
||||||
if (YM<0) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
i=YM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// dump VERA writes second
|
|
||||||
if (j==1) {
|
|
||||||
if (VERA<0) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
i=VERA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites();
|
|
||||||
if (writes.size()>0)
|
|
||||||
logD("zsmOps: Writing %d messages to chip %d",writes.size(),i);
|
|
||||||
for (DivRegWrite& write: writes) {
|
|
||||||
if (i==YM) zsm.writeYM(write.addr&0xff,write.val);
|
|
||||||
if (i==VERA) {
|
|
||||||
if (done && write.addr>=64) continue; // don't process any PCM or sync events on the loop lookahead
|
|
||||||
zsm.writePSG(write.addr&0xff,write.val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writes.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// write wait
|
|
||||||
int totalWait=cycles>>MASTER_CLOCK_PREC;
|
|
||||||
fracWait+=cycles&MASTER_CLOCK_MASK;
|
|
||||||
totalWait+=fracWait>>MASTER_CLOCK_PREC;
|
|
||||||
fracWait&=MASTER_CLOCK_MASK;
|
|
||||||
if (totalWait>0 && !done) {
|
|
||||||
zsm.tick(totalWait);
|
|
||||||
//tickCount+=totalWait;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// end of song
|
|
||||||
|
|
||||||
// done - close out.
|
|
||||||
got.rate=origRate;
|
|
||||||
if (VERA>=0) disCont[VERA].dispatch->toggleRegisterDump(false);
|
|
||||||
if (YM>=0) disCont[YM].dispatch->toggleRegisterDump(false);
|
|
||||||
|
|
||||||
remainingLoops=-1;
|
|
||||||
playing=false;
|
|
||||||
freelance=false;
|
|
||||||
extValuePresent=false;
|
|
||||||
|
|
||||||
BUSY_END;
|
|
||||||
return zsm.finish();
|
|
||||||
}
|
|
|
@ -319,6 +319,29 @@ void FurnaceGUI::drawExportROM(bool onWindow) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case DIV_ROM_ZSM: {
|
||||||
|
int zsmExportTickRate=romConfig.getInt("zsmrate",60);
|
||||||
|
bool zsmExportLoop=romConfig.getBool("loop",true);
|
||||||
|
bool zsmExportOptimize=romConfig.getBool("optimize",true);
|
||||||
|
|
||||||
|
if (ImGui::InputInt(_("Tick Rate (Hz)"),&zsmExportTickRate,1,2)) {
|
||||||
|
if (zsmExportTickRate<1) zsmExportTickRate=1;
|
||||||
|
if (zsmExportTickRate>44100) zsmExportTickRate=44100;
|
||||||
|
altered=true;
|
||||||
|
}
|
||||||
|
if (ImGui::Checkbox(_("loop"),&zsmExportLoop)) {
|
||||||
|
altered=true;
|
||||||
|
}
|
||||||
|
if (ImGui::Checkbox(_("optimize size"),&zsmExportOptimize)) {
|
||||||
|
altered=true;
|
||||||
|
}
|
||||||
|
if (altered) {
|
||||||
|
romConfig.set("zsmrate",zsmExportTickRate);
|
||||||
|
romConfig.set("loop",zsmExportLoop);
|
||||||
|
romConfig.set("optimize",zsmExportOptimize);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case DIV_ROM_ABSTRACT:
|
case DIV_ROM_ABSTRACT:
|
||||||
ImGui::TextWrapped("%s",_("select a target from the menu at the top of this dialog."));
|
ImGui::TextWrapped("%s",_("select a target from the menu at the top of this dialog."));
|
||||||
break;
|
break;
|
||||||
|
@ -340,28 +363,6 @@ void FurnaceGUI::drawExportROM(bool onWindow) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::drawExportZSM(bool onWindow) {
|
|
||||||
exitDisabledTimer=1;
|
|
||||||
|
|
||||||
ImGui::Text(_("Commander X16 Zsound Music File"));
|
|
||||||
if (ImGui::InputInt(_("Tick Rate (Hz)"),&zsmExportTickRate,1,2)) {
|
|
||||||
if (zsmExportTickRate<1) zsmExportTickRate=1;
|
|
||||||
if (zsmExportTickRate>44100) zsmExportTickRate=44100;
|
|
||||||
}
|
|
||||||
ImGui::Checkbox(_("loop"),&zsmExportLoop);
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::Checkbox(_("optimize size"),&zsmExportOptimize);
|
|
||||||
if (onWindow) {
|
|
||||||
ImGui::Separator();
|
|
||||||
if (ImGui::Button(_("Cancel"),ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
|
|
||||||
ImGui::SameLine();
|
|
||||||
}
|
|
||||||
if (ImGui::Button(_("Export"),ImVec2(200.0f*dpiScale,0))) {
|
|
||||||
openFileDialog(GUI_FILE_EXPORT_ZSM);
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FurnaceGUI::drawExportText(bool onWindow) {
|
void FurnaceGUI::drawExportText(bool onWindow) {
|
||||||
exitDisabledTimer=1;
|
exitDisabledTimer=1;
|
||||||
|
|
||||||
|
@ -444,16 +445,6 @@ void FurnaceGUI::drawExport() {
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int numZSMCompat=0;
|
|
||||||
for (int i=0; i<e->song.systemLen; i++) {
|
|
||||||
if ((e->song.system[i]==DIV_SYSTEM_VERA) || (e->song.system[i]==DIV_SYSTEM_YM2151)) numZSMCompat++;
|
|
||||||
}
|
|
||||||
if (numZSMCompat>0) {
|
|
||||||
if (ImGui::BeginTabItem(_("ZSM"))) {
|
|
||||||
drawExportZSM(true);
|
|
||||||
ImGui::EndTabItem();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ImGui::BeginTabItem(_("Text"))) {
|
if (ImGui::BeginTabItem(_("Text"))) {
|
||||||
drawExportText(true);
|
drawExportText(true);
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
|
@ -478,9 +469,6 @@ void FurnaceGUI::drawExport() {
|
||||||
case GUI_EXPORT_ROM:
|
case GUI_EXPORT_ROM:
|
||||||
drawExportROM(true);
|
drawExportROM(true);
|
||||||
break;
|
break;
|
||||||
case GUI_EXPORT_ZSM:
|
|
||||||
drawExportZSM(true);
|
|
||||||
break;
|
|
||||||
case GUI_EXPORT_TEXT:
|
case GUI_EXPORT_TEXT:
|
||||||
drawExportText(true);
|
drawExportText(true);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -2022,16 +2022,6 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
||||||
(settings.autoFillSave)?shortName:""
|
(settings.autoFillSave)?shortName:""
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case GUI_FILE_EXPORT_ZSM:
|
|
||||||
if (!dirExists(workingDirZSMExport)) workingDirZSMExport=getHomeDir();
|
|
||||||
hasOpened=fileDialog->openSave(
|
|
||||||
_("Export ZSM"),
|
|
||||||
{_("ZSM file"), "*.zsm"},
|
|
||||||
workingDirZSMExport,
|
|
||||||
dpiScale,
|
|
||||||
(settings.autoFillSave)?shortName:""
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case GUI_FILE_EXPORT_TEXT:
|
case GUI_FILE_EXPORT_TEXT:
|
||||||
if (!dirExists(workingDirROMExport)) workingDirROMExport=getHomeDir();
|
if (!dirExists(workingDirROMExport)) workingDirROMExport=getHomeDir();
|
||||||
hasOpened=fileDialog->openSave(
|
hasOpened=fileDialog->openSave(
|
||||||
|
@ -4405,16 +4395,6 @@ bool FurnaceGUI::loop() {
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int numZSMCompat=0;
|
|
||||||
for (int i=0; i<e->song.systemLen; i++) {
|
|
||||||
if ((e->song.system[i]==DIV_SYSTEM_VERA) || (e->song.system[i]==DIV_SYSTEM_YM2151)) numZSMCompat++;
|
|
||||||
}
|
|
||||||
if (numZSMCompat>0) {
|
|
||||||
if (ImGui::BeginMenu(_("export ZSM..."))) {
|
|
||||||
drawExportZSM();
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ImGui::BeginMenu(_("export text..."))) {
|
if (ImGui::BeginMenu(_("export text..."))) {
|
||||||
drawExportText();
|
drawExportText();
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
|
@ -4442,16 +4422,6 @@ bool FurnaceGUI::loop() {
|
||||||
displayExport=true;
|
displayExport=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int numZSMCompat=0;
|
|
||||||
for (int i=0; i<e->song.systemLen; i++) {
|
|
||||||
if ((e->song.system[i]==DIV_SYSTEM_VERA) || (e->song.system[i]==DIV_SYSTEM_YM2151)) numZSMCompat++;
|
|
||||||
}
|
|
||||||
if (numZSMCompat>0) {
|
|
||||||
if (ImGui::MenuItem(_("export ZSM..."))) {
|
|
||||||
curExportType=GUI_EXPORT_ZSM;
|
|
||||||
displayExport=true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ImGui::MenuItem(_("export text..."))) {
|
if (ImGui::MenuItem(_("export text..."))) {
|
||||||
curExportType=GUI_EXPORT_TEXT;
|
curExportType=GUI_EXPORT_TEXT;
|
||||||
displayExport=true;
|
displayExport=true;
|
||||||
|
@ -5034,9 +5004,6 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_FILE_EXPORT_VGM:
|
case GUI_FILE_EXPORT_VGM:
|
||||||
workingDirVGMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
workingDirVGMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
||||||
break;
|
break;
|
||||||
case GUI_FILE_EXPORT_ZSM:
|
|
||||||
workingDirZSMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
|
||||||
break;
|
|
||||||
case GUI_FILE_EXPORT_ROM:
|
case GUI_FILE_EXPORT_ROM:
|
||||||
case GUI_FILE_EXPORT_TEXT:
|
case GUI_FILE_EXPORT_TEXT:
|
||||||
case GUI_FILE_EXPORT_CMDSTREAM:
|
case GUI_FILE_EXPORT_CMDSTREAM:
|
||||||
|
@ -5136,9 +5103,6 @@ bool FurnaceGUI::loop() {
|
||||||
if (curFileDialog==GUI_FILE_EXPORT_ROM) {
|
if (curFileDialog==GUI_FILE_EXPORT_ROM) {
|
||||||
checkExtension(romFilterExt.c_str());
|
checkExtension(romFilterExt.c_str());
|
||||||
}
|
}
|
||||||
if (curFileDialog==GUI_FILE_EXPORT_ZSM) {
|
|
||||||
checkExtension(".zsm");
|
|
||||||
}
|
|
||||||
if (curFileDialog==GUI_FILE_EXPORT_TEXT) {
|
if (curFileDialog==GUI_FILE_EXPORT_TEXT) {
|
||||||
checkExtension(".txt");
|
checkExtension(".txt");
|
||||||
}
|
}
|
||||||
|
@ -5614,27 +5578,6 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GUI_FILE_EXPORT_ZSM: {
|
|
||||||
SafeWriter* w=e->saveZSM(zsmExportTickRate,zsmExportLoop,zsmExportOptimize);
|
|
||||||
if (w!=NULL) {
|
|
||||||
FILE* f=ps_fopen(copyOfName.c_str(),"wb");
|
|
||||||
if (f!=NULL) {
|
|
||||||
fwrite(w->getFinalBuf(),1,w->size(),f);
|
|
||||||
fclose(f);
|
|
||||||
pushRecentSys(copyOfName.c_str());
|
|
||||||
} else {
|
|
||||||
showError(_("could not open file!"));
|
|
||||||
}
|
|
||||||
w->finish();
|
|
||||||
delete w;
|
|
||||||
if (!e->getWarnings().empty()) {
|
|
||||||
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showError(fmt::sprintf(_("Could not write ZSM! (%s)"),e->getLastError()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GUI_FILE_EXPORT_ROM:
|
case GUI_FILE_EXPORT_ROM:
|
||||||
romExportPath=copyOfName;
|
romExportPath=copyOfName;
|
||||||
pendingExport=e->buildROM(romTarget);
|
pendingExport=e->buildROM(romTarget);
|
||||||
|
@ -7879,7 +7822,6 @@ void FurnaceGUI::syncState() {
|
||||||
workingDirSample=e->getConfString("lastDirSample",workingDir);
|
workingDirSample=e->getConfString("lastDirSample",workingDir);
|
||||||
workingDirAudioExport=e->getConfString("lastDirAudioExport",workingDir);
|
workingDirAudioExport=e->getConfString("lastDirAudioExport",workingDir);
|
||||||
workingDirVGMExport=e->getConfString("lastDirVGMExport",workingDir);
|
workingDirVGMExport=e->getConfString("lastDirVGMExport",workingDir);
|
||||||
workingDirZSMExport=e->getConfString("lastDirZSMExport",workingDir);
|
|
||||||
workingDirROMExport=e->getConfString("lastDirROMExport",workingDir);
|
workingDirROMExport=e->getConfString("lastDirROMExport",workingDir);
|
||||||
workingDirFont=e->getConfString("lastDirFont",workingDir);
|
workingDirFont=e->getConfString("lastDirFont",workingDir);
|
||||||
workingDirColors=e->getConfString("lastDirColors",workingDir);
|
workingDirColors=e->getConfString("lastDirColors",workingDir);
|
||||||
|
@ -8038,7 +7980,6 @@ void FurnaceGUI::commitState(DivConfig& conf) {
|
||||||
conf.set("lastDirSample",workingDirSample);
|
conf.set("lastDirSample",workingDirSample);
|
||||||
conf.set("lastDirAudioExport",workingDirAudioExport);
|
conf.set("lastDirAudioExport",workingDirAudioExport);
|
||||||
conf.set("lastDirVGMExport",workingDirVGMExport);
|
conf.set("lastDirVGMExport",workingDirVGMExport);
|
||||||
conf.set("lastDirZSMExport",workingDirZSMExport);
|
|
||||||
conf.set("lastDirROMExport",workingDirROMExport);
|
conf.set("lastDirROMExport",workingDirROMExport);
|
||||||
conf.set("lastDirFont",workingDirFont);
|
conf.set("lastDirFont",workingDirFont);
|
||||||
conf.set("lastDirColors",workingDirColors);
|
conf.set("lastDirColors",workingDirColors);
|
||||||
|
@ -8256,8 +8197,6 @@ FurnaceGUI::FurnaceGUI():
|
||||||
displayError(false),
|
displayError(false),
|
||||||
displayExporting(false),
|
displayExporting(false),
|
||||||
vgmExportLoop(true),
|
vgmExportLoop(true),
|
||||||
zsmExportLoop(true),
|
|
||||||
zsmExportOptimize(true),
|
|
||||||
vgmExportPatternHints(false),
|
vgmExportPatternHints(false),
|
||||||
vgmExportDirectStream(false),
|
vgmExportDirectStream(false),
|
||||||
displayInsTypeList(false),
|
displayInsTypeList(false),
|
||||||
|
@ -8300,7 +8239,6 @@ FurnaceGUI::FurnaceGUI():
|
||||||
vgmExportVersion(0x171),
|
vgmExportVersion(0x171),
|
||||||
vgmExportTrailingTicks(-1),
|
vgmExportTrailingTicks(-1),
|
||||||
drawHalt(10),
|
drawHalt(10),
|
||||||
zsmExportTickRate(60),
|
|
||||||
macroPointSize(16),
|
macroPointSize(16),
|
||||||
waveEditStyle(0),
|
waveEditStyle(0),
|
||||||
displayInsTypeListMakeInsSample(-1),
|
displayInsTypeListMakeInsSample(-1),
|
||||||
|
|
|
@ -598,7 +598,6 @@ enum FurnaceGUIFileDialogs {
|
||||||
GUI_FILE_EXPORT_AUDIO_PER_SYS,
|
GUI_FILE_EXPORT_AUDIO_PER_SYS,
|
||||||
GUI_FILE_EXPORT_AUDIO_PER_CHANNEL,
|
GUI_FILE_EXPORT_AUDIO_PER_CHANNEL,
|
||||||
GUI_FILE_EXPORT_VGM,
|
GUI_FILE_EXPORT_VGM,
|
||||||
GUI_FILE_EXPORT_ZSM,
|
|
||||||
GUI_FILE_EXPORT_CMDSTREAM,
|
GUI_FILE_EXPORT_CMDSTREAM,
|
||||||
GUI_FILE_EXPORT_TEXT,
|
GUI_FILE_EXPORT_TEXT,
|
||||||
GUI_FILE_EXPORT_ROM,
|
GUI_FILE_EXPORT_ROM,
|
||||||
|
@ -651,7 +650,6 @@ enum FurnaceGUIExportTypes {
|
||||||
GUI_EXPORT_AUDIO=0,
|
GUI_EXPORT_AUDIO=0,
|
||||||
GUI_EXPORT_VGM,
|
GUI_EXPORT_VGM,
|
||||||
GUI_EXPORT_ROM,
|
GUI_EXPORT_ROM,
|
||||||
GUI_EXPORT_ZSM,
|
|
||||||
GUI_EXPORT_CMD_STREAM,
|
GUI_EXPORT_CMD_STREAM,
|
||||||
GUI_EXPORT_TEXT,
|
GUI_EXPORT_TEXT,
|
||||||
GUI_EXPORT_DMF
|
GUI_EXPORT_DMF
|
||||||
|
@ -1594,7 +1592,7 @@ class FurnaceGUI {
|
||||||
|
|
||||||
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile, sysSearchQuery, newSongQuery, paletteQuery, sampleBankSearchQuery;
|
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile, sysSearchQuery, newSongQuery, paletteQuery, sampleBankSearchQuery;
|
||||||
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport;
|
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport;
|
||||||
String workingDirVGMExport, workingDirZSMExport, workingDirROMExport;
|
String workingDirVGMExport, workingDirROMExport;
|
||||||
String workingDirFont, workingDirColors, workingDirKeybinds;
|
String workingDirFont, workingDirColors, workingDirKeybinds;
|
||||||
String workingDirLayout, workingDirROM, workingDirTest;
|
String workingDirLayout, workingDirROM, workingDirTest;
|
||||||
String workingDirConfig;
|
String workingDirConfig;
|
||||||
|
@ -1613,7 +1611,7 @@ class FurnaceGUI {
|
||||||
std::vector<String> availRenderDrivers;
|
std::vector<String> availRenderDrivers;
|
||||||
std::vector<String> availAudioDrivers;
|
std::vector<String> availAudioDrivers;
|
||||||
|
|
||||||
bool quit, warnQuit, willCommit, edit, editClone, isPatUnique, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, zsmExportOptimize, vgmExportPatternHints;
|
bool quit, warnQuit, willCommit, edit, editClone, isPatUnique, modified, displayError, displayExporting, vgmExportLoop, vgmExportPatternHints;
|
||||||
bool vgmExportDirectStream, displayInsTypeList, displayWaveSizeList;
|
bool vgmExportDirectStream, displayInsTypeList, displayWaveSizeList;
|
||||||
bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed;
|
bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed;
|
||||||
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
||||||
|
@ -1634,7 +1632,6 @@ class FurnaceGUI {
|
||||||
int vgmExportTrailingTicks;
|
int vgmExportTrailingTicks;
|
||||||
int cvHiScore;
|
int cvHiScore;
|
||||||
int drawHalt;
|
int drawHalt;
|
||||||
int zsmExportTickRate;
|
|
||||||
int macroPointSize;
|
int macroPointSize;
|
||||||
int waveEditStyle;
|
int waveEditStyle;
|
||||||
int displayInsTypeListMakeInsSample;
|
int displayInsTypeListMakeInsSample;
|
||||||
|
@ -2700,7 +2697,6 @@ class FurnaceGUI {
|
||||||
void drawExportAudio(bool onWindow=false);
|
void drawExportAudio(bool onWindow=false);
|
||||||
void drawExportVGM(bool onWindow=false);
|
void drawExportVGM(bool onWindow=false);
|
||||||
void drawExportROM(bool onWindow=false);
|
void drawExportROM(bool onWindow=false);
|
||||||
void drawExportZSM(bool onWindow=false);
|
|
||||||
void drawExportText(bool onWindow=false);
|
void drawExportText(bool onWindow=false);
|
||||||
void drawExportCommand(bool onWindow=false);
|
void drawExportCommand(bool onWindow=false);
|
||||||
void drawExportDMF(bool onWindow=false);
|
void drawExportDMF(bool onWindow=false);
|
||||||
|
|
38
src/main.cpp
38
src/main.cpp
|
@ -85,7 +85,6 @@ FurnaceCLI cli;
|
||||||
|
|
||||||
String outName;
|
String outName;
|
||||||
String vgmOutName;
|
String vgmOutName;
|
||||||
String zsmOutName;
|
|
||||||
String cmdOutName;
|
String cmdOutName;
|
||||||
int benchMode=0;
|
int benchMode=0;
|
||||||
int subsong=-1;
|
int subsong=-1;
|
||||||
|
@ -429,12 +428,6 @@ TAParamResult pVGMOut(String val) {
|
||||||
return TA_PARAM_SUCCESS;
|
return TA_PARAM_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
TAParamResult pZSMOut(String val) {
|
|
||||||
zsmOutName=val;
|
|
||||||
e.setAudio(DIV_AUDIO_DUMMY);
|
|
||||||
return TA_PARAM_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
TAParamResult pCmdOut(String val) {
|
TAParamResult pCmdOut(String val) {
|
||||||
cmdOutName=val;
|
cmdOutName=val;
|
||||||
e.setAudio(DIV_AUDIO_DUMMY);
|
e.setAudio(DIV_AUDIO_DUMMY);
|
||||||
|
@ -457,7 +450,6 @@ void initParams() {
|
||||||
params.push_back(TAParam("o","output",true,pOutput,"<filename>","output audio to file"));
|
params.push_back(TAParam("o","output",true,pOutput,"<filename>","output audio to file"));
|
||||||
params.push_back(TAParam("O","vgmout",true,pVGMOut,"<filename>","output .vgm data"));
|
params.push_back(TAParam("O","vgmout",true,pVGMOut,"<filename>","output .vgm data"));
|
||||||
params.push_back(TAParam("D","direct",false,pDirect,"","set VGM export direct stream mode"));
|
params.push_back(TAParam("D","direct",false,pDirect,"","set VGM export direct stream mode"));
|
||||||
params.push_back(TAParam("Z","zsmout",true,pZSMOut,"<filename>","output .zsm data for Commander X16 Zsound"));
|
|
||||||
params.push_back(TAParam("C","cmdout",true,pCmdOut,"<filename>","output command stream"));
|
params.push_back(TAParam("C","cmdout",true,pCmdOut,"<filename>","output command stream"));
|
||||||
params.push_back(TAParam("L","loglevel",true,pLogLevel,"debug|info|warning|error","set the log level (info by default)"));
|
params.push_back(TAParam("L","loglevel",true,pLogLevel,"debug|info|warning|error","set the log level (info by default)"));
|
||||||
params.push_back(TAParam("v","view",true,pView,"pattern|commands|nothing","set visualization (nothing by default)"));
|
params.push_back(TAParam("v","view",true,pView,"pattern|commands|nothing","set visualization (nothing by default)"));
|
||||||
|
@ -560,7 +552,6 @@ int main(int argc, char** argv) {
|
||||||
#endif
|
#endif
|
||||||
outName="";
|
outName="";
|
||||||
vgmOutName="";
|
vgmOutName="";
|
||||||
zsmOutName="";
|
|
||||||
cmdOutName="";
|
cmdOutName="";
|
||||||
|
|
||||||
// load config for locale
|
// load config for locale
|
||||||
|
@ -729,14 +720,14 @@ int main(int argc, char** argv) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileName.empty() && (benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="")) {
|
if (fileName.empty() && (benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="")) {
|
||||||
logE("provide a file!");
|
logE("provide a file!");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_GUI
|
#ifdef HAVE_GUI
|
||||||
if (e.preInit(consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="")) {
|
if (e.preInit(consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="")) {
|
||||||
if (consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="") {
|
if (consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="") {
|
||||||
logW("engine wants safe mode, but Furnace GUI is not going to start.");
|
logW("engine wants safe mode, but Furnace GUI is not going to start.");
|
||||||
} else {
|
} else {
|
||||||
safeMode=true;
|
safeMode=true;
|
||||||
|
@ -748,7 +739,7 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (safeMode && (consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="")) {
|
if (safeMode && (consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="")) {
|
||||||
logE("you can't use safe mode and console/export mode together.");
|
logE("you can't use safe mode and console/export mode together.");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -757,7 +748,7 @@ int main(int argc, char** argv) {
|
||||||
e.setAudio(DIV_AUDIO_DUMMY);
|
e.setAudio(DIV_AUDIO_DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileName.empty() && ((!e.getConfBool("tutIntroPlayed",TUT_INTRO_PLAYED)) || e.getConfInt("alwaysPlayIntro",0)!=3 || consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="")) {
|
if (!fileName.empty() && ((!e.getConfBool("tutIntroPlayed",TUT_INTRO_PLAYED)) || e.getConfInt("alwaysPlayIntro",0)!=3 || consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="")) {
|
||||||
logI("loading module...");
|
logI("loading module...");
|
||||||
FILE* f=ps_fopen(fileName.c_str(),"rb");
|
FILE* f=ps_fopen(fileName.c_str(),"rb");
|
||||||
if (f==NULL) {
|
if (f==NULL) {
|
||||||
|
@ -849,7 +840,7 @@ int main(int argc, char** argv) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="") {
|
if (outName!="" || vgmOutName!="" || cmdOutName!="") {
|
||||||
if (cmdOutName!="") {
|
if (cmdOutName!="") {
|
||||||
SafeWriter* w=e.saveCommand();
|
SafeWriter* w=e.saveCommand();
|
||||||
if (w!=NULL) {
|
if (w!=NULL) {
|
||||||
|
@ -882,23 +873,6 @@ int main(int argc, char** argv) {
|
||||||
reportError(_("could not write VGM!"));
|
reportError(_("could not write VGM!"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (zsmOutName!="") {
|
|
||||||
// TODO: changing parameters
|
|
||||||
SafeWriter* w=e.saveZSM(60,true,true);
|
|
||||||
if (w!=NULL) {
|
|
||||||
FILE* f=ps_fopen(zsmOutName.c_str(),"wb");
|
|
||||||
if (f!=NULL) {
|
|
||||||
fwrite(w->getFinalBuf(),1,w->size(),f);
|
|
||||||
fclose(f);
|
|
||||||
} else {
|
|
||||||
reportError(fmt::sprintf(_("could not open file! (%s)"),e.getLastError()));
|
|
||||||
}
|
|
||||||
w->finish();
|
|
||||||
delete w;
|
|
||||||
} else {
|
|
||||||
reportError(fmt::sprintf(_("could not write ZSM! (%s)"),e.getLastError()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (outName!="") {
|
if (outName!="") {
|
||||||
e.setConsoleMode(true);
|
e.setConsoleMode(true);
|
||||||
e.saveAudio(outName.c_str(),exportOptions);
|
e.saveAudio(outName.c_str(),exportOptions);
|
||||||
|
|
Loading…
Reference in a new issue