diff --git a/CMakeLists.txt b/CMakeLists.txt index 85fa2b354..cb5586a97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -757,15 +757,11 @@ endif() if (WITH_RENDER_DX11) if (WIN32) - if (SUPPORT_XP) - message(FATAL_ERROR "SUPPORT_XP is on. cannot enable DirectX 11 backend.") - else() - list(APPEND GUI_SOURCES src/gui/render/renderDX11.cpp) - list(APPEND GUI_SOURCES extern/imgui_patched/backends/imgui_impl_dx11.cpp) - list(APPEND DEPENDENCIES_DEFINES HAVE_RENDER_DX11) - list(APPEND DEPENDENCIES_LIBRARIES d3d11) - message(STATUS "UI render backend: DirectX 11") - endif() + list(APPEND GUI_SOURCES src/gui/render/renderDX11.cpp) + list(APPEND GUI_SOURCES extern/imgui_patched/backends/imgui_impl_dx11.cpp) + list(APPEND DEPENDENCIES_DEFINES HAVE_RENDER_DX11) + list(APPEND DEPENDENCIES_LIBRARIES d3d11) + message(STATUS "UI render backend: DirectX 11") else() message(FATAL_ERROR "DirectX 11 render backend only for Windows!") endif() diff --git a/README.md b/README.md index f4d4af3c3..08ccfc85c 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,10 @@ for other operating systems, you may [build the source](#developer-info). - Ricoh RF5C68 used in Sega CD and FM Towns - OKI MSM6258 and MSM6295 - Konami K007232 + - Konami K053260 - Irem GA20 - Ensoniq ES5506 + - Namco C140 - wavetable chips: - HuC6280 used in PC Engine - Konami Bubble System WSG @@ -73,6 +75,7 @@ for other operating systems, you may [build the source](#developer-info). - QuadTone engine - Pokémon Mini - Commodore PET + - TED used in Commodore Plus/4 - Casio PV-1000 - TIA used in Atari 2600 - POKEY used in Atari 8-bit computers @@ -124,7 +127,7 @@ for other operating systems, you may [build the source](#developer-info). # quick references - **discussion**: see the [Discussions](https://github.com/tildearrow/furnace/discussions) section, the [official Revolt](https://rvlt.gg/GRPS6tmc) or the [official Discord server](https://discord.gg/EfrwT2wq7z). -- **help**: check out the [documentation](doc/README.md). it's about 80% complete. +- **help**: check out the [documentation](doc/README.md). it's about 90% complete. ## packages diff --git a/doc/9-guides/README.md b/doc/9-guides/README.md index 4c805940a..4efaf6634 100644 --- a/doc/9-guides/README.md +++ b/doc/9-guides/README.md @@ -4,8 +4,9 @@ here is a small collection of useful tricks and techniques to really make Furnac - [using samples with limited playback rates](limited-samples.md) - [choosing emulation cores](emulation-cores.md) -- [guide on using OPLL patch macro](opllswitching.md) +- [using OPLL patch macro](opllswitching.md) +- [using AY/SAA hardware envelope](envelope.md) # links -- [FM Synthesis of Real Instruments](http://www.javelinart.com/FM_Synthesis_of_Real_Instruments.pdf): an in-depth tutorial on creating FM patches from scratch. +- [FM Synthesis of Real Instruments](http://www.javelinart.com/FM_Synthesis_of_Real_Instruments.pdf): an in-depth tutorial on creating FM patches from scratch. \ No newline at end of file diff --git a/doc/9-guides/envelope.md b/doc/9-guides/envelope.md new file mode 100644 index 000000000..ec9fc1249 --- /dev/null +++ b/doc/9-guides/envelope.md @@ -0,0 +1,27 @@ +# AY-3-8910/8930/SAA1099 envelope guide + +AY-3-8910 programmable sound generator, aside of normal 4-bit volume control, has an hardware volume envelope - a feature that allows you for defining shape of volume envelope at arbibrary speed, according to 8 preset envelope shapes. One may think, what is any upside of hardware envelope? Well, it's somewhat independent of tone/noise generators, and it goes so high in frequency, it can be used melodically! This guide explains how to abuse AY/SAA envelope. + +## AY-3-8910/AY8930 + +going into instrument editor, first set the waveform macro value to `envelope`. This will disable any output, but don't worry. Then, go to `Envelope` macro and select `enable`. You will hear a very high-pitched squeak. This is because you must set envelope period - the frequency at which hardware envelope runs. You can do it in two ways: +- either via 23xx and 24xx effects (envelope coarse and fine period) or... +- 29xx auto-envelope period effect and macros + +Auto-envelope works via numerator and denominator. In general, the higher the numerator, the higher the envelope pitch. The higher the denominator, the lower the envelope pitch. Why there are both of these? Because, envelope generator might be used to mask the tone output (i.e. affect the square wave as well). To do it, set the waveform macro values to both square and envelope. Then, the higher the denominator value, then the lower the envelope pitch relative to the square wave output, analogously the numerator. With square + envelope setting, a lot of wild, detuned, synth instruments can do made. + +Back to the hardware envelope itself. Depending of the `Envelope` macro value, different envelope shapes can be obtained. The most basic one, at 8 is a sawtooth wave. The `direction` value will invert the envelope, producing the reverse sawtooth. The `alternate` value produces an interesting pseudo-triangular wave, similiar to halved sine. That one can also be reversed. `Hold` option disables the envelope. + +WARNING: the envelope pitch resolution is fairly low, at high pitched it will be detuned. Hence, it was used mostly for bass. +WARNING: there is only one hardware envelope generator. So, you cant use two pitches/two waveforms at once. + +## SAA1099 + +SAA envelope works a bit differently, It doesn't have its own pitch, it reles on a channel 2/5 pitch. It also has much more parameters than AY envelope. To use it: go to waveform macro, and set it to 0 (unless you want to have sqaure wave mask). Then, set up an envelope macro: tuen on enabled, loop and, depending on a desired shape, cut and direction. Resolution will give you higher pitch range than on AY. +Then lay two notes in pattern editor: the one in channel 2 will control the envelope pitch, the one in channel 3 can be any note you wish, its just to enable the envelope output. + +## examples + +- [Demoscene-type Beat by Duccinator](https://www.youtube.com/watch?v=qcBgmpPrlUA) +- [Philips SAA1099 Test by Duccinator](https://www.youtube.com/watch?v=IBh2gr09zjs) +- [Touhou Kaikidan: Mystic Square title theme by ZUN](https://www.youtube.com/watch?v=tUKei7Pz0Fw) /rare instance of AY envelope used for drums, it can be used to mask the noise generator output too \ No newline at end of file diff --git a/extern/fmt b/extern/fmt index afbcf1e8e..e57ca2e36 160000 --- a/extern/fmt +++ b/extern/fmt @@ -1 +1 @@ -Subproject commit afbcf1e8eafc5d7f27e29c7397f22521eaa33fac +Subproject commit e57ca2e3685b160617d3d95fcd9e789c4e06ca88 diff --git a/scripts/release-win32.sh b/scripts/release-win32.sh index fc056f5ec..21595dc4b 100755 --- a/scripts/release-win32.sh +++ b/scripts/release-win32.sh @@ -15,7 +15,7 @@ fi cd win32build # TODO: potential Arch-ism? -i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Werror" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON -DWITH_RENDER_DX11=OFF .. || exit 1 +i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Werror" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON -DWITH_RENDER_DX11=ON .. || exit 1 make -j8 || exit 1 cd .. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index fea380863..de13e7750 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -822,9 +822,6 @@ void DivEngine::runExportThread() { size_t curFadeOutSample=0; bool isFadingOut=false; - quitDispatch(); - initDispatch(true); - switch (exportMode) { case DIV_EXPORT_MODE_ONE: { SNDFILE* sf; @@ -1149,8 +1146,6 @@ void DivEngine::runExportThread() { } } - quitDispatch(); - initDispatch(false); stopExport=false; } #else @@ -1158,6 +1153,11 @@ 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) { #ifndef HAVE_SNDFILE logE("Furnace was not compiled with libsndfile. cannot export!"); @@ -1187,6 +1187,20 @@ bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode, } else { remainingLoops=-1; } + + if (shallSwitchCores()) { + bool isMutedBefore[DIV_MAX_CHANS]; + memcpy(isMutedBefore,isMuted,DIV_MAX_CHANS*sizeof(bool)); + quitDispatch(); + initDispatch(true); + renderSamplesP(); + for (int i=0; i=(getSampleMemCapacity())) { if (s->depth==DIV_SAMPLE_DEPTH_MULAW) { for (unsigned int i=0; i<(getSampleMemCapacity())-memPos; i++) { + if (i>=s->lengthMuLaw) break; unsigned char x=s->dataMuLaw[i]^0xff; if (x&0x80) x^=15; unsigned char c140Mu=(x&0x80)|((x&15)<<3)|((x&0x70)>>4); @@ -461,6 +462,7 @@ void DivPlatformC140::renderSamples(int sysID) { } else { if (s->depth==DIV_SAMPLE_DEPTH_MULAW) { for (unsigned int i=0; i=s->lengthMuLaw) break; unsigned char x=s->dataMuLaw[i]^0xff; if (x&0x80) x^=15; unsigned char c140Mu=(x&0x80)|((x&15)<<3)|((x&0x70)>>4); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index b743a02ba..e6a9cd148 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -559,6 +559,17 @@ void DivPlatformGenesisExt::tick(bool sysTick) { rWrite(0x22,lfoValue); } + if (opChan[i].std.panL.had) { + opChan[i].pan=opChan[i].std.panL.val&3; + if (parent->song.sharedExtStat) { + for (int j=0; j<4; j++) { + if (i==j) continue; + opChan[j].pan=opChan[i].pan; + } + } + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + // param macros unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]]; DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]]; diff --git a/src/engine/platform/pcmdac.cpp b/src/engine/platform/pcmdac.cpp index 3a00e431a..940efb3e7 100644 --- a/src/engine/platform/pcmdac.cpp +++ b/src/engine/platform/pcmdac.cpp @@ -229,7 +229,7 @@ void DivPlatformPCMDAC::acquire(short** buf, size_t len) { } else { output=output*chan[0].vol*chan[0].envVol/16384; } - oscBuf->data[oscBuf->needle++]=output>>1; + oscBuf->data[oscBuf->needle++]=((output>>depthScale)<>1; if (outStereo) { buf[0][h]=((output*chan[0].panL)>>(depthScale+8))<>(depthScale+8))<mulaw[(s1 >> 8) & 0xff]; s2 = c140->mulaw[(s2 >> 8) & 0xff]; } - // interpolate - signed int sample = s1 + (((voice->frac) * (s2 - s1)) >> 16); + // interpolate (originally was >>16, but I had to reduce it to 15 to prevent overflow) + signed int sample = s1 + (((voice->frac >> 1) * (s2 - s1)) >> 15); voice->lout = sample * voice->lvol; voice->rout = sample * voice->rvol; } diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index a2e0f25a5..3bc14a39f 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -508,6 +508,17 @@ void DivPlatformYM2608Ext::tick(bool sysTick) { rWrite(0x22,lfoValue); } + if (opChan[i].std.panL.had) { + opChan[i].pan=opChan[i].std.panL.val&3; + if (parent->song.sharedExtStat) { + for (int j=0; j<4; j++) { + if (i==j) continue; + opChan[j].pan=opChan[i].pan; + } + } + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + // param macros unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]]; diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 7f38830b3..48bb24fd1 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -504,6 +504,17 @@ void DivPlatformYM2610BExt::tick(bool sysTick) { rWrite(0x22,lfoValue); } + if (opChan[i].std.panL.had) { + opChan[i].pan=opChan[i].std.panL.val&3; + if (parent->song.sharedExtStat) { + for (int j=0; j<4; j++) { + if (i==j) continue; + opChan[j].pan=opChan[i].pan; + } + } + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + // param macros unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]]; DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]]; diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 9f84aa72c..3c18a6e4c 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -504,6 +504,17 @@ void DivPlatformYM2610Ext::tick(bool sysTick) { rWrite(0x22,lfoValue); } + if (opChan[i].std.panL.had) { + opChan[i].pan=opChan[i].std.panL.val&3; + if (parent->song.sharedExtStat) { + for (int j=0; j<4; j++) { + if (i==j) continue; + opChan[j].pan=opChan[i].pan; + } + } + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + // param macros unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]]; DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]]; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 12cf2d489..9fc6ab9d1 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -449,7 +449,12 @@ void DivEngine::registerSystems() { {0x30, {DIV_CMD_FM_HARD_RESET, "30xx: Toggle hard envelope reset on new notes"}}, }; - EffectHandlerMap fmOPN2EffectHandlerMap(fmEffectHandlerMap); + EffectHandlerMap fmExtChEffectHandlerMap(fmEffectHandlerMap); + fmExtChEffectHandlerMap.insert({ + {0x18, {DIV_CMD_FM_EXTCH, "18xx: Toggle extended channel 3 mode"}}, + }); + + EffectHandlerMap fmOPN2EffectHandlerMap(fmExtChEffectHandlerMap); fmOPN2EffectHandlerMap.insert({ {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}}, {0xdf, {DIV_CMD_SAMPLE_DIR, "DFxx: Set sample playback direction (0: normal; 1: reverse)"}}, @@ -522,7 +527,6 @@ void DivEngine::registerSystems() { fmOPNPostEffectHandlerMap.insert({ {0x10, {DIV_CMD_FM_LFO, "10xy: Setup LFO (x: enable; y: speed)"}}, - {0x18, {DIV_CMD_FM_EXTCH, "18xx: Toggle extended channel 3 mode"}}, {0x55, {DIV_CMD_FM_SSG, "55xy: Set SSG envelope (x: operator from 1 to 4 (0 for all ops); y: 0-7 on, 8 off)", effectOpVal<4>, effectValAnd<15>}}, }); EffectHandlerMap fmOPN2PostEffectHandlerMap(fmOPNPostEffectHandlerMap); @@ -769,7 +773,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); @@ -1028,7 +1032,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY}, {}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNPostEffectHandlerMap ); @@ -1040,7 +1044,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY}, {}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNPostEffectHandlerMap ); @@ -1064,7 +1068,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); @@ -1076,7 +1080,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); @@ -1368,7 +1372,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); @@ -1380,7 +1384,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); @@ -1457,7 +1461,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); @@ -1469,7 +1473,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); diff --git a/src/gui/fileDialog.cpp b/src/gui/fileDialog.cpp index b9ec5f69f..17d30a40f 100644 --- a/src/gui/fileDialog.cpp +++ b/src/gui/fileDialog.cpp @@ -69,7 +69,7 @@ void _nfdThread(const NFDState state, std::atomic* ok, std::vector (*errorOutput)=true; break; default: - logE("NFD unknown return code %d!\n",ret); + logE("NFD unknown return code %d!\n",(int)ret); break; } (*ok)=true; diff --git a/src/gui/fmPreview.cpp b/src/gui/fmPreview.cpp index eaf449ed9..9e9ba6a10 100644 --- a/src/gui/fmPreview.cpp +++ b/src/gui/fmPreview.cpp @@ -20,8 +20,12 @@ #define _USE_MATH_DEFINES #include "gui.h" #include "../../extern/opn/ym3438.h" +#include "../../extern/opm/opm.h" +#include "../../extern/opl/opl3.h" +#include "../../extern/Nuked-OPLL/opll.h" +#include "../engine/platform/sound/ymfm/ymfm_opz.h" -#define FM_WRITE(addr,val) \ +#define OPN_WRITE(addr,val) \ OPN2_Write((ym3438_t*)fmPreviewOPN,0,(addr)); \ do { \ OPN2_Clock((ym3438_t*)fmPreviewOPN,out); \ @@ -35,7 +39,7 @@ const unsigned char dtTableFMP[8]={ 7,6,5,0,1,2,3,4 }; -void FurnaceGUI::renderFMPreview(const DivInstrumentFM& params, int pos) { +void FurnaceGUI::renderFMPreviewOPN(const DivInstrumentFM& params, int pos) { if (fmPreviewOPN==NULL) { fmPreviewOPN=new ym3438_t; } @@ -57,19 +61,19 @@ void FurnaceGUI::renderFMPreview(const DivInstrumentFM& params, int pos) { for (int i=0; i<4; i++) { const DivInstrumentFM::Operator& op=params.op[i]; unsigned short baseAddr=i*4; - FM_WRITE(baseAddr+0x40,op.tl); - FM_WRITE(baseAddr+0x30,(op.mult&15)|(dtTableFMP[op.dt&7]<<4)); - FM_WRITE(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); - FM_WRITE(baseAddr+0x60,(op.dr&31)|(op.am<<7)); - FM_WRITE(baseAddr+0x70,op.d2r&31); - FM_WRITE(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); - FM_WRITE(baseAddr+0x90,op.ssgEnv&15); + OPN_WRITE(baseAddr+0x40,op.tl); + OPN_WRITE(baseAddr+0x30,(op.mult&15)|(dtTableFMP[op.dt&7]<<4)); + OPN_WRITE(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + OPN_WRITE(baseAddr+0x60,(op.dr&31)|(op.am<<7)); + OPN_WRITE(baseAddr+0x70,op.d2r&31); + OPN_WRITE(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); + OPN_WRITE(baseAddr+0x90,op.ssgEnv&15); } - FM_WRITE(0xb0,(params.alg&7)|((params.fb&7)<<3)); - FM_WRITE(0xb4,0xc0|(params.fms&7)|((params.ams&3)<<4)); - FM_WRITE(0xa4,mult0?0x1c:0x14); // frequency - FM_WRITE(0xa0,0); - FM_WRITE(0x28,0xf0); // key on + OPN_WRITE(0xb0,(params.alg&7)|((params.fb&7)<<3)); + OPN_WRITE(0xb4,0xc0|(params.fms&7)|((params.ams&3)<<4)); + OPN_WRITE(0xa4,mult0?0x1c:0x14); // frequency + OPN_WRITE(0xa0,0); + OPN_WRITE(0x28,0xf0); // key on } // render @@ -84,3 +88,37 @@ void FurnaceGUI::renderFMPreview(const DivInstrumentFM& params, int pos) { fmPreview[i]=aOut; } } + +void FurnaceGUI::renderFMPreviewOPM(const DivInstrumentFM& params, int pos) { +} + +void FurnaceGUI::renderFMPreviewOPLL(const DivInstrumentFM& params, int pos) { +} + +void FurnaceGUI::renderFMPreviewOPL(const DivInstrumentFM& params, int pos) { +} + +void FurnaceGUI::renderFMPreviewOPZ(const DivInstrumentFM& params, int pos) { +} + +void FurnaceGUI::renderFMPreview(const DivInstrument* ins, int pos) { + switch (ins->type) { + case DIV_INS_FM: + renderFMPreviewOPN(ins->fm,pos); + break; + case DIV_INS_OPM: + renderFMPreviewOPM(ins->fm,pos); + break; + case DIV_INS_OPLL: + renderFMPreviewOPLL(ins->fm,pos); + break; + case DIV_INS_OPL: + renderFMPreviewOPL(ins->fm,pos); + break; + case DIV_INS_OPZ: + renderFMPreviewOPZ(ins->fm,pos); + break; + default: + break; + } +} diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e1cf93acc..35c612714 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5262,6 +5262,7 @@ bool FurnaceGUI::loop() { } } if (!e->isExporting()) { + e->finishAudioFile(); ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); @@ -6872,6 +6873,10 @@ FurnaceGUI::FurnaceGUI(): fmPreviewOn(false), fmPreviewPaused(false), fmPreviewOPN(NULL), + fmPreviewOPM(NULL), + fmPreviewOPL(NULL), + fmPreviewOPLL(NULL), + fmPreviewOPZ(NULL), editString(NULL), pendingRawSampleDepth(8), pendingRawSampleChannels(1), diff --git a/src/gui/gui.h b/src/gui/gui.h index 42750bb12..3edbda1b3 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1349,6 +1349,10 @@ class FurnaceGUI { short fmPreview[FM_PREVIEW_SIZE]; bool updateFMPreview, fmPreviewOn, fmPreviewPaused; void* fmPreviewOPN; + void* fmPreviewOPM; + void* fmPreviewOPL; + void* fmPreviewOPLL; + void* fmPreviewOPZ; String* editString; String pendingRawSample; @@ -2137,7 +2141,12 @@ class FurnaceGUI { bool drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange, bool fromMenu=false); void kvsConfig(DivInstrument* ins); void drawFMPreview(const ImVec2& size); - void renderFMPreview(const DivInstrumentFM& params, int pos=0); + void renderFMPreview(const DivInstrument* ins, int pos=0); + void renderFMPreviewOPN(const DivInstrumentFM& params, int pos=0); + void renderFMPreviewOPM(const DivInstrumentFM& params, int pos=0); + void renderFMPreviewOPLL(const DivInstrumentFM& params, int pos=0); + void renderFMPreviewOPL(const DivInstrumentFM& params, int pos=0); + void renderFMPreviewOPZ(const DivInstrumentFM& params, int pos=0); // these ones offer ctrl-wheel fine value changes. bool CWSliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format=NULL, ImGuiSliderFlags flags=0); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index e6056d036..1878e8da3 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1284,7 +1284,7 @@ inline bool enBit30(const int val) { void FurnaceGUI::kvsConfig(DivInstrument* ins) { - if (ins->type==DIV_INS_FM && fmPreviewOn) { + if (fmPreviewOn) { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("left click to restart\nmiddle click to pause\nright click to see algorithm"); } @@ -1299,10 +1299,10 @@ void FurnaceGUI::kvsConfig(DivInstrument* ins) { ImGui::SetTooltip("left click to configure TL scaling\nright click to see FM preview"); } } - if (ImGui::IsItemClicked(ImGuiMouseButton_Right) && ins->type==DIV_INS_FM) { + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { fmPreviewOn=!fmPreviewOn; } - if (!fmPreviewOn || ins->type!=DIV_INS_FM) { + if (!fmPreviewOn) { int opCount=4; if (ins->type==DIV_INS_OPLL) opCount=2; if (ins->type==DIV_INS_OPL) opCount=(ins->fm.ops==4)?4:2; @@ -1568,6 +1568,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Bottom"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1590,6 +1591,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Attack"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1609,6 +1611,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Hold"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1628,6 +1631,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Decay"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1650,6 +1654,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextColumn(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Release"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1671,6 +1676,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Bottom"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1693,6 +1699,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Speed"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1711,6 +1718,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail } rightClickable ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Shape"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -2281,7 +2289,7 @@ void FurnaceGUI::drawInsEdit() { } else { DivInstrument* ins=e->song.ins[curIns]; if (updateFMPreview) { - renderFMPreview(ins->fm); + renderFMPreview(ins); updateFMPreview=false; } if (settings.insEditColorize) { @@ -2469,10 +2477,10 @@ void FurnaceGUI::drawInsEdit() { P(CWSliderScalar(FM_NAME(FM_ALG),ImGuiDataType_U8,&ins->fm.alg,&_ZERO,&_SEVEN)); rightClickable P(CWSliderScalar(FM_NAME(FM_AMS),ImGuiDataType_U8,&ins->fm.ams,&_ZERO,&_THREE)); rightClickable ImGui::TableNextColumn(); - if (ins->type==DIV_INS_FM && fmPreviewOn) { + if (fmPreviewOn) { drawFMPreview(ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); if (!fmPreviewPaused) { - renderFMPreview(ins->fm,1); + renderFMPreview(ins,1); WAKE_UP; } } else { @@ -2490,7 +2498,15 @@ void FurnaceGUI::drawInsEdit() { P(CWSliderScalar(FM_NAME(FM_AMS),ImGuiDataType_U8,&ins->fm.ams,&_ZERO,&_THREE)); rightClickable P(CWSliderScalar(FM_NAME(FM_AMS2),ImGuiDataType_U8,&ins->fm.ams2,&_ZERO,&_THREE)); rightClickable ImGui::TableNextColumn(); - drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + if (fmPreviewOn) { + drawFMPreview(ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + if (!fmPreviewPaused) { + renderFMPreview(ins,1); + WAKE_UP; + } + } else { + drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + } kvsConfig(ins); if (ImGui::Button("Request from TX81Z")) { @@ -2524,7 +2540,15 @@ void FurnaceGUI::drawInsEdit() { } } ImGui::TableNextColumn(); - drawAlgorithm(ins->fm.alg&algMax,fourOp?FM_ALGS_4OP_OPL:FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + if (fmPreviewOn) { + drawFMPreview(ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + if (!fmPreviewPaused) { + renderFMPreview(ins,1); + WAKE_UP; + } + } else { + drawAlgorithm(ins->fm.alg&algMax,fourOp?FM_ALGS_4OP_OPL:FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + } kvsConfig(ins); break; } @@ -2549,7 +2573,15 @@ void FurnaceGUI::drawInsEdit() { } ImGui::EndDisabled(); ImGui::TableNextColumn(); - drawAlgorithm(0,FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,24.0*dpiScale)); + if (fmPreviewOn) { + drawFMPreview(ImVec2(ImGui::GetContentRegionAvail().x,24.0*dpiScale)); + if (!fmPreviewPaused) { + renderFMPreview(ins,1); + WAKE_UP; + } + } else { + drawAlgorithm(0,FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,24.0*dpiScale)); + } ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 25a4aa3b0..02d0aed58 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -293,6 +293,9 @@ void FurnaceGUI::drawSampleEdit() { if (sample->samples>131070) { SAMPLE_WARN(warnLength,"Amiga: maximum sample length is 131070"); } + if (dispatch!=NULL) { + MAX_RATE("Amiga",31250.0); + } break; case DIV_SYSTEM_SEGAPCM: case DIV_SYSTEM_SEGAPCM_COMPAT: diff --git a/src/gui/waveEdit.cpp b/src/gui/waveEdit.cpp index 3905b7614..de7cee606 100644 --- a/src/gui/waveEdit.cpp +++ b/src/gui/waveEdit.cpp @@ -1093,7 +1093,7 @@ void FurnaceGUI::drawWaveEdit() { MARK_MODIFIED; }); } - if (ImGui::Button("Invert",buttonSize)) { + if (ImGui::Button("Invert",buttonSizeHalf)) { e->lockEngine([this,wave]() { for (int i=0; ilen; i++) { wave->data[i]=wave->max-wave->data[i]; @@ -1101,6 +1101,18 @@ void FurnaceGUI::drawWaveEdit() { MARK_MODIFIED; }); } + ImGui::SameLine(); + if (ImGui::Button("Reverse",buttonSizeHalf)) { + e->lockEngine([this,wave]() { + int origData[256]; + memcpy(origData,wave->data,wave->len*sizeof(int)); + + for (int i=0; ilen; i++) { + wave->data[i]=origData[wave->len-1-i]; + } + MARK_MODIFIED; + }); + } if (ImGui::Button("Half",buttonSizeHalf)) { int origData[256]; diff --git a/src/log.cpp b/src/log.cpp index 9cfabe901..2fbf24dd3 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -97,7 +97,11 @@ int writeLog(int level, const char* msg, fmt::printf_args args) { time_t thisMakesNoSense=time(NULL); int pos=(logPosition.fetch_add(1))&TA_LOG_MASK; +#if FMT_VERSION >= 100100 + logEntries[pos].text.assign(fmt::vsprintf(fmt::basic_string_view(msg),args)); +#else logEntries[pos].text.assign(fmt::vsprintf(msg,args)); +#endif // why do I have to pass a pointer // can't I just pass the time_t directly?! #ifdef _WIN32