From 07ab7db2f86efc6ba42b5e09883b8b0b7e160ead Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 20 Aug 2024 15:04:41 -0500 Subject: [PATCH 01/13] don't use built-in MBS to WCS --- extern/igfd/dirent/dirent.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/extern/igfd/dirent/dirent.h b/extern/igfd/dirent/dirent.h index dccfe5e8c..e152dd510 100644 --- a/extern/igfd/dirent/dirent.h +++ b/extern/igfd/dirent/dirent.h @@ -1142,13 +1142,6 @@ dirent_mbstowcs_s( { int error; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - - /* Microsoft Visual Studio 2005 or later */ - error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); - -#else - /* Older Visual Studio or non-Microsoft compiler */ size_t n; @@ -1179,7 +1172,6 @@ dirent_mbstowcs_s( } -#endif return error; } @@ -1230,13 +1222,6 @@ dirent_wcstombs_s( { int error; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - - /* Microsoft Visual Studio 2005 or later */ - error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); - -#else - /* Older Visual Studio or non-Microsoft compiler */ size_t n; @@ -1268,7 +1253,6 @@ dirent_wcstombs_s( } -#endif return error; } From a77d8bc87e6a290e706bcf2d95063847073024ca Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 21 Aug 2024 14:46:40 -0500 Subject: [PATCH 02/13] oh crap --- src/gui/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 6a8514a1b..e2c5bc387 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4748,7 +4748,7 @@ bool FurnaceGUI::loop() { info=fmt::sprintf(_("Set volume: %d (%.2X, INVALID!)"),p->data[cursor.y][3],p->data[cursor.y][3]); } else { float realVol=e->getGain(cursor.xCoarse,p->data[cursor.y][3]); - info=fmt::sprintf(_("Set volume: %d (%.2X, %d%%)"),p->data[cursor.y][3],p->data[cursor.y][3],(int)(realVol*100.0f/(float)maxVol)); + info=fmt::sprintf(_("Set volume: %d (%.2X, %d%%)"),p->data[cursor.y][3],p->data[cursor.y][3],(int)(realVol*100.0f)); } hasInfo=true; } From d79bad195a79bc9b167adaefbd977f5e52f035f3 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Tue, 20 Aug 2024 22:02:07 +0300 Subject: [PATCH 03/13] looks like it works, no account for fadeout tho --- .github/workflows/build.yml | 2 +- src/engine/engine.cpp | 6 ++ src/engine/engine.h | 21 +++++- src/engine/song.cpp | 128 ++++++++++++++++++++++++++++++++++++ src/engine/song.h | 5 ++ src/engine/wavOps.cpp | 83 ++++++++++++++++++++++- src/gui/gui.cpp | 51 +++++++++++++- src/gui/gui.h | 4 ++ 8 files changed, 295 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8edc74ede..b86fcf96c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,7 @@ name: Build furnace on: push: - branches: [master, locale] + branches: [master, locale, audio-export-progress-bar] pull_request: branches: master diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index e9b3255c3..b8defa1f7 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -199,6 +199,12 @@ void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) { } } +void DivEngine::findSongLength(bool& hasFFxx, std::vector& orders, int& length, int loopOrder, int loopRow, int loopEnd) { + if (curSubSong!=NULL) { + curSubSong->findLength(hasFFxx, orders, length, loopOrder, loopRow, loopEnd, chans, song.jumpTreatment, song.ignoreJumpAtEnd); + } +} + #define EXPORT_BUFSIZE 2048 double DivEngine::benchmarkPlayback() { diff --git a/src/engine/engine.h b/src/engine/engine.h index a845cb0e1..5541b4fe6 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -474,7 +474,7 @@ class DivEngine { int midiOutTimeRate; float midiVolExp; int softLockCount; - int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed; + int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, curExportChan /*for per-channel export progress*/, nextSpeed, elapsedBars, elapsedBeats, curSpeed; size_t curSubSongIndex; size_t bufferPos; double divider; @@ -816,6 +816,9 @@ class DivEngine { // find song loop position void walkSong(int& loopOrder, int& loopRow, int& loopEnd); + // find song length in rows (up to specified loop point), and find length of every order + void findSongLength(bool& hasFFxx, std::vector& orders, int& length, int loopOrder, int loopRow, int loopEnd); + // play (returns whether successful) bool play(); @@ -1007,6 +1010,21 @@ class DivEngine { // is exporting bool isExporting(); + // get how many loops is left + void getLoopsLeft(int& loops); + + //get how many loops in total export needs to do + void getTotalLoops(int& loops); + + // get current position in song + void getCurSongPos(int& row, int& order); + + //get how many files export needs to create + void getTotalAudioFiles(int& files); + + //get which file is processed right now (progress for e.g. per-channel export) + void getCurFileIndex(int& file); + // add instrument int addInstrument(int refChan=0, DivInstrumentType fallbackType=DIV_INS_STD); @@ -1400,6 +1418,7 @@ class DivEngine { totalLoops(0), lastLoopPos(0), exportLoopCount(0), + curExportChan(0), nextSpeed(3), elapsedBars(0), elapsedBeats(0), diff --git a/src/engine/song.cpp b/src/engine/song.cpp index 7d1215225..e0b14a443 100644 --- a/src/engine/song.cpp +++ b/src/engine/song.cpp @@ -102,6 +102,134 @@ bool DivSubSong::walk(int& loopOrder, int& loopRow, int& loopEnd, int chans, int return false; } +void DivSubSong::findLength(bool& hasFFxx, std::vector& orders_vec, int& length, int loopOrder, int loopRow, int loopEnd, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat) +{ + length = 0; + hasFFxx = false; + + loopOrder=0; + loopRow=0; + loopEnd=-1; + int nextOrder=-1; + int nextRow=0; + int effectVal=0; + int lastSuspectedLoopEnd=-1; + DivPattern* subPat[DIV_MAX_CHANS]; + unsigned char wsWalked[8192]; + memset(wsWalked,0,8192); + if (firstPat>0) { + memset(wsWalked,255,32*firstPat); + } + for (int i=firstPat; ilastSuspectedLoopEnd) + { + lastSuspectedLoopEnd=i; + } + for (int j=nextRow; j>3))&8191]&(1<<(j&7))) + { + loopOrder=i; + loopRow=j; + loopEnd=lastSuspectedLoopEnd; + return; + } + for (int k=0; kdata[j][5+(l<<1)]; + if (effectVal<0) effectVal=0; + + if (subPat[k]->data[j][4+(l<<1)]==0xff) + { + hasFFxx = true; + + //FFxx makes YOU SHALL NOT PASS!!! move + orders_vec.push_back(j + 1); //order len + length += j + 1; //add length of order to song length + + return; + } + + if (subPat[k]->data[j][4+(l<<1)]==0x0d) + { + if (jumpTreatment==2) + { + if ((idata[j][4+(l<<1)]==0x0b) + { + if (nextOrder==-1 || jumpTreatment==0) + { + nextOrder=effectVal; + if (jumpTreatment==1 || jumpTreatment==2 || !jumpingOrder) + { + nextRow=0; + } + changingOrder=true; + } + } + } + } + + wsWalked[((i<<5)+(j>>3))&8191]|=1<<(j&7); + + if (nextOrder!=-1) + { + i=nextOrder-1; + orders_vec.push_back(j + 1); //order len + length += j + 1; //add length of order to song length + jumped = true; + nextOrder=-1; + break; + } + } + if(!jumped) //if no jump occured we add full pattern length + { + orders_vec.push_back(patLen); //order len + length += patLen; //add length of order to song length + } + } +} + void DivSubSong::clearData() { for (int i=0; i& orders, int& length, int loopOrder, int loopRow, int loopEnd, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat=0); + void clearData(); void optimizePatterns(); void rearrangePatterns(); diff --git a/src/engine/wavOps.cpp b/src/engine/wavOps.cpp index 001e3a3e7..c8e5a5b03 100644 --- a/src/engine/wavOps.cpp +++ b/src/engine/wavOps.cpp @@ -33,6 +33,77 @@ bool DivEngine::isExporting() { return exporting; } +void DivEngine::getLoopsLeft(int& loops) { + if(totalLoops < 0 || exportLoopCount == 0) + { + loops = 0; + return; + } + loops = exportLoopCount - 1 - totalLoops; +} + +void DivEngine::getTotalLoops(int& loops) { + loops = exportLoopCount - 1; +} + +void DivEngine::getCurSongPos(int& row, int& order) { + row = curRow; + order = curOrder; +} + +void DivEngine::getTotalAudioFiles(int& files) +{ + files = 0; + + switch(exportMode) + { + case DIV_EXPORT_MODE_ONE: + { + files = 1; + break; + } + case DIV_EXPORT_MODE_MANY_SYS: + { + files = 1; //there actually are several files but they are processed in the same loop, so to correctly draw progress we think of them as one file + break; + } + case DIV_EXPORT_MODE_MANY_CHAN: + { + for(int i = 0; i < chans; i++) + { + if (exportChannelMask[i]) files++; + } + break; + } + default: break; + } +} + +void DivEngine::getCurFileIndex(int& file) +{ + file = 0; + + switch(exportMode) + { + case DIV_EXPORT_MODE_ONE: + { + file = 0; + break; + } + case DIV_EXPORT_MODE_MANY_SYS: + { + file = 0; //there actually are several files but they are processed in the same loop, so to correctly draw progress we think of them as one file + break; + } + case DIV_EXPORT_MODE_MANY_CHAN: + { + file = curExportChan; + break; + } + default: break; + } +} + #ifdef HAVE_SNDFILE void DivEngine::runExportThread() { size_t fadeOutSamples=got.rate*exportFadeOut; @@ -140,7 +211,11 @@ void DivEngine::runExportThread() { sf[i]=NULL; si[i].samplerate=got.rate; si[i].channels=disCont[i].dispatch->getOutputCount(); - si[i].format=SF_FORMAT_WAV|SF_FORMAT_PCM_16; + if (exportFormat==DIV_EXPORT_FORMAT_S16) { + si[i].format=SF_FORMAT_WAV|SF_FORMAT_PCM_16; + } else { + si[i].format=SF_FORMAT_WAV|SF_FORMAT_FLOAT; + } } for (int i=0; iwalkSong(loopOrder,loopRow,loopEnd); + e->findSongLength(songHasSongEndCommand, songOrdersLengths, songLength, loopOrder, loopRow, loopEnd); //for progress estimation + e->saveAudio(path.c_str(),audioExportOptions); displayExporting=true; } @@ -5820,8 +5826,49 @@ bool FurnaceGUI::loop() { MEASURE_BEGIN(popup); centerNextWindow(_("Rendering..."),canvasW,canvasH); - if (ImGui::BeginPopupModal(_("Rendering..."),NULL,ImGuiWindowFlags_AlwaysAutoResize)) { + if (ImGui::BeginPopupModal(_("Rendering..."),NULL,ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { ImGui::Text(_("Please wait...")); + int songLengthLambda = songLength; + std::vector* songOrdersLengthsLambda = & songOrdersLengths; + float progress = 0.0f; + float* progressLambda = &progress; + if(e->isExporting()) + { + e->lockEngine([this, songOrdersLengthsLambda, progressLambda]() + { + int totalFiles = 0; + e->getTotalAudioFiles(totalFiles); + int loopsLeft = 0; + int totalLoops = 0; + + if(!songHasSongEndCommand) + { + e->getLoopsLeft(loopsLeft); + e->getTotalLoops(totalLoops); + } + int curRow = 0; + int curOrder = 0; + e->getCurSongPos(curRow, curOrder); + int curFile = 0; + e->getCurFileIndex(curFile); + + int curPosInRows = curRow; + + for(int i = 0; i < curOrder; i++) + { + curPosInRows += songOrdersLengths[i]; + } + int lengthOfOneFile = songLength * (totalLoops + 1); + int totalLength = lengthOfOneFile * totalFiles; + + *progressLambda = (float)(curPosInRows + + (totalLoops - loopsLeft) * songLength + + lengthOfOneFile * curFile) + / (float)totalLength; + }); + } + ImGui::Text(_("%f"), progress); + ImGui::ProgressBar(progress,ImVec2(-FLT_MIN,0)); if (ImGui::Button(_("Abort"))) { if (e->haltAudioFile()) { ImGui::CloseCurrentPopup(); diff --git a/src/gui/gui.h b/src/gui/gui.h index 9ff06e013..87f72a815 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1723,6 +1723,10 @@ class FurnaceGUI { char emptyLabel[32]; char emptyLabel2[32]; + std::vector songOrdersLengths; //lengths of all orders (for drawing song export progress) + int songLength; //length of all the song in rows + bool songHasSongEndCommand; //song has "Song end" command (FFxx) + struct Settings { bool settingsChanged; int mainFontSize, patFontSize, headFontSize, iconSize; From beca6bea004967a3492a1f9f861ec5c347e29729 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Tue, 20 Aug 2024 22:08:24 +0300 Subject: [PATCH 04/13] fix? --- src/engine/song.cpp | 2 +- src/engine/song.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/song.cpp b/src/engine/song.cpp index e0b14a443..e4a398c2c 100644 --- a/src/engine/song.cpp +++ b/src/engine/song.cpp @@ -102,7 +102,7 @@ bool DivSubSong::walk(int& loopOrder, int& loopRow, int& loopEnd, int chans, int return false; } -void DivSubSong::findLength(bool& hasFFxx, std::vector& orders_vec, int& length, int loopOrder, int loopRow, int loopEnd, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat) +void DivSubSong::findLength(bool& hasFFxx, std::vector& orders_vec, int& length, int& loopOrder, int& loopRow, int& loopEnd, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat) { length = 0; hasFFxx = false; diff --git a/src/engine/song.h b/src/engine/song.h index 43c17d6fc..e247ca0ca 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -188,7 +188,7 @@ struct DivSubSong { /** * find song length in rows (up to specified loop point). Also find length of every row */ - void findLength(bool& hasFFxx, std::vector& orders, int& length, int loopOrder, int loopRow, int loopEnd, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat=0); + void findLength(bool& hasFFxx, std::vector& orders, int& length, int& loopOrder, int& loopRow, int& loopEnd, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat=0); void clearData(); void optimizePatterns(); From 56749ad2860da408474968c2c0a3fb59527145e0 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Tue, 20 Aug 2024 22:15:21 +0300 Subject: [PATCH 05/13] fix2? --- src/gui/gui.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 12e57544f..13663b742 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5829,12 +5829,11 @@ bool FurnaceGUI::loop() { if (ImGui::BeginPopupModal(_("Rendering..."),NULL,ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { ImGui::Text(_("Please wait...")); int songLengthLambda = songLength; - std::vector* songOrdersLengthsLambda = & songOrdersLengths; float progress = 0.0f; float* progressLambda = &progress; if(e->isExporting()) { - e->lockEngine([this, songOrdersLengthsLambda, progressLambda]() + e->lockEngine([this, progressLambda]() { int totalFiles = 0; e->getTotalAudioFiles(totalFiles); From 481a5710df9842d1f24ca9f26bd463b888ef211e Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Tue, 20 Aug 2024 22:19:44 +0300 Subject: [PATCH 06/13] fix3? --- src/gui/gui.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 13663b742..ead92f80d 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5828,7 +5828,6 @@ bool FurnaceGUI::loop() { centerNextWindow(_("Rendering..."),canvasW,canvasH); if (ImGui::BeginPopupModal(_("Rendering..."),NULL,ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { ImGui::Text(_("Please wait...")); - int songLengthLambda = songLength; float progress = 0.0f; float* progressLambda = &progress; if(e->isExporting()) From 17f6ea5c6a2f9aad15c52c2f163c95ae3ab96232 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Tue, 20 Aug 2024 22:45:21 +0300 Subject: [PATCH 07/13] account for non-beginning loop point location Ig I will optimize some of the calculations later when account for fadeout is added --- src/gui/gui.cpp | 33 +++++++++++++++++++++++++++------ src/gui/gui.h | 1 + 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ead92f80d..4e314959d 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2582,12 +2582,21 @@ int FurnaceGUI::loadStream(String path) { void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) { + songOrdersLengths.clear(); + int loopOrder=0; int loopRow=0; int loopEnd=0; e->walkSong(loopOrder,loopRow,loopEnd); e->findSongLength(songHasSongEndCommand, songOrdersLengths, songLength, loopOrder, loopRow, loopEnd); //for progress estimation + songLoopedSectionLength = songLength; + for(int i = 0; i < loopOrder; i++) + { + songLoopedSectionLength -= songOrdersLengths[i]; + } + songLoopedSectionLength -= loopRow; + e->saveAudio(path.c_str(),audioExportOptions); displayExporting=true; } @@ -5839,24 +5848,36 @@ bool FurnaceGUI::loop() { int loopsLeft = 0; int totalLoops = 0; - if(!songHasSongEndCommand) - { - e->getLoopsLeft(loopsLeft); - e->getTotalLoops(totalLoops); - } int curRow = 0; int curOrder = 0; e->getCurSongPos(curRow, curOrder); int curFile = 0; e->getCurFileIndex(curFile); + int lengthOfOneFile = songLength; + int curPosInRows = curRow; for(int i = 0; i < curOrder; i++) { curPosInRows += songOrdersLengths[i]; } - int lengthOfOneFile = songLength * (totalLoops + 1); + + if(!songHasSongEndCommand) + { + e->getLoopsLeft(loopsLeft); + e->getTotalLoops(totalLoops); + + lengthOfOneFile += songLoopedSectionLength * totalLoops; + + if(totalLoops != loopsLeft) //we are going 2nd, 3rd, etc. time through the song + { + curPosInRows -= (songLength - songLoopedSectionLength); //a hack so progress bar does not jump? + } + } + + //int lengthOfOneFile = songLength * (totalLoops + 1); + //songLoopedSectionLength int totalLength = lengthOfOneFile * totalFiles; *progressLambda = (float)(curPosInRows + diff --git a/src/gui/gui.h b/src/gui/gui.h index 87f72a815..46ec9f592 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1725,6 +1725,7 @@ class FurnaceGUI { std::vector songOrdersLengths; //lengths of all orders (for drawing song export progress) int songLength; //length of all the song in rows + int songLoopedSectionLength; //length of looped part of the song bool songHasSongEndCommand; //song has "Song end" command (FFxx) struct Settings { From a4dae5302ba4fe68331f3a5f00ef3491d1133d15 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:11:11 +0300 Subject: [PATCH 08/13] account for fadeout length, optimize some progress bar calc --- src/engine/engine.cpp | 8 ++-- src/engine/engine.h | 7 +++- src/engine/song.cpp | 91 +++++++++++++++++++++++++++++++++++++++---- src/engine/song.h | 2 +- src/engine/wavOps.cpp | 7 +++- src/gui/gui.cpp | 89 +++++++++++++++++++++++++++++++++++------- src/gui/gui.h | 4 ++ 7 files changed, 180 insertions(+), 28 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index b8defa1f7..fbbf0edc8 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -199,9 +199,11 @@ void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) { } } -void DivEngine::findSongLength(bool& hasFFxx, std::vector& orders, int& length, int loopOrder, int loopRow, int loopEnd) { - if (curSubSong!=NULL) { - curSubSong->findLength(hasFFxx, orders, length, loopOrder, loopRow, loopEnd, chans, song.jumpTreatment, song.ignoreJumpAtEnd); +void DivEngine::findSongLength(int loopOrder, int loopRow, double fadeoutLen, int& rowsForFadeout, bool& hasFFxx, std::vector& orders, int& length) +{ + if (curSubSong!=NULL) + { + curSubSong->findLength(loopOrder, loopRow, fadeoutLen, rowsForFadeout, hasFFxx, orders, song.grooves, length, chans, song.jumpTreatment, song.ignoreJumpAtEnd); } } diff --git a/src/engine/engine.h b/src/engine/engine.h index 5541b4fe6..64d406964 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -498,6 +498,7 @@ class DivEngine { DivAudioExportModes exportMode; DivAudioExportFormats exportFormat; double exportFadeOut; + bool isFadingOut; int exportOutputs; bool exportChannelMask[DIV_MAX_CHANS]; DivConfig conf; @@ -817,7 +818,7 @@ class DivEngine { void walkSong(int& loopOrder, int& loopRow, int& loopEnd); // find song length in rows (up to specified loop point), and find length of every order - void findSongLength(bool& hasFFxx, std::vector& orders, int& length, int loopOrder, int loopRow, int loopEnd); + void findSongLength(int loopOrder, int loopRow, double fadeoutLen, int& rowsForFadeout, bool& hasFFxx, std::vector& orders, int& length); // play (returns whether successful) bool play(); @@ -1025,6 +1026,9 @@ class DivEngine { //get which file is processed right now (progress for e.g. per-channel export) void getCurFileIndex(int& file); + //get fadeout state + bool getIsFadingOut(); + // add instrument int addInstrument(int refChan=0, DivInstrumentType fallbackType=DIV_INS_STD); @@ -1457,6 +1461,7 @@ class DivEngine { exportMode(DIV_EXPORT_MODE_ONE), exportFormat(DIV_EXPORT_FORMAT_S16), exportFadeOut(0.0), + isFadingOut(false), exportOutputs(2), cmdStreamInt(NULL), midiBaseChan(0), diff --git a/src/engine/song.cpp b/src/engine/song.cpp index e4a398c2c..e4c516b6e 100644 --- a/src/engine/song.cpp +++ b/src/engine/song.cpp @@ -102,14 +102,39 @@ bool DivSubSong::walk(int& loopOrder, int& loopRow, int& loopEnd, int chans, int return false; } -void DivSubSong::findLength(bool& hasFFxx, std::vector& orders_vec, int& length, int& loopOrder, int& loopRow, int& loopEnd, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat) +double calcRowLenInSeconds(const DivGroovePattern& speeds, float hz, int vN, int vD, int timeBaseFromSong) +{ + double hl=1; //count for 1 row + if (hl<=0.0) hl=4.0; + double timeBase=timeBaseFromSong+1; + double speedSum=0; + for (int i=0; i& orders_vec, std::vector& grooves, int& length, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat) { length = 0; hasFFxx = false; + rowsForFadeout = 0; + + float secondsPerThisRow = 0.0f; + + DivGroovePattern curSpeeds = speeds; //simulate that we are playing the song, track all speed/BPM/tempo/engine rate changes + short curVirtualTempoN = virtualTempoN; + short curVirtualTempoD = virtualTempoD; + float curHz = hz; + double curDivider = (double)timeBase; + + double curLen = 0.0; //how many seconds passed since the start of song - loopOrder=0; - loopRow=0; - loopEnd=-1; int nextOrder=-1; int nextRow=0; int effectVal=0; @@ -139,9 +164,6 @@ void DivSubSong::findLength(bool& hasFFxx, std::vector& orders_vec, int& le bool jumpingOrder=false; if (wsWalked[((i<<5)+(j>>3))&8191]&(1<<(j&7))) { - loopOrder=i; - loopRow=j; - loopEnd=lastSuspectedLoopEnd; return; } for (int k=0; k& orders_vec, int& le return; } + switch(subPat[k]->data[j][4+(l<<1)]) //track speed/BMP/Hz/tempo changes + { + case 0x09: // select groove pattern/speed 1 + { + if (grooves.empty()) { + if (effectVal>0) curSpeeds.val[0]=effectVal; + } else { + if (effectVal<(short)grooves.size()) { + curSpeeds=grooves[effectVal]; + //curSpeed=0; + } + } + break; + } + case 0x0f: // speed 1/speed 2 + { + if (curSpeeds.len==2 && grooves.empty()) { + if (effectVal>0) curSpeeds.val[1]=effectVal; + } else { + if (effectVal>0) curSpeeds.val[0]=effectVal; + } + break; + } + case 0xfd: // virtual tempo num + { + if (effectVal>0) curVirtualTempoN=effectVal; + break; + } + case 0xfe: // virtual tempo den + { + if (effectVal>0) curVirtualTempoD=effectVal; + break; + } + case 0xf0: // set Hz by tempo (set bpm) + { + curDivider=(double)effectVal*2.0/5.0; + if (curDivider<1) curDivider=1; + //cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/divider; + //clockDrift=0; + //subticks=0; + break; + } + default: break; + } + if (subPat[k]->data[j][4+(l<<1)]==0x0d) { if (jumpTreatment==2) @@ -210,6 +277,16 @@ void DivSubSong::findLength(bool& hasFFxx, std::vector& orders_vec, int& le } } + if(i > loopOrder || (i == loopOrder && j > loopRow)) + { + if(curLen <= fadeoutLen && fadeoutLen > 0.0) //we count each row fadeout lasts. When our time is greater than fadeout length we successfully counted the number of fadeout rows + { + secondsPerThisRow = calcRowLenInSeconds(speeds, curHz, curVirtualTempoN, curVirtualTempoD, curDivider); + curLen += secondsPerThisRow; + rowsForFadeout++; + } + } + wsWalked[((i<<5)+(j>>3))&8191]|=1<<(j&7); if (nextOrder!=-1) diff --git a/src/engine/song.h b/src/engine/song.h index e247ca0ca..75b86ec71 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -188,7 +188,7 @@ struct DivSubSong { /** * find song length in rows (up to specified loop point). Also find length of every row */ - void findLength(bool& hasFFxx, std::vector& orders, int& length, int& loopOrder, int& loopRow, int& loopEnd, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat=0); + void findLength(int loopOrder, int loopRow, double fadeoutLen, int& rowsForFadeout, bool& hasFFxx, std::vector& orders, std::vector& grooves, int& length, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat=0); void clearData(); void optimizePatterns(); diff --git a/src/engine/wavOps.cpp b/src/engine/wavOps.cpp index c8e5a5b03..f1e8de315 100644 --- a/src/engine/wavOps.cpp +++ b/src/engine/wavOps.cpp @@ -104,11 +104,16 @@ void DivEngine::getCurFileIndex(int& file) } } +bool DivEngine::getIsFadingOut() +{ + return isFadingOut; +} + #ifdef HAVE_SNDFILE void DivEngine::runExportThread() { size_t fadeOutSamples=got.rate*exportFadeOut; size_t curFadeOutSample=0; - bool isFadingOut=false; + isFadingOut=false; switch (exportMode) { case DIV_EXPORT_MODE_ONE: { diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 4e314959d..5235faa21 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2588,7 +2588,8 @@ void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) { int loopRow=0; int loopEnd=0; e->walkSong(loopOrder,loopRow,loopEnd); - e->findSongLength(songHasSongEndCommand, songOrdersLengths, songLength, loopOrder, loopRow, loopEnd); //for progress estimation + + e->findSongLength(loopOrder, loopRow, audioExportOptions.fadeOut, songFadeoutSectionLength, songHasSongEndCommand, songOrdersLengths, songLength); //for progress estimation songLoopedSectionLength = songLength; for(int i = 0; i < loopOrder; i++) @@ -2598,9 +2599,74 @@ void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) { songLoopedSectionLength -= loopRow; e->saveAudio(path.c_str(),audioExportOptions); + + int totalFiles = 0; + e->getTotalAudioFiles(totalFiles); + int totalLoops = 0; + + lengthOfOneFile = songLength; + + if(!songHasSongEndCommand) + { + e->getTotalLoops(totalLoops); + + lengthOfOneFile += songLoopedSectionLength * totalLoops; + lengthOfOneFile += songFadeoutSectionLength; //account for fadeout + } + + totalLength = lengthOfOneFile * totalFiles; + + curProgress = 0.0f; + displayExporting=true; } + /*e->lockEngine([this, progressLambda]() + { + int totalFiles = 0; + e->getTotalAudioFiles(totalFiles); + int loopsLeft = 0; + int totalLoops = 0; + + int curRow = 0; + int curOrder = 0; + e->getCurSongPos(curRow, curOrder); + int curFile = 0; + e->getCurFileIndex(curFile); + + int lengthOfOneFile = songLength; + + int curPosInRows = curRow; + + for(int i = 0; i < curOrder; i++) + { + curPosInRows += songOrdersLengths[i]; + } + + if(!songHasSongEndCommand) + { + e->getLoopsLeft(loopsLeft); + e->getTotalLoops(totalLoops); + + lengthOfOneFile += songLoopedSectionLength * totalLoops; + lengthOfOneFile += songFadeoutSectionLength; //account for fadeout + + if(totalLoops != loopsLeft) //we are going 2nd, 3rd, etc. time through the song + { + curPosInRows -= (songLength - songLoopedSectionLength); //a hack so progress bar does not jump? + } + } + + //int lengthOfOneFile = songLength * (totalLoops + 1); + //songLoopedSectionLength + int totalLength = lengthOfOneFile * totalFiles; + + *progressLambda = (float)(curPosInRows + + (totalLoops - loopsLeft) * songLength + + lengthOfOneFile * curFile) + / (float)totalLength; + });*/ + void FurnaceGUI::editStr(String* which) { editString=which; displayEditString=true; @@ -5837,14 +5903,11 @@ bool FurnaceGUI::loop() { centerNextWindow(_("Rendering..."),canvasW,canvasH); if (ImGui::BeginPopupModal(_("Rendering..."),NULL,ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { ImGui::Text(_("Please wait...")); - float progress = 0.0f; - float* progressLambda = &progress; + float* progressLambda = &curProgress; if(e->isExporting()) { e->lockEngine([this, progressLambda]() { - int totalFiles = 0; - e->getTotalAudioFiles(totalFiles); int loopsLeft = 0; int totalLoops = 0; @@ -5854,8 +5917,6 @@ bool FurnaceGUI::loop() { int curFile = 0; e->getCurFileIndex(curFile); - int lengthOfOneFile = songLength; - int curPosInRows = curRow; for(int i = 0; i < curOrder; i++) @@ -5868,26 +5929,24 @@ bool FurnaceGUI::loop() { e->getLoopsLeft(loopsLeft); e->getTotalLoops(totalLoops); - lengthOfOneFile += songLoopedSectionLength * totalLoops; - if(totalLoops != loopsLeft) //we are going 2nd, 3rd, etc. time through the song { curPosInRows -= (songLength - songLoopedSectionLength); //a hack so progress bar does not jump? } + if(e->getIsFadingOut()) //we are in fadeout??? why it works like that bruh + { + curPosInRows -= (songLength - songLoopedSectionLength); //a hack so progress bar does not jump? + } } - //int lengthOfOneFile = songLength * (totalLoops + 1); - //songLoopedSectionLength - int totalLength = lengthOfOneFile * totalFiles; - *progressLambda = (float)(curPosInRows + (totalLoops - loopsLeft) * songLength + lengthOfOneFile * curFile) / (float)totalLength; }); } - ImGui::Text(_("%f"), progress); - ImGui::ProgressBar(progress,ImVec2(-FLT_MIN,0)); + ImGui::Text(_("%f"), curProgress); + ImGui::ProgressBar(curProgress,ImVec2(-FLT_MIN,0)); if (ImGui::Button(_("Abort"))) { if (e->haltAudioFile()) { ImGui::CloseCurrentPopup(); diff --git a/src/gui/gui.h b/src/gui/gui.h index 46ec9f592..f95160de2 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1726,7 +1726,11 @@ class FurnaceGUI { std::vector songOrdersLengths; //lengths of all orders (for drawing song export progress) int songLength; //length of all the song in rows int songLoopedSectionLength; //length of looped part of the song + int songFadeoutSectionLength; //length of fading part of the song bool songHasSongEndCommand; //song has "Song end" command (FFxx) + int lengthOfOneFile; //length of one rendering pass. song length times num of loops + fadeout + int totalLength; //total length of render (lengthOfOneFile times num of files for per-channel export) + float curProgress; struct Settings { bool settingsChanged; From 33eea96986b3ea39960dd64032873543cdbe5362 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Wed, 21 Aug 2024 19:40:42 +0300 Subject: [PATCH 09/13] small fixes, print progress in rows/files --- src/gui/gui.cpp | 103 +++++++++++++++++------------------------------- src/gui/gui.h | 1 + 2 files changed, 38 insertions(+), 66 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 5235faa21..b6b3ade14 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2600,7 +2600,7 @@ void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) { e->saveAudio(path.c_str(),audioExportOptions); - int totalFiles = 0; + totalFiles = 0; e->getTotalAudioFiles(totalFiles); int totalLoops = 0; @@ -2621,52 +2621,6 @@ void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) { displayExporting=true; } - /*e->lockEngine([this, progressLambda]() - { - int totalFiles = 0; - e->getTotalAudioFiles(totalFiles); - int loopsLeft = 0; - int totalLoops = 0; - - int curRow = 0; - int curOrder = 0; - e->getCurSongPos(curRow, curOrder); - int curFile = 0; - e->getCurFileIndex(curFile); - - int lengthOfOneFile = songLength; - - int curPosInRows = curRow; - - for(int i = 0; i < curOrder; i++) - { - curPosInRows += songOrdersLengths[i]; - } - - if(!songHasSongEndCommand) - { - e->getLoopsLeft(loopsLeft); - e->getTotalLoops(totalLoops); - - lengthOfOneFile += songLoopedSectionLength * totalLoops; - lengthOfOneFile += songFadeoutSectionLength; //account for fadeout - - if(totalLoops != loopsLeft) //we are going 2nd, 3rd, etc. time through the song - { - curPosInRows -= (songLength - songLoopedSectionLength); //a hack so progress bar does not jump? - } - } - - //int lengthOfOneFile = songLength * (totalLoops + 1); - //songLoopedSectionLength - int totalLength = lengthOfOneFile * totalFiles; - - *progressLambda = (float)(curPosInRows + - (totalLoops - loopsLeft) * songLength + - lengthOfOneFile * curFile) - / (float)totalLength; - });*/ - void FurnaceGUI::editStr(String* which) { editString=which; displayEditString=true; @@ -5902,51 +5856,68 @@ bool FurnaceGUI::loop() { centerNextWindow(_("Rendering..."),canvasW,canvasH); if (ImGui::BeginPopupModal(_("Rendering..."),NULL,ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { - ImGui::Text(_("Please wait...")); + if(audioExportOptions.mode != DIV_EXPORT_MODE_MANY_CHAN) + { + ImGui::Text(_("Please wait...")); + } float* progressLambda = &curProgress; + int curPosInRows = 0; + int* curPosInRowsLambda = &curPosInRows; + int loopsLeft = 0; + int* loopsLeftLambda = &loopsLeft; + int totalLoops = 0; + int* totalLoopsLambda = &totalLoops; + int curFile = 0; + int* curFileLambda = &curFile; if(e->isExporting()) { - e->lockEngine([this, progressLambda]() + e->lockEngine([this, progressLambda, curPosInRowsLambda, curFileLambda, loopsLeftLambda, totalLoopsLambda]() { - int loopsLeft = 0; - int totalLoops = 0; - int curRow = 0; int curOrder = 0; e->getCurSongPos(curRow, curOrder); - int curFile = 0; - e->getCurFileIndex(curFile); + *curFileLambda = 0; + e->getCurFileIndex(*curFileLambda); - int curPosInRows = curRow; + *curPosInRowsLambda = curRow; for(int i = 0; i < curOrder; i++) { - curPosInRows += songOrdersLengths[i]; + *curPosInRowsLambda += songOrdersLengths[i]; } if(!songHasSongEndCommand) { - e->getLoopsLeft(loopsLeft); - e->getTotalLoops(totalLoops); + e->getLoopsLeft(*loopsLeftLambda); + e->getTotalLoops(*totalLoopsLambda); - if(totalLoops != loopsLeft) //we are going 2nd, 3rd, etc. time through the song + if((*totalLoopsLambda) != (*loopsLeftLambda)) //we are going 2nd, 3rd, etc. time through the song { - curPosInRows -= (songLength - songLoopedSectionLength); //a hack so progress bar does not jump? + *curPosInRowsLambda -= (songLength - songLoopedSectionLength); //a hack so progress bar does not jump? } if(e->getIsFadingOut()) //we are in fadeout??? why it works like that bruh { - curPosInRows -= (songLength - songLoopedSectionLength); //a hack so progress bar does not jump? + *curPosInRowsLambda -= (songLength - songLoopedSectionLength); //a hack so progress bar does not jump? } } - *progressLambda = (float)(curPosInRows + - (totalLoops - loopsLeft) * songLength + - lengthOfOneFile * curFile) + *progressLambda = (float)((*curPosInRowsLambda) + + ((*totalLoopsLambda) - (*loopsLeftLambda)) * songLength + + lengthOfOneFile * (*curFileLambda)) / (float)totalLength; }); } - ImGui::Text(_("%f"), curProgress); - ImGui::ProgressBar(curProgress,ImVec2(-FLT_MIN,0)); + + ImGui::Text(_("Row %d of %d"), curPosInRows + + ((totalLoops) - (loopsLeft)) * songLength, lengthOfOneFile); + + if(audioExportOptions.mode == DIV_EXPORT_MODE_MANY_CHAN) + { + ImGui::Text(_("Channel %d of %d"), curFile + 1, totalFiles); + } + + ImGui::ProgressBar(curProgress,ImVec2(-FLT_MIN,0), fmt::sprintf("%.2f%%", curProgress * 100.0f).c_str()); + if (ImGui::Button(_("Abort"))) { if (e->haltAudioFile()) { ImGui::CloseCurrentPopup(); diff --git a/src/gui/gui.h b/src/gui/gui.h index f95160de2..9c84cacb0 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1731,6 +1731,7 @@ class FurnaceGUI { int lengthOfOneFile; //length of one rendering pass. song length times num of loops + fadeout int totalLength; //total length of render (lengthOfOneFile times num of files for per-channel export) float curProgress; + int totalFiles; struct Settings { bool settingsChanged; From 952b18c0c9775dd301df02e94259987c6dea3590 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Wed, 21 Aug 2024 20:22:18 +0300 Subject: [PATCH 10/13] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b86fcf96c..8edc74ede 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,7 @@ name: Build furnace on: push: - branches: [master, locale, audio-export-progress-bar] + branches: [master, locale] pull_request: branches: master From 3a72acc3b0428175c9a1f7a9ec4ecb559b4763b7 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Wed, 21 Aug 2024 21:59:30 +0300 Subject: [PATCH 11/13] init --- src/gui/gui.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index b6b3ade14..a99201d21 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -8800,7 +8800,15 @@ FurnaceGUI::FurnaceGUI(): romMultiFile(false), romExportSave(false), pendingExport(NULL), - romExportExists(false) { + romExportExists(false), + songLength(0), + songLoopedSectionLength(0), + songFadeoutSectionLength(0), + songHasSongEndCommand(false), + lengthOfOneFile(0), + totalLength(0), + curProgress(0.0f), + totalFiles(0) { // value keys valueKeys[SDLK_0]=0; valueKeys[SDLK_1]=1; @@ -8921,6 +8929,8 @@ FurnaceGUI::FurnaceGUI(): memset(romExportAvail,0,sizeof(bool)*DIV_ROM_MAX); + songOrdersLengths.clear(); + strncpy(noteOffLabel,"OFF",32); strncpy(noteRelLabel,"===",32); strncpy(macroRelLabel,"REL",32); From f433f79dfe5b704b50ab93f3c8ab25ef1b411020 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:02:51 +0300 Subject: [PATCH 12/13] aarr --- src/gui/gui.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index a99201d21..39a24c482 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -8394,6 +8394,14 @@ FurnaceGUI::FurnaceGUI(): totalBackupSize(0), refreshBackups(true), learning(-1), + songLength(0), + songLoopedSectionLength(0), + songFadeoutSectionLength(0), + songHasSongEndCommand(false), + lengthOfOneFile(0), + totalLength(0), + curProgress(0.0f), + totalFiles(0), mainFont(NULL), iconFont(NULL), furIconFont(NULL), @@ -8800,15 +8808,7 @@ FurnaceGUI::FurnaceGUI(): romMultiFile(false), romExportSave(false), pendingExport(NULL), - romExportExists(false), - songLength(0), - songLoopedSectionLength(0), - songFadeoutSectionLength(0), - songHasSongEndCommand(false), - lengthOfOneFile(0), - totalLength(0), - curProgress(0.0f), - totalFiles(0) { + romExportExists(false) { // value keys valueKeys[SDLK_0]=0; valueKeys[SDLK_1]=1; From a08380182daf929cb0e4606dd80bfa5da8104511 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:06:12 +0300 Subject: [PATCH 13/13] arr 2 --- src/gui/gui.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 39a24c482..254065992 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -8394,6 +8394,13 @@ FurnaceGUI::FurnaceGUI(): totalBackupSize(0), refreshBackups(true), learning(-1), + mainFont(NULL), + iconFont(NULL), + furIconFont(NULL), + patFont(NULL), + bigFont(NULL), + headFont(NULL), + fontRange(NULL), songLength(0), songLoopedSectionLength(0), songFadeoutSectionLength(0), @@ -8402,13 +8409,6 @@ FurnaceGUI::FurnaceGUI(): totalLength(0), curProgress(0.0f), totalFiles(0), - mainFont(NULL), - iconFont(NULL), - furIconFont(NULL), - patFont(NULL), - bigFont(NULL), - headFont(NULL), - fontRange(NULL), localeRequiresJapanese(false), localeRequiresChinese(false), localeRequiresChineseTrad(false),