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] 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;