Merge branch 'tildearrow:master' into SID3

This commit is contained in:
LTVA1 2024-08-19 21:44:23 +03:00 committed by GitHub
commit c26fa0c1f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 549 additions and 484 deletions

View file

@ -719,8 +719,6 @@ src/engine/wavetable.cpp
src/engine/waveSynth.cpp
src/engine/wavOps.cpp
src/engine/vgmOps.cpp
src/engine/zsmOps.cpp
src/engine/zsm.cpp
src/engine/platform/abstract.cpp
src/engine/platform/genesis.cpp
@ -800,6 +798,7 @@ src/engine/platform/dummy.cpp
src/engine/export/abstract.cpp
src/engine/export/amigaValidation.cpp
src/engine/export/tiuna.cpp
src/engine/export/zsm.cpp
src/engine/effect/abstract.cpp
src/engine/effect/dummy.cpp

View file

@ -0,0 +1,42 @@
def merge_po_files_preserve_format(base_file, new_file, output_file):
with open(base_file, 'r', encoding='utf-8') as f1, open(new_file, 'r', encoding='utf-8') as f2:
base_lines = f1.readlines()
new_lines = f2.readlines()
# Traverse the new_lines and extract msgid and msgstr
new_translations = {}
i = 0
while i < len(new_lines):
if new_lines[i].startswith('msgid '):
msgid_start = i
while not new_lines[i].startswith('msgstr '):
i += 1
msgid = ''.join(new_lines[msgid_start:i])
msgstr = new_lines[i].split('msgstr ')[1].strip()
new_translations[msgid] = msgstr
i += 1
# Open the output_file and write the merged content
with open(output_file, 'w', encoding='utf-8') as output:
i = 0
while i < len(base_lines):
if base_lines[i].startswith('msgid '):
msgid_start = i
while not base_lines[i].startswith('msgstr '):
i += 1
msgid = ''.join(base_lines[msgid_start:i])
if msgid in new_translations:
output.write(f'{msgid}')
output.write(f'msgstr {new_translations[msgid]}\n')
else:
output.write(base_lines[i])
i += 1
else:
output.write(base_lines[i])
i += 1
if __name__ == '__main__':
base_file = 'base.po'
new_file = 'new.po'
output_file = 'output.po'
merge_po_files_preserve_format(base_file, new_file, output_file)

View file

