mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-01 10:32:40 +00:00
Merge branch 'master' into dx9
This commit is contained in:
commit
c9147b5152
36 changed files with 3187 additions and 2894 deletions
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -11,7 +11,7 @@ defaults:
|
|||
shell: bash
|
||||
|
||||
env:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,6 +5,7 @@ nosdl/
|
|||
release/
|
||||
t/
|
||||
winbuild/
|
||||
winCbuild/
|
||||
win32build/
|
||||
xpbuild/
|
||||
macbuild/
|
||||
|
|
|
@ -122,6 +122,7 @@ option(WITH_DEMOS "Install demo songs" ON)
|
|||
option(WITH_INSTRUMENTS "Install instruments" ON)
|
||||
option(WITH_WAVETABLES "Install wavetables" ON)
|
||||
option(SHOW_OPEN_ASSETS_MENU_ENTRY "Show option to open built-in assets directory (on supported platforms)" OFF)
|
||||
option(CONSOLE_SUBSYSTEM "Build Furnace with Console subsystem on Windows" OFF)
|
||||
if (APPLE)
|
||||
option(FORCE_APPLE_BIN "Force enable binary installation to /bin" OFF)
|
||||
else()
|
||||
|
@ -1081,7 +1082,7 @@ elseif (APPLE)
|
|||
find_library(COCOA Cocoa REQUIRED)
|
||||
list(APPEND DEPENDENCIES_LIBRARIES ${COCOA})
|
||||
else()
|
||||
list(APPEND DEPENDENCIES_LIBRARIES dl)
|
||||
list(APPEND DEPENDENCIES_LIBRARIES ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
if (NOT MSVC)
|
||||
|
@ -1128,9 +1129,13 @@ if (NOT ANDROID OR TERMUX)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if (WIN32 AND CONSOLE_SUBSYSTEM)
|
||||
list(APPEND DEPENDENCIES_DEFINES "TA_SUBSYSTEM_CONSOLE")
|
||||
endif()
|
||||
|
||||
if(ANDROID AND NOT TERMUX)
|
||||
add_library(furnace SHARED ${USED_SOURCES})
|
||||
elseif(WIN32)
|
||||
elseif(WIN32 AND NOT CONSOLE_SUBSYSTEM)
|
||||
add_executable(furnace WIN32 ${USED_SOURCES})
|
||||
else()
|
||||
add_executable(furnace ${USED_SOURCES})
|
||||
|
|
|
@ -280,6 +280,7 @@ Available options:
|
|||
| `WITH_INSTRUMENTS` | `ON` | Install demo instruments on `make install` |
|
||||
| `WITH_WAVETABLES` | `ON` | Install wavetables on `make install` |
|
||||
| `SHOW_OPEN_ASSETS_MENU_ENTRY` | `OFF` | Show option to open built-in assets directory (on supported platforms) |
|
||||
| `CONSOLE_SUBSYSTEM` | `OFF` | Build with subsystem set to Console on Windows |
|
||||
| `FORCE_APPLE_BIN` | `OFF` | Enable installation of binaries (when doing `make install`) to PREFIX/bin on Apple platforms |
|
||||
|
||||
(\*) `ON` if system-installed JACK detected, otherwise `OFF`
|
||||
|
|
8
TODO.md
8
TODO.md
|
@ -1,4 +1,12 @@
|
|||
# to-do for 0.6.4
|
||||
|
||||
- revamp audio export dialog
|
||||
- fix possible issues when moving selection
|
||||
- fix Metal intro crash
|
||||
|
||||
# to-do long term
|
||||
|
||||
- finish auto-clone
|
||||
- new pattern renderer - performance improvements
|
||||
- new info header
|
||||
- unlimited channels and chips
|
||||
|
|
2
extern/igfd/ImGuiFileDialog.cpp
vendored
2
extern/igfd/ImGuiFileDialog.cpp
vendored
|
@ -54,7 +54,7 @@ SOFTWARE.
|
|||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 260
|
||||
#endif // PATH_MAX
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined (__EMSCRIPTEN__) || defined(__HAIKU__)
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined (__EMSCRIPTEN__) || defined(__HAIKU__)
|
||||
#define UNIX
|
||||
#define stricmp strcasecmp
|
||||
#include <sys/types.h>
|
||||
|
|
42
scripts/release-winconsole.sh
Executable file
42
scripts/release-winconsole.sh
Executable file
|
@ -0,0 +1,42 @@
|
|||
#!/bin/bash
|
||||
# make Windows release
|
||||
# this script shall be run from Arch Linux with MinGW installed!
|
||||
|
||||
if [ ! -e /tmp/furnace ]; then
|
||||
ln -s "$PWD" /tmp/furnace || exit 1
|
||||
fi
|
||||
|
||||
cd /tmp/furnace
|
||||
|
||||
if [ ! -e winCbuild ]; then
|
||||
mkdir winCbuild || exit 1
|
||||
fi
|
||||
|
||||
cd winCbuild
|
||||
|
||||
# TODO: potential Arch-ism?
|
||||
x86_64-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Wno-deprecated-declarations -Werror" -DUSE_BACKWARD=ON -DCONSOLE_SUBSYSTEM=ON .. || exit 1
|
||||
make -j8 || exit 1
|
||||
|
||||
cd ..
|
||||
|
||||
mkdir -p release/winconsole || exit 1
|
||||
cd release/winconsole
|
||||
|
||||
cp ../../LICENSE LICENSE.txt || exit 1
|
||||
cp ../../winCbuild/furnace.exe . || exit 1
|
||||
cp ../../res/releaseReadme/stable-win.txt README.txt || exit 1
|
||||
cp -r ../../papers papers || exit 1
|
||||
cp -r ../../demos demos || exit 1
|
||||
cp -r ../../instruments instruments || exit 1
|
||||
cp -r ../../wavetables wavetables || exit 1
|
||||
|
||||
cp ../../res/docpdf/manual.pdf . || exit 1
|
||||
|
||||
x86_64-w64-mingw32-strip -s furnace.exe || exit 1
|
||||
|
||||
zip -r furnace.zip LICENSE.txt furnace.exe README.txt manual.pdf papers demos instruments wavetables
|
||||
|
||||
furName=$(git describe --tags | sed "s/v0/0/")
|
||||
|
||||
mv furnace.zip furnace-"$furName"-win64-console.zip
|
|
@ -1095,7 +1095,7 @@ bool DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) {
|
|||
}
|
||||
|
||||
bool DivEngine::addSystem(DivSystem which) {
|
||||
if (song.systemLen>DIV_MAX_CHIPS) {
|
||||
if (song.systemLen>=DIV_MAX_CHIPS) {
|
||||
lastError=fmt::sprintf("max number of systems is %d",DIV_MAX_CHIPS);
|
||||
return false;
|
||||
}
|
||||
|
@ -1149,7 +1149,7 @@ bool DivEngine::duplicateSystem(int index, bool pat, bool end) {
|
|||
lastError="invalid index";
|
||||
return false;
|
||||
}
|
||||
if (song.systemLen>DIV_MAX_CHIPS) {
|
||||
if (song.systemLen>=DIV_MAX_CHIPS) {
|
||||
lastError=fmt::sprintf("max number of systems is %d",DIV_MAX_CHIPS);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -98,6 +98,35 @@ enum DivMIDIModes {
|
|||
DIV_MIDI_MODE_LIGHT_SHOW
|
||||
};
|
||||
|
||||
enum DivAudioExportFormats {
|
||||
DIV_EXPORT_FORMAT_S16=0,
|
||||
DIV_EXPORT_FORMAT_F32
|
||||
};
|
||||
|
||||
struct DivAudioExportOptions {
|
||||
DivAudioExportModes mode;
|
||||
DivAudioExportFormats format;
|
||||
int sampleRate;
|
||||
int chans;
|
||||
int loops;
|
||||
double fadeOut;
|
||||
int orderBegin, orderEnd;
|
||||
bool channelMask[DIV_MAX_CHANS];
|
||||
DivAudioExportOptions():
|
||||
mode(DIV_EXPORT_MODE_ONE),
|
||||
format(DIV_EXPORT_FORMAT_S16),
|
||||
sampleRate(44100),
|
||||
chans(2),
|
||||
loops(0),
|
||||
fadeOut(0.0),
|
||||
orderBegin(-1),
|
||||
orderEnd(-1) {
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
channelMask[i]=true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DivChannelState {
|
||||
std::vector<DivDelayedCommand> delayed;
|
||||
int note, oldNote, lastIns, pitch, portaSpeed, portaNote;
|
||||
|
@ -456,7 +485,10 @@ class DivEngine {
|
|||
DivChannelState chan[DIV_MAX_CHANS];
|
||||
DivAudioEngines audioEngine;
|
||||
DivAudioExportModes exportMode;
|
||||
DivAudioExportFormats exportFormat;
|
||||
double exportFadeOut;
|
||||
int exportOutputs;
|
||||
bool exportChannelMask[DIV_MAX_CHANS];
|
||||
DivConfig conf;
|
||||
FixedQueue<DivNoteEvent,8192> pendingNotes;
|
||||
// bitfield
|
||||
|
@ -670,7 +702,7 @@ class DivEngine {
|
|||
// export to text
|
||||
SafeWriter* saveText(bool separatePatterns=true);
|
||||
// export to an audio file
|
||||
bool saveAudio(const char* path, int loops, DivAudioExportModes mode, double fadeOutTime=0.0);
|
||||
bool saveAudio(const char* path, DivAudioExportOptions options);
|
||||
// wait for audio export to finish
|
||||
void waitAudioFile();
|
||||
// stop audio file export
|
||||
|
@ -1359,7 +1391,9 @@ class DivEngine {
|
|||
haltOn(DIV_HALT_NONE),
|
||||
audioEngine(DIV_AUDIO_NULL),
|
||||
exportMode(DIV_EXPORT_MODE_ONE),
|
||||
exportFormat(DIV_EXPORT_FORMAT_S16),
|
||||
exportFadeOut(0.0),
|
||||
exportOutputs(2),
|
||||
cmdStreamInt(NULL),
|
||||
midiBaseChan(0),
|
||||
midiPoly(true),
|
||||
|
@ -1411,6 +1445,7 @@ class DivEngine {
|
|||
memset(sysDefs,0,DIV_MAX_CHIP_DEFS*sizeof(void*));
|
||||
memset(walked,0,8192);
|
||||
memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*)));
|
||||
memset(exportChannelMask,1,DIV_MAX_CHANS*sizeof(bool));
|
||||
|
||||
for (int i=0; i<DIV_MAX_CHIP_DEFS; i++) {
|
||||
sysFileMapFur[i]=DIV_SYSTEM_NULL;
|
||||
|
|
|
@ -362,6 +362,29 @@ void DivPlatformAY8930::tick(bool sysTick) {
|
|||
immWrite(regPeriodL[i],chan[i].envelope.period);
|
||||
immWrite(regPeriodH[i],chan[i].envelope.period>>8);
|
||||
}
|
||||
if (chan[i].freqChanged && chan[i].autoNoiseMode) {
|
||||
int noiseFreq=chan[i].freq;
|
||||
switch (chan[i].autoNoiseMode) {
|
||||
case 1: // noise
|
||||
noiseFreq+=chan[i].autoNoiseOff;
|
||||
if (noiseFreq<0) noiseFreq=0;
|
||||
if (noiseFreq>255) noiseFreq=255;
|
||||
rWrite(0x06,noiseFreq);
|
||||
break;
|
||||
case 2: { // noise + OR mask
|
||||
if (noiseFreq<0) noiseFreq=0;
|
||||
int noiseDiv=(noiseFreq>>8)+1;
|
||||
noiseFreq/=noiseDiv;
|
||||
ayNoiseOr=noiseDiv;
|
||||
immWrite(0x1a,ayNoiseOr);
|
||||
noiseFreq+=chan[i].autoNoiseOff;
|
||||
if (noiseFreq<0) noiseFreq=0;
|
||||
if (noiseFreq>255) noiseFreq=255;
|
||||
rWrite(0x06,noiseFreq);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
|
||||
|
@ -632,6 +655,12 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
|
|||
chan[c.chan].autoEnvDen=c.value&15;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_AY_AUTO_PWM:
|
||||
chan[c.chan].autoNoiseMode=c.value>>4;
|
||||
chan[c.chan].autoNoiseOff=c.value&15;
|
||||
if (chan[c.chan].autoNoiseOff>=8) chan[c.chan].autoNoiseOff-=16;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_AY_IO_WRITE:
|
||||
if (c.value==255) {
|
||||
immWrite(0x1f,c.value2);
|
||||
|
|
|
@ -77,8 +77,8 @@ class DivPlatformAY8930: public DivDispatch {
|
|||
setPos(false) {}
|
||||
} dac;
|
||||
|
||||
unsigned char autoEnvNum, autoEnvDen, duty;
|
||||
signed char konCycles;
|
||||
unsigned char autoEnvNum, autoEnvDen, duty, autoNoiseMode;
|
||||
signed char konCycles, autoNoiseOff;
|
||||
Channel():
|
||||
SharedChannel<int>(31),
|
||||
envelope(Envelope()),
|
||||
|
@ -88,7 +88,9 @@ class DivPlatformAY8930: public DivDispatch {
|
|||
autoEnvNum(0),
|
||||
autoEnvDen(0),
|
||||
duty(4),
|
||||
konCycles(0) {}
|
||||
autoNoiseMode(0),
|
||||
konCycles(0),
|
||||
autoNoiseOff(0) {}
|
||||
};
|
||||
Channel chan[3];
|
||||
bool isMuted[3];
|
||||
|
|
|
@ -437,14 +437,24 @@ void DivEngine::registerSystems() {
|
|||
{0x2f, {DIV_CMD_AY_IO_WRITE, "2Fxx: Write to I/O port B", constVal<1>, effectVal}},
|
||||
};
|
||||
|
||||
EffectHandlerMap ay8930PostEffectHandlerMap(ayPostEffectHandlerMap);
|
||||
ay8930PostEffectHandlerMap.insert({
|
||||
EffectHandlerMap ay8930PostEffectHandlerMap={
|
||||
{0x20, {DIV_CMD_STD_NOISE_MODE, "20xx: Set channel mode (bit 0: square; bit 1: noise; bit 2: envelope)"}},
|
||||
{0x21, {DIV_CMD_STD_NOISE_FREQ, "21xx: Set noise frequency (0 to FF)"}},
|
||||
{0x22, {DIV_CMD_AY_ENVELOPE_SET, "22xy: Set envelope mode (x: shape, y: enable for this channel)"}},
|
||||
{0x23, {DIV_CMD_AY_ENVELOPE_LOW, "23xx: Set envelope period low byte"}},
|
||||
{0x24, {DIV_CMD_AY_ENVELOPE_HIGH, "24xx: Set envelope period high byte"}},
|
||||
{0x25, {DIV_CMD_AY_ENVELOPE_SLIDE, "25xx: Envelope slide up", negEffectVal}},
|
||||
{0x26, {DIV_CMD_AY_ENVELOPE_SLIDE, "26xx: Envelope slide down"}},
|
||||
{0x29, {DIV_CMD_AY_AUTO_ENVELOPE, "29xy: Set auto-envelope (x: numerator; y: denominator)"}},
|
||||
{0x2e, {DIV_CMD_AY_IO_WRITE, "2Exx: Write to I/O port A", constVal<0>, effectVal}},
|
||||
{0x2f, {DIV_CMD_AY_IO_WRITE, "2Fxx: Write to I/O port B", constVal<1>, effectVal}},
|
||||
{0x12, {DIV_CMD_STD_NOISE_MODE, "12xx: Set duty cycle (0 to 8)",
|
||||
[](unsigned char, unsigned char val) -> int { return 0x10+(val&15); }}},
|
||||
{0x27, {DIV_CMD_AY_NOISE_MASK_AND, "27xx: Set noise AND mask"}},
|
||||
{0x28, {DIV_CMD_AY_NOISE_MASK_OR, "28xx: Set noise OR mask"}},
|
||||
{0x2c, {DIV_CMD_AY_AUTO_PWM, "2Cxy: Automatic noise frequency (x: mode (0: disable, 1: freq, 2: freq + OR mask); y: offset"}},
|
||||
{0x2d, {DIV_CMD_AY_IO_WRITE, "2Dxx: NOT TO BE EMPLOYED BY THE COMPOSER", constVal<255>, effectVal}},
|
||||
});
|
||||
};
|
||||
|
||||
EffectHandlerMap fmEffectHandlerMap={
|
||||
{0x30, {DIV_CMD_FM_HARD_RESET, "30xx: Toggle hard envelope reset on new notes"}},
|
||||
|
|
|
@ -45,8 +45,12 @@ void DivEngine::runExportThread() {
|
|||
SF_INFO si;
|
||||
SFWrapper sfWrap;
|
||||
si.samplerate=got.rate;
|
||||
si.channels=2;
|
||||
si.format=SF_FORMAT_WAV|SF_FORMAT_PCM_16;
|
||||
si.channels=exportOutputs;
|
||||
if (exportFormat==DIV_EXPORT_FORMAT_S16) {
|
||||
si.format=SF_FORMAT_WAV|SF_FORMAT_PCM_16;
|
||||
} else {
|
||||
si.format=SF_FORMAT_WAV|SF_FORMAT_FLOAT;
|
||||
}
|
||||
|
||||
sf=sfWrap.doOpen(exportPath.c_str(),SFM_WRITE,&si);
|
||||
if (sf==NULL) {
|
||||
|
@ -55,10 +59,12 @@ void DivEngine::runExportThread() {
|
|||
return;
|
||||
}
|
||||
|
||||
float* outBuf[3];
|
||||
outBuf[0]=new float[EXPORT_BUFSIZE];
|
||||
outBuf[1]=new float[EXPORT_BUFSIZE];
|
||||
outBuf[2]=new float[EXPORT_BUFSIZE*2];
|
||||
float* outBuf[DIV_MAX_OUTPUTS];
|
||||
float* outBufFinal;
|
||||
for (int i=0; i<exportOutputs; i++) {
|
||||
outBuf[i]=new float[EXPORT_BUFSIZE];
|
||||
}
|
||||
outBufFinal=new float[EXPORT_BUFSIZE*exportOutputs];
|
||||
|
||||
// take control of audio output
|
||||
deinitAudioBackend();
|
||||
|
@ -68,24 +74,27 @@ void DivEngine::runExportThread() {
|
|||
|
||||
while (playing) {
|
||||
size_t total=0;
|
||||
nextBuf(NULL,outBuf,0,2,EXPORT_BUFSIZE);
|
||||
nextBuf(NULL,outBuf,0,exportOutputs,EXPORT_BUFSIZE);
|
||||
if (totalProcessed>EXPORT_BUFSIZE) {
|
||||
logE("error: total processed is bigger than export bufsize! %d>%d",totalProcessed,EXPORT_BUFSIZE);
|
||||
totalProcessed=EXPORT_BUFSIZE;
|
||||
}
|
||||
int fi=0;
|
||||
for (int i=0; i<(int)totalProcessed; i++) {
|
||||
total++;
|
||||
if (isFadingOut) {
|
||||
double mul=(1.0-((double)curFadeOutSample/(double)fadeOutSamples));
|
||||
outBuf[2][i<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][i]))*mul;
|
||||
outBuf[2][1+(i<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][i]))*mul;
|
||||
for (int j=0; j<exportOutputs; j++) {
|
||||
outBufFinal[fi++]=MAX(-1.0f,MIN(1.0f,outBuf[j][i]))*mul;
|
||||
}
|
||||
if (++curFadeOutSample>=fadeOutSamples) {
|
||||
playing=false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
outBuf[2][i<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][i]));
|
||||
outBuf[2][1+(i<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][i]));
|
||||
for (int j=0; j<exportOutputs; j++) {
|
||||
outBufFinal[fi++]=MAX(-1.0f,MIN(1.0f,outBuf[j][i]));
|
||||
}
|
||||
if (lastLoopPos>-1 && i>=lastLoopPos && totalLoops>=exportLoopCount) {
|
||||
logD("start fading out...");
|
||||
isFadingOut=true;
|
||||
|
@ -94,15 +103,16 @@ void DivEngine::runExportThread() {
|
|||
}
|
||||
}
|
||||
|
||||
if (sf_writef_float(sf,outBuf[2],total)!=(int)total) {
|
||||
if (sf_writef_float(sf,outBufFinal,total)!=(int)total) {
|
||||
logE("error: failed to write entire buffer!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] outBuf[0];
|
||||
delete[] outBuf[1];
|
||||
delete[] outBuf[2];
|
||||
delete[] outBufFinal;
|
||||
for (int i=0; i<exportOutputs; i++) {
|
||||
delete[] outBuf[i];
|
||||
}
|
||||
|
||||
if (sfWrap.doClose()!=0) {
|
||||
logE("could not close audio file!");
|
||||
|
@ -237,22 +247,29 @@ void DivEngine::runExportThread() {
|
|||
// take control of audio output
|
||||
deinitAudioBackend();
|
||||
|
||||
float* outBuf[3];
|
||||
outBuf[0]=new float[EXPORT_BUFSIZE];
|
||||
outBuf[1]=new float[EXPORT_BUFSIZE];
|
||||
outBuf[2]=new float[EXPORT_BUFSIZE*2];
|
||||
float* outBuf[DIV_MAX_OUTPUTS];
|
||||
float* outBufFinal;
|
||||
for (int i=0; i<exportOutputs; i++) {
|
||||
outBuf[i]=new float[EXPORT_BUFSIZE];
|
||||
}
|
||||
outBufFinal=new float[EXPORT_BUFSIZE*exportOutputs];
|
||||
|
||||
logI("rendering to files...");
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
if (!exportChannelMask[i]) continue;
|
||||
SNDFILE* sf;
|
||||
SF_INFO si;
|
||||
SFWrapper sfWrap;
|
||||
String fname=fmt::sprintf("%s_c%02d.wav",exportPath,i+1);
|
||||
logI("- %s",fname.c_str());
|
||||
si.samplerate=got.rate;
|
||||
si.channels=2;
|
||||
si.format=SF_FORMAT_WAV|SF_FORMAT_PCM_16;
|
||||
si.channels=exportOutputs;
|
||||
if (exportFormat==DIV_EXPORT_FORMAT_S16) {
|
||||
si.format=SF_FORMAT_WAV|SF_FORMAT_PCM_16;
|
||||
} else {
|
||||
si.format=SF_FORMAT_WAV|SF_FORMAT_FLOAT;
|
||||
}
|
||||
|
||||
sf=sfWrap.doOpen(fname.c_str(),SFM_WRITE,&si);
|
||||
if (sf==NULL) {
|
||||
|
@ -287,24 +304,27 @@ void DivEngine::runExportThread() {
|
|||
|
||||
while (playing) {
|
||||
size_t total=0;
|
||||
nextBuf(NULL,outBuf,0,2,EXPORT_BUFSIZE);
|
||||
nextBuf(NULL,outBuf,0,exportOutputs,EXPORT_BUFSIZE);
|
||||
if (totalProcessed>EXPORT_BUFSIZE) {
|
||||
logE("error: total processed is bigger than export bufsize! %d>%d",totalProcessed,EXPORT_BUFSIZE);
|
||||
totalProcessed=EXPORT_BUFSIZE;
|
||||
}
|
||||
int fi=0;
|
||||
for (int j=0; j<(int)totalProcessed; j++) {
|
||||
total++;
|
||||
if (isFadingOut) {
|
||||
double mul=(1.0-((double)curFadeOutSample/(double)fadeOutSamples));
|
||||
outBuf[2][j<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][j]))*mul;
|
||||
outBuf[2][1+(j<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][j]))*mul;
|
||||
for (int k=0; k<exportOutputs; k++) {
|
||||
outBufFinal[fi++]=MAX(-1.0f,MIN(1.0f,outBuf[k][j]))*mul;
|
||||
}
|
||||
if (++curFadeOutSample>=fadeOutSamples) {
|
||||
playing=false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
outBuf[2][j<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][j]));
|
||||
outBuf[2][1+(j<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][j]));
|
||||
for (int k=0; k<exportOutputs; k++) {
|
||||
outBufFinal[fi++]=MAX(-1.0f,MIN(1.0f,outBuf[k][j]));
|
||||
}
|
||||
if (lastLoopPos>-1 && j>=lastLoopPos && totalLoops>=exportLoopCount) {
|
||||
logD("start fading out...");
|
||||
isFadingOut=true;
|
||||
|
@ -312,7 +332,7 @@ void DivEngine::runExportThread() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (sf_writef_float(sf,outBuf[2],total)!=(int)total) {
|
||||
if (sf_writef_float(sf,outBufFinal,total)!=(int)total) {
|
||||
logE("error: failed to write entire buffer!");
|
||||
break;
|
||||
}
|
||||
|
@ -335,9 +355,10 @@ void DivEngine::runExportThread() {
|
|||
if (stopExport) break;
|
||||
}
|
||||
|
||||
delete[] outBuf[0];
|
||||
delete[] outBuf[1];
|
||||
delete[] outBuf[2];
|
||||
delete[] outBufFinal;
|
||||
for (int i=0; i<exportOutputs; i++) {
|
||||
delete[] outBuf[i];
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
isMuted[i]=false;
|
||||
|
@ -369,18 +390,19 @@ void DivEngine::runExportThread() {
|
|||
#endif
|
||||
|
||||
bool DivEngine::shallSwitchCores() {
|
||||
// TODO: detect whether we should
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode, double fadeOutTime) {
|
||||
bool DivEngine::saveAudio(const char* path, DivAudioExportOptions options) {
|
||||
#ifndef HAVE_SNDFILE
|
||||
logE("Furnace was not compiled with libsndfile. cannot export!");
|
||||
return false;
|
||||
#else
|
||||
exportPath=path;
|
||||
exportMode=mode;
|
||||
exportFadeOut=fadeOutTime;
|
||||
exportMode=options.mode;
|
||||
exportFormat=options.format;
|
||||
exportFadeOut=options.fadeOut;
|
||||
memcpy(exportChannelMask,options.channelMask,DIV_MAX_CHANS*sizeof(bool));
|
||||
if (exportMode!=DIV_EXPORT_MODE_ONE) {
|
||||
// remove extension
|
||||
String lowerCase=exportPath;
|
||||
|
@ -398,6 +420,7 @@ bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode,
|
|||
repeatPattern=false;
|
||||
setOrder(0);
|
||||
remainingLoops=-1;
|
||||
got.rate=options.sampleRate;
|
||||
|
||||
if (shallSwitchCores()) {
|
||||
bool isMutedBefore[DIV_MAX_CHANS];
|
||||
|
@ -412,7 +435,11 @@ bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode,
|
|||
}
|
||||
}
|
||||
|
||||
exportLoopCount=loops;
|
||||
exportOutputs=options.chans;
|
||||
if (exportOutputs<1) exportOutputs=1;
|
||||
if (exportOutputs>DIV_MAX_OUTPUTS) exportOutputs=DIV_MAX_OUTPUTS;
|
||||
|
||||
exportLoopCount=options.loops+1;
|
||||
exportThread=new std::thread(_runExportThread,this);
|
||||
return true;
|
||||
#endif
|
||||
|
|
|
@ -196,6 +196,10 @@ void FurnaceGUI::drawCompatFlags() {
|
|||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("behavior changed in 0.6.1\nthis flag will be removed if I find out that none of the songs break after disabling it.");
|
||||
}
|
||||
ImGui::Checkbox("Old sample offset effect",&e->song.oldSampleOffset);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("behavior changed in 0.6.3");
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem(".mod import")) {
|
||||
|
|
|
@ -555,6 +555,18 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_PAT_SELECTION_DOWN_COARSE:
|
||||
moveCursor(0,editStepCoarse,true);
|
||||
break;
|
||||
case GUI_ACTION_PAT_MOVE_UP:
|
||||
moveSelected(0,-1);
|
||||
break;
|
||||
case GUI_ACTION_PAT_MOVE_DOWN:
|
||||
moveSelected(0,1);
|
||||
break;
|
||||
case GUI_ACTION_PAT_MOVE_LEFT_CHANNEL:
|
||||
moveSelected(-1,0);
|
||||
break;
|
||||
case GUI_ACTION_PAT_MOVE_RIGHT_CHANNEL:
|
||||
moveSelected(1,0);
|
||||
break;
|
||||
case GUI_ACTION_PAT_DELETE:
|
||||
doDelete();
|
||||
if (settings.stepOnDelete) {
|
||||
|
|
|
@ -1842,6 +1842,25 @@ void FurnaceGUI::doDrag() {
|
|||
makeUndo(GUI_UNDO_PATTERN_DRAG);
|
||||
}
|
||||
|
||||
void FurnaceGUI::moveSelected(int x, int y) {
|
||||
prepareUndo(GUI_UNDO_PATTERN_DRAG);
|
||||
|
||||
// copy and clear
|
||||
String c=doCopy(true,false,selStart,selEnd);
|
||||
|
||||
logV("copy: %s",c);
|
||||
|
||||
// replace
|
||||
selStart.xCoarse+=x;
|
||||
selEnd.xCoarse+=x;
|
||||
selStart.y+=y;
|
||||
selEnd.y+=y;
|
||||
cursor=selStart;
|
||||
doPaste(GUI_PASTE_MODE_NORMAL,0,false,c);
|
||||
|
||||
makeUndo(GUI_UNDO_PATTERN_DRAG);
|
||||
}
|
||||
|
||||
void FurnaceGUI::doUndo() {
|
||||
if (undoHist.empty()) return;
|
||||
UndoStep& us=undoHist.back();
|
||||
|
|
|
@ -26,14 +26,83 @@
|
|||
void FurnaceGUI::drawExportAudio(bool onWindow) {
|
||||
exitDisabledTimer=1;
|
||||
|
||||
ImGui::RadioButton("one file",&audioExportType,0);
|
||||
ImGui::RadioButton("multiple files (one per chip)",&audioExportType,1);
|
||||
ImGui::RadioButton("multiple files (one per channel)",&audioExportType,2);
|
||||
if (ImGui::InputInt("Loops",&exportLoops,1,2)) {
|
||||
if (exportLoops<0) exportLoops=0;
|
||||
ImGui::Text("Export type:");
|
||||
|
||||
ImGui::Indent();
|
||||
if (ImGui::RadioButton("one file",audioExportOptions.mode==DIV_EXPORT_MODE_ONE)) {
|
||||
audioExportOptions.mode=DIV_EXPORT_MODE_ONE;
|
||||
}
|
||||
if (ImGui::InputDouble("Fade out (seconds)",&exportFadeOut,1.0,2.0,"%.1f")) {
|
||||
if (exportFadeOut<0.0) exportFadeOut=0.0;
|
||||
if (ImGui::RadioButton("multiple files (one per chip)",audioExportOptions.mode==DIV_EXPORT_MODE_MANY_SYS)) {
|
||||
audioExportOptions.mode=DIV_EXPORT_MODE_MANY_SYS;
|
||||
}
|
||||
if (ImGui::RadioButton("multiple files (one per channel)",audioExportOptions.mode==DIV_EXPORT_MODE_MANY_CHAN)) {
|
||||
audioExportOptions.mode=DIV_EXPORT_MODE_MANY_CHAN;
|
||||
}
|
||||
ImGui::Unindent();
|
||||
|
||||
if (audioExportOptions.mode!=DIV_EXPORT_MODE_MANY_SYS) {
|
||||
ImGui::Text("Bit depth:");
|
||||
ImGui::Indent();
|
||||
if (ImGui::RadioButton("16-bit integer",audioExportOptions.format==DIV_EXPORT_FORMAT_S16)) {
|
||||
audioExportOptions.format=DIV_EXPORT_FORMAT_S16;
|
||||
}
|
||||
if (ImGui::RadioButton("32-bit float",audioExportOptions.format==DIV_EXPORT_FORMAT_F32)) {
|
||||
audioExportOptions.format=DIV_EXPORT_FORMAT_F32;
|
||||
}
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
if (ImGui::InputInt("Sample rate",&audioExportOptions.sampleRate,100,10000)) {
|
||||
if (audioExportOptions.sampleRate<8000) audioExportOptions.sampleRate=8000;
|
||||
if (audioExportOptions.sampleRate>384000) audioExportOptions.sampleRate=384000;
|
||||
}
|
||||
|
||||
if (audioExportOptions.mode!=DIV_EXPORT_MODE_MANY_SYS) {
|
||||
if (ImGui::InputInt("Channels in file",&audioExportOptions.chans,1,1)) {
|
||||
if (audioExportOptions.chans<1) audioExportOptions.chans=1;
|
||||
if (audioExportOptions.chans>16) audioExportOptions.chans=16;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::InputInt("Loops",&audioExportOptions.loops,1,2)) {
|
||||
if (audioExportOptions.loops<0) audioExportOptions.loops=0;
|
||||
}
|
||||
if (ImGui::InputDouble("Fade out (seconds)",&audioExportOptions.fadeOut,1.0,2.0,"%.1f")) {
|
||||
if (audioExportOptions.fadeOut<0.0) audioExportOptions.fadeOut=0.0;
|
||||
}
|
||||
|
||||
bool isOneOn=false;
|
||||
if (audioExportOptions.mode==DIV_EXPORT_MODE_MANY_CHAN) {
|
||||
ImGui::Text("Channels to export:");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("All")) {
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
audioExportOptions.channelMask[i]=true;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("None")) {
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
audioExportOptions.channelMask[i]=false;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Invert")) {
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
audioExportOptions.channelMask[i]=!audioExportOptions.channelMask[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginChild("Channel Selection",ImVec2(0,200.0f*dpiScale))) {
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
String name=fmt::sprintf("%d. %s##_CE%d",i+1,e->getChannelName(i),i);
|
||||
ImGui::Checkbox(name.c_str(),&audioExportOptions.channelMask[i]);
|
||||
if (audioExportOptions.channelMask[i]) isOneOn=true;
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
} else {
|
||||
isOneOn=true;
|
||||
}
|
||||
|
||||
if (onWindow) {
|
||||
|
@ -42,19 +111,23 @@ void FurnaceGUI::drawExportAudio(bool onWindow) {
|
|||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) {
|
||||
switch (audioExportType) {
|
||||
case 0:
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
|
||||
break;
|
||||
case 1:
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_SYS);
|
||||
break;
|
||||
case 2:
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_CHANNEL);
|
||||
break;
|
||||
if (isOneOn) {
|
||||
if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) {
|
||||
switch (audioExportOptions.mode) {
|
||||
case DIV_EXPORT_MODE_ONE:
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
|
||||
break;
|
||||
case DIV_EXPORT_MODE_MANY_SYS:
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_SYS);
|
||||
break;
|
||||
case DIV_EXPORT_MODE_MANY_CHAN:
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_CHANNEL);
|
||||
break;
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
} else {
|
||||
ImGui::Text("select at least one channel");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2436,7 +2436,7 @@ int FurnaceGUI::loadStream(String path) {
|
|||
|
||||
|
||||
void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) {
|
||||
e->saveAudio(path.c_str(),exportLoops+1,mode,exportFadeOut);
|
||||
e->saveAudio(path.c_str(),audioExportOptions);
|
||||
displayExporting=true;
|
||||
}
|
||||
|
||||
|
@ -6761,10 +6761,10 @@ bool FurnaceGUI::init() {
|
|||
followOrders=e->getConfBool("followOrders",true);
|
||||
followPattern=e->getConfBool("followPattern",true);
|
||||
noteInputPoly=e->getConfBool("noteInputPoly",true);
|
||||
exportLoops=e->getConfInt("exportLoops",0);
|
||||
if (exportLoops<0) exportLoops=0;
|
||||
exportFadeOut=e->getConfDouble("exportFadeOut",0.0);
|
||||
if (exportFadeOut<0.0) exportFadeOut=0.0;
|
||||
audioExportOptions.loops=e->getConfInt("exportLoops",0);
|
||||
if (audioExportOptions.loops<0) audioExportOptions.loops=0;
|
||||
audioExportOptions.fadeOut=e->getConfDouble("exportFadeOut",0.0);
|
||||
if (audioExportOptions.fadeOut<0.0) audioExportOptions.fadeOut=0.0;
|
||||
orderEditMode=e->getConfInt("orderEditMode",0);
|
||||
if (orderEditMode<0) orderEditMode=0;
|
||||
if (orderEditMode>3) orderEditMode=3;
|
||||
|
@ -6826,8 +6826,8 @@ bool FurnaceGUI::init() {
|
|||
syncTutorial();
|
||||
|
||||
if (!settings.persistFadeOut) {
|
||||
exportLoops=settings.exportLoops;
|
||||
exportFadeOut=settings.exportFadeOut;
|
||||
audioExportOptions.loops=settings.exportLoops;
|
||||
audioExportOptions.fadeOut=settings.exportFadeOut;
|
||||
}
|
||||
|
||||
for (int i=0; i<settings.maxRecentFile; i++) {
|
||||
|
@ -6982,12 +6982,19 @@ bool FurnaceGUI::init() {
|
|||
return false;
|
||||
}
|
||||
|
||||
rend->preInit();
|
||||
rend->preInit(e->getConfObject());
|
||||
|
||||
logD("creating window...");
|
||||
sdlWin=SDL_CreateWindow("Furnace",scrX,scrY,scrW,scrH,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI|(scrMax?SDL_WINDOW_MAXIMIZED:0)|(fullScreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0)|rend->getWindowFlags());
|
||||
if (sdlWin==NULL) {
|
||||
lastError=fmt::sprintf("could not open window! %s",SDL_GetError());
|
||||
const char* sdlErr=SDL_GetError();
|
||||
lastError=fmt::sprintf("could not open window! %s",sdlErr);
|
||||
if (settings.renderBackend!="Software" && strcmp(sdlErr,"No matching GL pixel format available")==0) {
|
||||
settings.renderBackend="Software";
|
||||
e->setConf("renderBackend","Software");
|
||||
e->saveConf();
|
||||
lastError+="\r\nfalling back to software renderer. please restart Furnace.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -7313,8 +7320,8 @@ void FurnaceGUI::commitState() {
|
|||
e->setConf("orderEditMode",orderEditMode);
|
||||
e->setConf("noteInputPoly",noteInputPoly);
|
||||
if (settings.persistFadeOut) {
|
||||
e->setConf("exportLoops",exportLoops);
|
||||
e->setConf("exportFadeOut",exportFadeOut);
|
||||
e->setConf("exportLoops",audioExportOptions.loops);
|
||||
e->setConf("exportFadeOut",audioExportOptions.fadeOut);
|
||||
}
|
||||
|
||||
// commit oscilloscope state
|
||||
|
@ -7574,7 +7581,6 @@ FurnaceGUI::FurnaceGUI():
|
|||
oldRow(0),
|
||||
editStep(1),
|
||||
editStepCoarse(16),
|
||||
exportLoops(0),
|
||||
soloChan(-1),
|
||||
orderEditMode(0),
|
||||
orderCursor(-1),
|
||||
|
@ -7597,7 +7603,6 @@ FurnaceGUI::FurnaceGUI():
|
|||
curPaletteChoice(0),
|
||||
curPaletteType(0),
|
||||
soloTimeout(0.0f),
|
||||
exportFadeOut(5.0),
|
||||
patExtraButtons(false),
|
||||
patChannelNames(false),
|
||||
patChannelPairs(true),
|
||||
|
@ -7948,7 +7953,6 @@ FurnaceGUI::FurnaceGUI():
|
|||
introStopped(false),
|
||||
curTutorial(-1),
|
||||
curTutorialStep(0),
|
||||
audioExportType(0),
|
||||
dmfExportVersion(0),
|
||||
curExportType(GUI_EXPORT_NONE) {
|
||||
// value keys
|
||||
|
|
|
@ -778,6 +778,10 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_PAT_SELECTION_END,
|
||||
GUI_ACTION_PAT_SELECTION_UP_COARSE,
|
||||
GUI_ACTION_PAT_SELECTION_DOWN_COARSE,
|
||||
GUI_ACTION_PAT_MOVE_UP,
|
||||
GUI_ACTION_PAT_MOVE_DOWN,
|
||||
GUI_ACTION_PAT_MOVE_LEFT_CHANNEL,
|
||||
GUI_ACTION_PAT_MOVE_RIGHT_CHANNEL,
|
||||
GUI_ACTION_PAT_DELETE,
|
||||
GUI_ACTION_PAT_PULL_DELETE,
|
||||
GUI_ACTION_PAT_INSERT,
|
||||
|
@ -1505,7 +1509,7 @@ class FurnaceGUIRender {
|
|||
virtual const char* getDeviceName();
|
||||
virtual const char* getAPIVersion();
|
||||
virtual void setSwapInterval(int swapInterval);
|
||||
virtual void preInit();
|
||||
virtual void preInit(const DivConfig& conf);
|
||||
virtual bool init(SDL_Window* win, int swapInterval);
|
||||
virtual void initGUI(SDL_Window* win);
|
||||
virtual void quitGUI();
|
||||
|
@ -1892,6 +1896,12 @@ class FurnaceGUI {
|
|||
int frameRateLimit;
|
||||
int displayRenderTime;
|
||||
int inputRepeat;
|
||||
int glRedSize;
|
||||
int glGreenSize;
|
||||
int glBlueSize;
|
||||
int glAlphaSize;
|
||||
int glDepthSize;
|
||||
int glDoubleBuffer;
|
||||
unsigned int maxUndoSteps;
|
||||
float vibrationStrength;
|
||||
int vibrationLength;
|
||||
|
@ -2135,6 +2145,12 @@ class FurnaceGUI {
|
|||
frameRateLimit(60),
|
||||
displayRenderTime(0),
|
||||
inputRepeat(0),
|
||||
glRedSize(8),
|
||||
glGreenSize(8),
|
||||
glBlueSize(8),
|
||||
glAlphaSize(0),
|
||||
glDepthSize(24),
|
||||
glDoubleBuffer(1),
|
||||
maxUndoSteps(100),
|
||||
vibrationStrength(0.5f),
|
||||
vibrationLength(20),
|
||||
|
@ -2178,15 +2194,13 @@ class FurnaceGUI {
|
|||
int pendingLayoutImportStep;
|
||||
FixedQueue<bool*,64> pendingLayoutImportReopen;
|
||||
|
||||
int curIns, curWave, curSample, curOctave, curOrder, playOrder, prevIns, oldRow, editStep, editStepCoarse, exportLoops, soloChan, orderEditMode, orderCursor;
|
||||
int curIns, curWave, curSample, curOctave, curOrder, playOrder, prevIns, oldRow, editStep, editStepCoarse, soloChan, orderEditMode, orderCursor;
|
||||
int loopOrder, loopRow, loopEnd, isClipping, newSongCategory, latchTarget;
|
||||
int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar;
|
||||
int curGroove, exitDisabledTimer;
|
||||
int curPaletteChoice, curPaletteType;
|
||||
float soloTimeout;
|
||||
|
||||
double exportFadeOut;
|
||||
|
||||
bool patExtraButtons, patChannelNames, patChannelPairs;
|
||||
unsigned char patChannelHints;
|
||||
|
||||
|
@ -2587,7 +2601,7 @@ class FurnaceGUI {
|
|||
ImGuiListClipper csClipper;
|
||||
|
||||
// export options
|
||||
int audioExportType;
|
||||
DivAudioExportOptions audioExportOptions;
|
||||
int dmfExportVersion;
|
||||
FurnaceGUIExportTypes curExportType;
|
||||
|
||||
|
@ -2665,6 +2679,7 @@ class FurnaceGUI {
|
|||
void drawMacros(std::vector<FurnaceGUIMacroDesc>& macros, FurnaceGUIMacroEditState& state);
|
||||
void alterSampleMap(int column, int val);
|
||||
|
||||
void insTabFM(DivInstrument* ins);
|
||||
void insTabSample(DivInstrument* ins);
|
||||
|
||||
void drawOrderButtons();
|
||||
|
@ -2782,6 +2797,7 @@ class FurnaceGUI {
|
|||
void doDelete();
|
||||
void doPullDelete();
|
||||
void doInsert();
|
||||
void moveSelected(int x, int y);
|
||||
void doTranspose(int amount, OperationMask& mask);
|
||||
String doCopy(bool cut, bool writeClipboard, const SelectionPoint& sStart, const SelectionPoint& sEnd);
|
||||
void doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector<String> data, int startOff, bool invalidData, UndoRegion ur);
|
||||
|
|
|
@ -659,6 +659,10 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("PAT_SELECTION_END", "Expand selection to end of pattern", 0),
|
||||
D("PAT_SELECTION_UP_COARSE", "Expand selection upwards (coarse)", FURKMOD_SHIFT|SDLK_PAGEUP),
|
||||
D("PAT_SELECTION_DOWN_COARSE", "Expand selection downwards (coarse)", FURKMOD_SHIFT|SDLK_PAGEDOWN),
|
||||
D("PAT_MOVE_UP", "Move selection up", FURKMOD_ALT|SDLK_UP),
|
||||
D("PAT_MOVE_DOWN", "Move selection down", FURKMOD_ALT|SDLK_DOWN),
|
||||
D("PAT_MOVE_LEFT_CHANNEL", "Move selection to previous channel", FURKMOD_ALT|SDLK_LEFT),
|
||||
D("PAT_MOVE_RIGHT_CHANNEL", "Move selection to next channel", FURKMOD_ALT|SDLK_RIGHT),
|
||||
D("PAT_DELETE", "Delete", SDLK_DELETE),
|
||||
D("PAT_PULL_DELETE", "Pull delete", SDLK_BACKSPACE),
|
||||
D("PAT_INSERT", "Insert", SDLK_INSERT),
|
||||
|
|
5449
src/gui/insEdit.cpp
5449
src/gui/insEdit.cpp
File diff suppressed because it is too large
Load diff
|
@ -128,7 +128,7 @@ const char* FurnaceGUIRender::getAPIVersion() {
|
|||
void FurnaceGUIRender::setSwapInterval(int swapInterval) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::preInit() {
|
||||
void FurnaceGUIRender::preInit(const DivConfig& conf) {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::init(SDL_Window* win, int swapInterval) {
|
||||
|
|
|
@ -397,7 +397,7 @@ void FurnaceGUIRenderDX11::setSwapInterval(int swapInt) {
|
|||
swapInterval=swapInt;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::preInit() {
|
||||
void FurnaceGUIRenderDX11::preInit(const DivConfig& conf) {
|
||||
}
|
||||
|
||||
const float wipeVertices[4][4]={
|
||||
|
|
|
@ -89,7 +89,7 @@ class FurnaceGUIRenderDX11: public FurnaceGUIRender {
|
|||
const char* getDeviceName();
|
||||
const char* getAPIVersion();
|
||||
void setSwapInterval(int swapInterval);
|
||||
void preInit();
|
||||
void preInit(const DivConfig& conf);
|
||||
bool init(SDL_Window* win, int swapInterval);
|
||||
void initGUI(SDL_Window* win);
|
||||
void quitGUI();
|
||||
|
|
|
@ -580,7 +580,7 @@ void FurnaceGUIRenderGL::setSwapInterval(int swapInterval) {
|
|||
}
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::preInit() {
|
||||
void FurnaceGUIRenderGL::preInit(const DivConfig& conf) {
|
||||
#if defined(USE_GLES)
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,SDL_GL_CONTEXT_PROFILE_ES);
|
||||
|
@ -603,12 +603,12 @@ void FurnaceGUIRenderGL::preInit() {
|
|||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,0);
|
||||
#endif
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,0);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,24);
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,conf.getInt("glRedSize",8));
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,conf.getInt("glGreenSize",8));
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,conf.getInt("glBlueSize",8));
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,conf.getInt("glAlphaSize",0));
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,conf.getInt("glDoubleBuffer",1));
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,conf.getInt("glDepthSize",24));
|
||||
}
|
||||
|
||||
#define LOAD_PROC_MANDATORY(_v,_t,_s) \
|
||||
|
|
|
@ -82,7 +82,7 @@ class FurnaceGUIRenderGL: public FurnaceGUIRender {
|
|||
const char* getDeviceName();
|
||||
const char* getAPIVersion();
|
||||
void setSwapInterval(int swapInterval);
|
||||
void preInit();
|
||||
void preInit(const DivConfig& conf);
|
||||
bool init(SDL_Window* win, int swapInterval);
|
||||
void initGUI(SDL_Window* win);
|
||||
void quitGUI();
|
||||
|
|
|
@ -245,18 +245,18 @@ void FurnaceGUIRenderGL1::setSwapInterval(int swapInterval) {
|
|||
}
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL1::preInit() {
|
||||
void FurnaceGUIRenderGL1::preInit(const DivConfig& conf) {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,1);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,1);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,0);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,24);
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,conf.getInt("glRedSize",8));
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,conf.getInt("glGreenSize",8));
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,conf.getInt("glBlueSize",8));
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,conf.getInt("glAlphaSize",0));
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,conf.getInt("glDoubleBuffer",1));
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,conf.getInt("glDepthSize",24));
|
||||
}
|
||||
|
||||
#define LOAD_PROC_MANDATORY(_v,_t,_s) \
|
||||
|
@ -334,4 +334,4 @@ void FurnaceGUIRenderGL1::quitGUI() {
|
|||
// sadly, OpenGL 1.1 doesn't have the ability to recover from death...
|
||||
bool FurnaceGUIRenderGL1::isDead() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ class FurnaceGUIRenderGL1: public FurnaceGUIRender {
|
|||
const char* getDeviceName();
|
||||
const char* getAPIVersion();
|
||||
void setSwapInterval(int swapInterval);
|
||||
void preInit();
|
||||
void preInit(const DivConfig& conf);
|
||||
bool init(SDL_Window* win, int swapInterval);
|
||||
void initGUI(SDL_Window* win);
|
||||
void quitGUI();
|
||||
|
|
|
@ -52,7 +52,7 @@ class FurnaceGUIRenderMetal: public FurnaceGUIRender {
|
|||
const char* getDeviceName();
|
||||
const char* getAPIVersion();
|
||||
void setSwapInterval(int swapInterval);
|
||||
void preInit();
|
||||
void preInit(const DivConfig& conf);
|
||||
bool init(SDL_Window* win, int swapInterval);
|
||||
void initGUI(SDL_Window* win);
|
||||
void quitGUI();
|
||||
|
|
|
@ -218,7 +218,7 @@ void FurnaceGUIRenderMetal::setSwapInterval(int swapInterval) {
|
|||
}
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderMetal::preInit() {
|
||||
void FurnaceGUIRenderMetal::preInit(const DivConfig& conf) {
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER,"metal");
|
||||
priv=new FurnaceGUIRenderMetalPrivate;
|
||||
}
|
||||
|
|
|
@ -182,7 +182,7 @@ void FurnaceGUIRenderSDL::setSwapInterval(int swapInterval) {
|
|||
}
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::preInit() {
|
||||
void FurnaceGUIRenderSDL::preInit(const DivConfig& conf) {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::init(SDL_Window* win, int swapInterval) {
|
||||
|
|
|
@ -50,7 +50,7 @@ class FurnaceGUIRenderSDL: public FurnaceGUIRender {
|
|||
const char* getDeviceName();
|
||||
const char* getAPIVersion();
|
||||
void setSwapInterval(int swapInterval);
|
||||
void preInit();
|
||||
void preInit(const DivConfig& conf);
|
||||
bool init(SDL_Window* win, int swapInterval);
|
||||
void initGUI(SDL_Window* win);
|
||||
void quitGUI();
|
||||
|
|
|
@ -160,7 +160,7 @@ const char* FurnaceGUIRenderSoftware::getAPIVersion() {
|
|||
void FurnaceGUIRenderSoftware::setSwapInterval(int swapInterval) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSoftware::preInit() {
|
||||
void FurnaceGUIRenderSoftware::preInit(const DivConfig& conf) {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSoftware::init(SDL_Window* win, int swapInterval) {
|
||||
|
|
|
@ -47,7 +47,7 @@ class FurnaceGUIRenderSoftware: public FurnaceGUIRender {
|
|||
const char* getDeviceName();
|
||||
const char* getAPIVersion();
|
||||
void setSwapInterval(int swapInterval);
|
||||
void preInit();
|
||||
void preInit(const DivConfig& conf);
|
||||
bool init(SDL_Window* win, int swapInterval);
|
||||
void initGUI(SDL_Window* win);
|
||||
void quitGUI();
|
||||
|
|
|
@ -449,23 +449,61 @@ void FurnaceGUI::drawSettings() {
|
|||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("you may need to restart Furnace for this setting to take effect.");
|
||||
}
|
||||
if (curRenderBackend=="SDL") {
|
||||
if (ImGui::BeginCombo("Render driver",settings.renderDriver.empty()?"Automatic":settings.renderDriver.c_str())) {
|
||||
if (ImGui::Selectable("Automatic",settings.renderDriver.empty())) {
|
||||
settings.renderDriver="";
|
||||
settingsChanged=true;
|
||||
}
|
||||
for (String& i: availRenderDrivers) {
|
||||
if (ImGui::Selectable(i.c_str(),i==settings.renderDriver)) {
|
||||
settings.renderDriver=i;
|
||||
|
||||
if (ImGui::TreeNode("Advanced render backend settings")) {
|
||||
if (curRenderBackend=="SDL") {
|
||||
if (ImGui::BeginCombo("Render driver",settings.renderDriver.empty()?"Automatic":settings.renderDriver.c_str())) {
|
||||
if (ImGui::Selectable("Automatic",settings.renderDriver.empty())) {
|
||||
settings.renderDriver="";
|
||||
settingsChanged=true;
|
||||
}
|
||||
for (String& i: availRenderDrivers) {
|
||||
if (ImGui::Selectable(i.c_str(),i==settings.renderDriver)) {
|
||||
settings.renderDriver=i;
|
||||
settingsChanged=true;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("you may need to restart Furnace for this setting to take effect.");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("you may need to restart Furnace for this setting to take effect.");
|
||||
}
|
||||
} else if (curRenderBackend.find("OpenGL")==0) {
|
||||
ImGui::TextWrapped("beware: changing these settings may render Furnace unusable! do so at your own risk.\nstart Furnace with -safemode if you mess something up.");
|
||||
if (ImGui::InputInt("Red bits",&settings.glRedSize)) {
|
||||
if (settings.glRedSize<0) settings.glRedSize=0;
|
||||
if (settings.glRedSize>32) settings.glRedSize=32;
|
||||
settingsChanged=true;
|
||||
}
|
||||
if (ImGui::InputInt("Green bits",&settings.glGreenSize)) {
|
||||
if (settings.glGreenSize<0) settings.glGreenSize=0;
|
||||
if (settings.glGreenSize>32) settings.glGreenSize=32;
|
||||
settingsChanged=true;
|
||||
}
|
||||
if (ImGui::InputInt("Blue bits",&settings.glBlueSize)) {
|
||||
if (settings.glBlueSize<0) settings.glBlueSize=0;
|
||||
if (settings.glBlueSize>32) settings.glBlueSize=32;
|
||||
settingsChanged=true;
|
||||
}
|
||||
if (ImGui::InputInt("Alpha bits",&settings.glAlphaSize)) {
|
||||
if (settings.glAlphaSize<0) settings.glAlphaSize=0;
|
||||
if (settings.glAlphaSize>32) settings.glAlphaSize=32;
|
||||
settingsChanged=true;
|
||||
}
|
||||
if (ImGui::InputInt("Color depth",&settings.glDepthSize)) {
|
||||
if (settings.glDepthSize<0) settings.glDepthSize=0;
|
||||
if (settings.glDepthSize>128) settings.glDepthSize=128;
|
||||
settingsChanged=true;
|
||||
}
|
||||
bool glDoubleBufferB=settings.glDoubleBuffer;
|
||||
if (ImGui::Checkbox("Double buffer",&glDoubleBufferB)) {
|
||||
settings.glDoubleBuffer=glDoubleBufferB;
|
||||
settingsChanged=true;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped("the following values are common (in red, green, blue, alpha order):\n- 24 bits: 8, 8, 8, 0\n- 16 bits: 5, 6, 5, 0\n- 32 bits (with alpha): 8, 8, 8, 8\n- 30 bits (deep): 10, 10, 10, 0");
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::TextWrapped("current backend: %s\n%s\n%s\n%s",rend->getBackendName(),rend->getVendorName(),rend->getDeviceName(),rend->getAPIVersion());
|
||||
|
@ -667,13 +705,13 @@ void FurnaceGUI::drawSettings() {
|
|||
ImGui::BeginDisabled(settings.persistFadeOut);
|
||||
ImGui::Indent();
|
||||
if (ImGui::InputInt("Loops",&settings.exportLoops,1,2)) {
|
||||
if (exportLoops<0) exportLoops=0;
|
||||
exportLoops=settings.exportLoops;
|
||||
if (settings.exportLoops<0) settings.exportLoops=0;
|
||||
audioExportOptions.loops=settings.exportLoops;
|
||||
settingsChanged=true;
|
||||
}
|
||||
if (ImGui::InputDouble("Fade out (seconds)",&settings.exportFadeOut,1.0,2.0,"%.1f")) {
|
||||
if (exportFadeOut<0.0) exportFadeOut=0.0;
|
||||
exportFadeOut=settings.exportFadeOut;
|
||||
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
|
||||
audioExportOptions.fadeOut=settings.exportFadeOut;
|
||||
settingsChanged=true;
|
||||
}
|
||||
ImGui::Unindent();
|
||||
|
@ -2106,6 +2144,10 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_SELECTION_END);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_SELECTION_UP_COARSE);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_SELECTION_DOWN_COARSE);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_MOVE_UP);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_MOVE_DOWN);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_MOVE_LEFT_CHANNEL);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_MOVE_RIGHT_CHANNEL);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_DELETE);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_PULL_DELETE);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_INSERT);
|
||||
|
@ -4119,6 +4161,13 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
settings.renderBackend=conf.getString("renderBackend",GUI_BACKEND_DEFAULT_NAME);
|
||||
settings.renderClearPos=conf.getInt("renderClearPos",0);
|
||||
|
||||
settings.glRedSize=conf.getInt("glRedSize",8);
|
||||
settings.glGreenSize=conf.getInt("glGreenSize",8);
|
||||
settings.glBlueSize=conf.getInt("glBlueSize",8);
|
||||
settings.glAlphaSize=conf.getInt("glAlphaSize",0);
|
||||
settings.glDepthSize=conf.getInt("glDepthSize",24);
|
||||
settings.glDoubleBuffer=conf.getInt("glDoubleBuffer",1);
|
||||
|
||||
settings.vsync=conf.getInt("vsync",1);
|
||||
settings.frameRateLimit=conf.getInt("frameRateLimit",100);
|
||||
settings.displayRenderTime=conf.getInt("displayRenderTime",0);
|
||||
|
@ -4653,6 +4702,12 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
clampSetting(settings.vibrationStrength,0.0f,1.0f);
|
||||
clampSetting(settings.vibrationLength,10,500);
|
||||
clampSetting(settings.inputRepeat,0,1);
|
||||
clampSetting(settings.glRedSize,0,32);
|
||||
clampSetting(settings.glGreenSize,0,32);
|
||||
clampSetting(settings.glBlueSize,0,32);
|
||||
clampSetting(settings.glAlphaSize,0,32);
|
||||
clampSetting(settings.glDepthSize,0,128);
|
||||
clampSetting(settings.glDoubleBuffer,0,1);
|
||||
|
||||
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
|
||||
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
|
||||
|
@ -4676,6 +4731,13 @@ void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
conf.set("renderBackend",settings.renderBackend);
|
||||
conf.set("renderClearPos",settings.renderClearPos);
|
||||
|
||||
conf.set("glRedSize",settings.glRedSize);
|
||||
conf.set("glGreenSize",settings.glGreenSize);
|
||||
conf.set("glBlueSize",settings.glBlueSize);
|
||||
conf.set("glAlphaSize",settings.glAlphaSize);
|
||||
conf.set("glDepthSize",settings.glDepthSize);
|
||||
conf.set("glDoubleBuffer",settings.glDoubleBuffer);
|
||||
|
||||
conf.set("vsync",settings.vsync);
|
||||
conf.set("frameRateLimit",settings.frameRateLimit);
|
||||
conf.set("displayRenderTime",settings.displayRenderTime);
|
||||
|
|
20
src/main.cpp
20
src/main.cpp
|
@ -66,10 +66,9 @@ String outName;
|
|||
String vgmOutName;
|
||||
String zsmOutName;
|
||||
String cmdOutName;
|
||||
int loops=1;
|
||||
int benchMode=0;
|
||||
int subsong=-1;
|
||||
DivAudioExportModes outMode=DIV_EXPORT_MODE_ONE;
|
||||
DivAudioExportOptions exportOptions;
|
||||
|
||||
#ifdef HAVE_GUI
|
||||
bool consoleMode=false;
|
||||
|
@ -299,9 +298,9 @@ TAParamResult pLoops(String val) {
|
|||
try {
|
||||
int count=std::stoi(val);
|
||||
if (count<0) {
|
||||
loops=0;
|
||||
exportOptions.loops=0;
|
||||
} else {
|
||||
loops=count+1;
|
||||
exportOptions.loops=count;
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
logE("loop count shall be a number.");
|
||||
|
@ -327,11 +326,11 @@ TAParamResult pSubSong(String val) {
|
|||
|
||||
TAParamResult pOutMode(String val) {
|
||||
if (val=="one") {
|
||||
outMode=DIV_EXPORT_MODE_ONE;
|
||||
exportOptions.mode=DIV_EXPORT_MODE_ONE;
|
||||
} else if (val=="persys") {
|
||||
outMode=DIV_EXPORT_MODE_MANY_SYS;
|
||||
exportOptions.mode=DIV_EXPORT_MODE_MANY_SYS;
|
||||
} else if (val=="perchan") {
|
||||
outMode=DIV_EXPORT_MODE_MANY_CHAN;
|
||||
exportOptions.mode=DIV_EXPORT_MODE_MANY_CHAN;
|
||||
} else {
|
||||
logE("invalid value for outmode! valid values are: one, persys and perchan.");
|
||||
return TA_PARAM_ERROR;
|
||||
|
@ -401,7 +400,7 @@ void initParams() {
|
|||
params.push_back(TAParam("n","nostatus",false,pNoStatus,"","disable playback status in console mode"));
|
||||
params.push_back(TAParam("N","nocontrols",false,pNoControls,"","disable standard input controls in console mode"));
|
||||
|
||||
params.push_back(TAParam("l","loops",true,pLoops,"<count>","set number of loops (-1 means loop forever)"));
|
||||
params.push_back(TAParam("l","loops",true,pLoops,"<count>","set number of loops"));
|
||||
params.push_back(TAParam("s","subsong",true,pSubSong,"<number>","set sub-song"));
|
||||
params.push_back(TAParam("o","outmode",true,pOutMode,"one|persys|perchan","set file output mode"));
|
||||
params.push_back(TAParam("S","safemode",false,pSafeMode,"","enable safe mode (software rendering and no audio)"));
|
||||
|
@ -451,12 +450,13 @@ int main(int argc, char** argv) {
|
|||
|
||||
// Windows console thing - thanks dj.tuBIG/MaliceX
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef TA_SUBSYSTEM_CONSOLE
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
freopen("CONOUT$", "w", stdout);
|
||||
freopen("CONOUT$", "w", stderr);
|
||||
freopen("CONIN$", "r", stdin);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
srand(time(NULL));
|
||||
|
@ -715,7 +715,7 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
if (outName!="") {
|
||||
e.setConsoleMode(true);
|
||||
e.saveAudio(outName.c_str(),loops,outMode);
|
||||
e.saveAudio(outName.c_str(),exportOptions);
|
||||
e.waitAudioFile();
|
||||
}
|
||||
finishLogFile();
|
||||
|
|
Loading…
Reference in a new issue