@ -0,0 +1,54 @@
#!/bin/bash
merge_po_files_preserve_format() {
base_file="base.po"
new_file="new.po"
output_file="output.po"
declare -A new_translations
# Read new_file and extract msgid and msgstr
msgid=""
while IFS= read -r line; do
if [[ $line == msgid* ]]; then
msgid="$line"
msgstr=""
elif [[ $line == msgstr* ]]; then
msgstr="$line"
new_translations["$msgid"]="$msgstr"
elif [[ $line == "\"\"" ]]; then
msgid+="$line"
fi
done < "$new_file"
# Open output_file and write merged content
msgid=""
while IFS= read -r line; do
if [[ $line == msgid* ]]; then
msgid="$line"
msgstr=""
elif [[ $line == msgstr* ]]; then
msgstr="$line"
if [[ -n ${new_translations["$msgid"]} ]]; then
echo "$msgid" >> "$output_file"
echo "${new_translations["$msgid"]}" >> "$output_file"
else
echo "$msgid" >> "$output_file"
echo "$msgstr" >> "$output_file"
fi
msgid=""
msgstr=""
elif [[ $line == "\"\"" ]]; then
msgid+="$line"
else
echo "$line" >> "$output_file"
fi
done < "$base_file"
}
if [[ $# -ne 3 ]]; then
echo "Usage: $0 base_file new_file output_file"
exit 1
fi
merge_po_files_preserve_format "base.po" "new.po" "output.po"

View file

@ -669,6 +669,7 @@ class DivEngine {
friend class DivROMExport;
friend class DivExportAmigaValidation;
friend class DivExportTiuna;
friend class DivExportZSM;
public:
DivSong song;
@ -721,8 +722,6 @@ class DivEngine {
// - -1 to auto-determine 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);
// dump to ZSM.
SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true, bool optimize=true);
// dump to TIunA.
SafeWriter* saveTiuna(const bool* sysToExport, const char* baseLabel, int firstBankSize, int otherBankSize);
// dump command stream.

View file

@ -21,6 +21,7 @@
#include "export/amigaValidation.h"
#include "export/tiuna.h"
#include "export/zsm.h"
DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) {
DivROMExport* exporter=NULL;
@ -31,6 +32,9 @@ DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) {
case DIV_ROM_TIUNA:
exporter=new DivExportTiuna;
break;
case DIV_ROM_ZSM:
exporter=new DivExportZSM;
break;
default:
exporter=new DivROMExport;
break;

View file

@ -18,9 +18,77 @@
*/
#include "zsm.h"
#include "../engine.h"
#include "../ta-log.h"
#include "../utfutils.h"
#include "song.h"
#include <fmt/printf.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() {
w=NULL;
@ -447,3 +515,240 @@ void DivZSM::flushTicks() {
}
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
View 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() {}
};

View file

@ -401,8 +401,8 @@ std::vector<DivSample*> DivEngine::sampleFromFile(const char* path) {
sample->loop=false;
}
if (sample->centerRate<4000) sample->centerRate=4000;
if (sample->centerRate>64000) sample->centerRate=64000;
if (sample->centerRate<100) sample->centerRate=100;
if (sample->centerRate>384000) sample->centerRate=384000;
sfWrap.doClose();
BUSY_END;
ret.push_back(sample);

View file

@ -19,6 +19,7 @@
#include "sfWrapper.h"
#include "../fileutils.h"
#include "../ta-log.h"
#include "sndfile.h"
sf_count_t _vioGetSize(void* user) {
@ -80,6 +81,7 @@ SNDFILE* SFWrapper::doOpen(const char* path, int mode, SF_INFO* sfinfo) {
vio.seek=_vioSeek;
vio.tell=_vioTell;
vio.write=_vioWrite;
logV("SFWrapper: opening %s",path);
const char* modeC="rb";
if (mode==SFM_WRITE) {
@ -91,10 +93,12 @@ SNDFILE* SFWrapper::doOpen(const char* path, int mode, SF_INFO* sfinfo) {
f=ps_fopen(path,modeC);
if (f==NULL) {
logE("SFWrapper: failed to open (%s)",strerror(errno));
return NULL;
}
if (fseek(f,0,SEEK_END)==-1) {
logE("SFWrapper: failed to seek to end (%s)",strerror(errno));
fclose(f);
f=NULL;
return NULL;
@ -102,6 +106,7 @@ SNDFILE* SFWrapper::doOpen(const char* path, int mode, SF_INFO* sfinfo) {
len=ftell(f);
if (len==(SIZE_MAX>>1)) {
logE("SFWrapper: failed to tell (%s)",strerror(errno));
len=0;
fclose(f);
f=NULL;
@ -109,6 +114,7 @@ SNDFILE* SFWrapper::doOpen(const char* path, int mode, SF_INFO* sfinfo) {
}
if (fseek(f,0,SEEK_SET)==-1) {
logE("SFWrapper: failed to seek to beginning (%s)",strerror(errno));
len=0;
fclose(f);
f=NULL;
@ -117,5 +123,8 @@ SNDFILE* SFWrapper::doOpen(const char* path, int mode, SF_INFO* sfinfo) {
sf=sf_open_virtual(&vio,mode,sfinfo,this);
if (sf!=NULL) fileMode=mode;
if (sf==NULL) {
logE("SFWrapper: WHY IS IT NULL?!");
}
return sf;
}

View file

@ -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

View file

@ -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();
}

View file

@ -123,16 +123,16 @@ void FurnaceGUI::doAction(int what) {
pendingStepUpdate=1;
break;
case GUI_ACTION_OCTAVE_UP:
if (++curOctave>7) {
curOctave=7;
if (++curOctave>GUI_EDIT_OCTAVE_MAX) {
curOctave=GUI_EDIT_OCTAVE_MAX;
} else {
e->autoNoteOffAll();
failedNoteOn=false;
}
break;
case GUI_ACTION_OCTAVE_DOWN:
if (--curOctave<-5) {
curOctave=-5;
if (--curOctave<GUI_EDIT_OCTAVE_MIN) {
curOctave=GUI_EDIT_OCTAVE_MIN;
} else {
e->autoNoteOffAll();
failedNoteOn=false;
@ -679,10 +679,26 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_PAT_ABSORB_INSTRUMENT: {
DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],false);
if (!pat) break;
for (int i=cursor.y; i>=0; i--) {
if (pat->data[i][2] >= 0) {
bool foundIns=false;
bool foundOctave=false;
for (int i=cursor.y; i>=0 && !(foundIns && foundOctave); i--) {
// absorb most recent instrument
if (!foundIns && pat->data[i][2] >= 0) {
curIns=pat->data[i][2];
break;
foundIns=true;
}
// absorb most recent octave (i.e. set curOctave such that the "main row" (QWERTY) of notes
// will result in an octave number equal to the previous note).
if (!foundOctave && pat->data[i][0] != 0) {
// decode octave data (was signed cast to unsigned char)
int octave=pat->data[i][1];
if (octave>128) octave-=256;
// @NOTE the special handling when note==12, which is really an octave above what's
// stored in the octave data. without this handling, if you press Q, then
// "ABSORB_INSTRUMENT", then Q again, you'd get a different octave!
if (pat->data[i][0]==12) octave++;
curOctave=CLAMP(octave-1, GUI_EDIT_OCTAVE_MIN, GUI_EDIT_OCTAVE_MAX);
foundOctave=true;
}
}
break;

View file

@ -647,8 +647,8 @@ void FurnaceGUI::drawEditControls() {
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##Octave",&curOctave,1,1)) {
if (curOctave>7) curOctave=7;
if (curOctave<-5) curOctave=-5;
if (curOctave>GUI_EDIT_OCTAVE_MAX) curOctave=GUI_EDIT_OCTAVE_MAX;
if (curOctave<GUI_EDIT_OCTAVE_MIN) curOctave=GUI_EDIT_OCTAVE_MIN;
e->autoNoteOffAll();
failedNoteOn=false;
@ -808,8 +808,8 @@ void FurnaceGUI::drawEditControls() {
ImGui::SameLine();
ImGui::SetNextItemWidth(96.0f*dpiScale);
if (ImGui::InputInt("##Octave",&curOctave,1,1)) {
if (curOctave>7) curOctave=7;
if (curOctave<-5) curOctave=-5;
if (curOctave>GUI_EDIT_OCTAVE_MAX) curOctave=GUI_EDIT_OCTAVE_MAX;
if (curOctave<GUI_EDIT_OCTAVE_MIN) curOctave=GUI_EDIT_OCTAVE_MIN;
e->autoNoteOffAll();
failedNoteOn=false;
@ -926,8 +926,8 @@ void FurnaceGUI::drawEditControls() {
float avail=ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(avail);
if (ImGui::InputInt("##Octave",&curOctave,0,0)) {
if (curOctave>7) curOctave=7;
if (curOctave<-5) curOctave=-5;
if (curOctave>GUI_EDIT_OCTAVE_MAX) curOctave=GUI_EDIT_OCTAVE_MAX;
if (curOctave<GUI_EDIT_OCTAVE_MIN) curOctave=GUI_EDIT_OCTAVE_MIN;
e->autoNoteOffAll();
failedNoteOn=false;
@ -1093,8 +1093,8 @@ void FurnaceGUI::drawEditControls() {
float avail=ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(avail);
if (ImGui::InputInt("##Octave",&curOctave,1,1)) {
if (curOctave>7) curOctave=7;
if (curOctave<-5) curOctave=-5;
if (curOctave>GUI_EDIT_OCTAVE_MAX) curOctave=GUI_EDIT_OCTAVE_MAX;
if (curOctave<GUI_EDIT_OCTAVE_MIN) curOctave=GUI_EDIT_OCTAVE_MIN;
e->autoNoteOffAll();
failedNoteOn=false;

View file

@ -319,6 +319,29 @@ void FurnaceGUI::drawExportROM(bool onWindow) {
}
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:
ImGui::TextWrapped("%s",_("select a target from the menu at the top of this dialog."));
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) {
exitDisabledTimer=1;
@ -444,16 +445,6 @@ void FurnaceGUI::drawExport() {
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"))) {
drawExportText(true);
ImGui::EndTabItem();
@ -478,9 +469,6 @@ void FurnaceGUI::drawExport() {
case GUI_EXPORT_ROM:
drawExportROM(true);
break;
case GUI_EXPORT_ZSM:
drawExportZSM(true);
break;
case GUI_EXPORT_TEXT:
drawExportText(true);
break;

View file

@ -2022,16 +2022,6 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
(settings.autoFillSave)?shortName:""
);
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:
if (!dirExists(workingDirROMExport)) workingDirROMExport=getHomeDir();
hasOpened=fileDialog->openSave(
@ -4405,16 +4395,6 @@ bool FurnaceGUI::loop() {
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..."))) {
drawExportText();
ImGui::EndMenu();
@ -4442,16 +4422,6 @@ bool FurnaceGUI::loop() {
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..."))) {
curExportType=GUI_EXPORT_TEXT;
displayExport=true;
@ -5034,9 +5004,6 @@ bool FurnaceGUI::loop() {
case GUI_FILE_EXPORT_VGM:
workingDirVGMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_EXPORT_ZSM:
workingDirZSMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_EXPORT_ROM:
case GUI_FILE_EXPORT_TEXT:
case GUI_FILE_EXPORT_CMDSTREAM:
@ -5136,9 +5103,6 @@ bool FurnaceGUI::loop() {
if (curFileDialog==GUI_FILE_EXPORT_ROM) {
checkExtension(romFilterExt.c_str());
}
if (curFileDialog==GUI_FILE_EXPORT_ZSM) {
checkExtension(".zsm");
}
if (curFileDialog==GUI_FILE_EXPORT_TEXT) {
checkExtension(".txt");
}
@ -5614,27 +5578,6 @@ bool FurnaceGUI::loop() {
}
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:
romExportPath=copyOfName;
pendingExport=e->buildROM(romTarget);
@ -7879,7 +7822,6 @@ void FurnaceGUI::syncState() {
workingDirSample=e->getConfString("lastDirSample",workingDir);
workingDirAudioExport=e->getConfString("lastDirAudioExport",workingDir);
workingDirVGMExport=e->getConfString("lastDirVGMExport",workingDir);
workingDirZSMExport=e->getConfString("lastDirZSMExport",workingDir);
workingDirROMExport=e->getConfString("lastDirROMExport",workingDir);
workingDirFont=e->getConfString("lastDirFont",workingDir);
workingDirColors=e->getConfString("lastDirColors",workingDir);
@ -8038,7 +7980,6 @@ void FurnaceGUI::commitState(DivConfig& conf) {
conf.set("lastDirSample",workingDirSample);
conf.set("lastDirAudioExport",workingDirAudioExport);
conf.set("lastDirVGMExport",workingDirVGMExport);
conf.set("lastDirZSMExport",workingDirZSMExport);
conf.set("lastDirROMExport",workingDirROMExport);
conf.set("lastDirFont",workingDirFont);
conf.set("lastDirColors",workingDirColors);
@ -8256,8 +8197,6 @@ FurnaceGUI::FurnaceGUI():
displayError(false),
displayExporting(false),
vgmExportLoop(true),
zsmExportLoop(true),
zsmExportOptimize(true),
vgmExportPatternHints(false),
vgmExportDirectStream(false),
displayInsTypeList(false),
@ -8300,7 +8239,6 @@ FurnaceGUI::FurnaceGUI():
vgmExportVersion(0x171),
vgmExportTrailingTicks(-1),
drawHalt(10),
zsmExportTickRate(60),
macroPointSize(16),
waveEditStyle(0),
displayInsTypeListMakeInsSample(-1),

View file

@ -137,6 +137,9 @@ enum FurnaceGUIRenderBackend {
#define ngettext momo_ngettext
#endif
#define GUI_EDIT_OCTAVE_MIN -5
#define GUI_EDIT_OCTAVE_MAX 7
// TODO:
// - add colors for FM envelope and waveform
// - maybe add "alternate" color for FM modulators/carriers (a bit difficult)
@ -599,7 +602,6 @@ enum FurnaceGUIFileDialogs {
GUI_FILE_EXPORT_AUDIO_PER_SYS,
GUI_FILE_EXPORT_AUDIO_PER_CHANNEL,
GUI_FILE_EXPORT_VGM,
GUI_FILE_EXPORT_ZSM,
GUI_FILE_EXPORT_CMDSTREAM,
GUI_FILE_EXPORT_TEXT,
GUI_FILE_EXPORT_ROM,
@ -652,7 +654,6 @@ enum FurnaceGUIExportTypes {
GUI_EXPORT_AUDIO=0,
GUI_EXPORT_VGM,
GUI_EXPORT_ROM,
GUI_EXPORT_ZSM,
GUI_EXPORT_CMD_STREAM,
GUI_EXPORT_TEXT,
GUI_EXPORT_DMF
@ -1595,7 +1596,7 @@ class FurnaceGUI {
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile, sysSearchQuery, newSongQuery, paletteQuery, sampleBankSearchQuery;
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport;
String workingDirVGMExport, workingDirZSMExport, workingDirROMExport;
String workingDirVGMExport, workingDirROMExport;
String workingDirFont, workingDirColors, workingDirKeybinds;
String workingDirLayout, workingDirROM, workingDirTest;
String workingDirConfig;
@ -1614,7 +1615,7 @@ class FurnaceGUI {
std::vector<String> availRenderDrivers;
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 portrait, injectBackUp, mobileMenuOpen, warnColorPushed;
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
@ -1635,7 +1636,6 @@ class FurnaceGUI {
int vgmExportTrailingTicks;
int cvHiScore;
int drawHalt;
int zsmExportTickRate;
int macroPointSize;
int waveEditStyle;
int displayInsTypeListMakeInsSample;
@ -2701,7 +2701,6 @@ class FurnaceGUI {
void drawExportAudio(bool onWindow=false);
void drawExportVGM(bool onWindow=false);
void drawExportROM(bool onWindow=false);
void drawExportZSM(bool onWindow=false);
void drawExportText(bool onWindow=false);
void drawExportCommand(bool onWindow=false);
void drawExportDMF(bool onWindow=false);

View file

@ -688,7 +688,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("PAT_LATCH", _N("Set note input latch"), 0),
D("PAT_SCROLL_MODE", _N("Change mobile scroll mode"), 0),
D("PAT_CLEAR_LATCH", _N("Clear note input latch"), 0),
D("PAT_ABSORB_INSTRUMENT", _N("Set current instrument to channel's current instrument column"), 0),
D("PAT_ABSORB_INSTRUMENT", _N("Absorb instrument/octave from status at cursor"), 0),
D("PAT_MAX", "", NOT_AN_ACTION),
D("INS_LIST_MIN", _N("---Instrument list"), NOT_AN_ACTION),

View file

@ -1204,62 +1204,62 @@ void FurnaceGUI::initSystemPresets() {
}
);
SUB_ENTRY(
"Sega TeraDrive", {
CH(DIV_SYSTEM_YM2612, 1.0f, 0, ""),
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
"Sega TeraDrive", {
CH(DIV_SYSTEM_YM2612, 1.0f, 0, ""),
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
}
);
SUB_SUB_ENTRY(
"Sega TeraDrive (extended channel 3)", {
CH(DIV_SYSTEM_YM2612_EXT, 1.0f, 0, ""),
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
"Sega TeraDrive (extended channel 3)", {
CH(DIV_SYSTEM_YM2612_EXT, 1.0f, 0, ""),
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
}
);
SUB_SUB_ENTRY(
"Sega TeraDrive (CSM)", {
CH(DIV_SYSTEM_YM2612_CSM, 1.0f, 0, ""),
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
"Sega TeraDrive (CSM)", {
CH(DIV_SYSTEM_YM2612_CSM, 1.0f, 0, ""),
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
}
);
SUB_SUB_ENTRY(
"Sega TeraDrive (DualPCM)", {
CH(DIV_SYSTEM_YM2612_DUALPCM, 1.0f, 0, ""),
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
"Sega TeraDrive (DualPCM)", {
CH(DIV_SYSTEM_YM2612_DUALPCM, 1.0f, 0, ""),
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
}
);
SUB_SUB_ENTRY(
"Sega TeraDrive (DualPCM, extended channel 3)", {
CH(DIV_SYSTEM_YM2612_DUALPCM_EXT, 1.0f, 0, ""),
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
"Sega TeraDrive (DualPCM, extended channel 3)", {
CH(DIV_SYSTEM_YM2612_DUALPCM_EXT, 1.0f, 0, ""),
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
}
);
ENTRY(
_("Sharp X1"), {
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=3")
}
}
);
SUB_ENTRY(
_("Sharp X1 + FM Addon"), {
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=3"),
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2")
}
}
);
ENTRY(
_("Sharp X68000"), {
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2"),
CH(DIV_SYSTEM_MSM6258, 1.0f, 0, "clockSel=2")
}
}
);
ENTRY(
_("FM-7"), {
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=12"),
CH(DIV_SYSTEM_YM2203, 1.0f, 0, "clockSel=5")
}
}
);
SUB_ENTRY(
_("FM-7 (extended channel 3)"), {

View file

@ -85,7 +85,6 @@ FurnaceCLI cli;
String outName;
String vgmOutName;
String zsmOutName;
String cmdOutName;
int benchMode=0;
int subsong=-1;
@ -431,12 +430,6 @@ TAParamResult pVGMOut(String val) {
return TA_PARAM_SUCCESS;
}
TAParamResult pZSMOut(String val) {
zsmOutName=val;
e.setAudio(DIV_AUDIO_DUMMY);
return TA_PARAM_SUCCESS;
}
TAParamResult pCmdOut(String val) {
cmdOutName=val;
e.setAudio(DIV_AUDIO_DUMMY);
@ -459,7 +452,6 @@ void initParams() {
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("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("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)"));
@ -562,7 +554,6 @@ int main(int argc, char** argv) {
#endif
outName="";
vgmOutName="";
zsmOutName="";
cmdOutName="";
// load config for locale
@ -731,14 +722,14 @@ int main(int argc, char** argv) {
return 1;
}
if (fileName.empty() && (benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="")) {
if (fileName.empty() && (benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="")) {
logE("provide a file!");
return 1;
}
#ifdef HAVE_GUI
if (e.preInit(consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="")) {
if (consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="") {
if (e.preInit(consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="")) {
if (consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="") {
logW("engine wants safe mode, but Furnace GUI is not going to start.");
} else {
safeMode=true;
@ -750,7 +741,7 @@ int main(int argc, char** argv) {
}
#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.");
return 1;
}
@ -759,7 +750,7 @@ int main(int argc, char** argv) {
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...");
FILE* f=ps_fopen(fileName.c_str(),"rb");
if (f==NULL) {
@ -851,7 +842,7 @@ int main(int argc, char** argv) {
return 0;
}
if (outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="") {
if (outName!="" || vgmOutName!="" || cmdOutName!="") {
if (cmdOutName!="") {
SafeWriter* w=e.saveCommand();
if (w!=NULL) {
@ -884,23 +875,6 @@ int main(int argc, char** argv) {
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!="") {
e.setConsoleMode(true);
e.saveAudio(outName.c_str(),exportOptions);