looks like it works, no account for fadeout tho

This commit is contained in:
LTVA1 2024-08-20 22:02:07 +03:00 committed by tildearrow
parent a77d8bc87e
commit d79bad195a
8 changed files with 295 additions and 5 deletions

View file

@ -2,7 +2,7 @@ name: Build furnace
on: on:
push: push:
branches: [master, locale] branches: [master, locale, audio-export-progress-bar]
pull_request: pull_request:
branches: master branches: master

View file

@ -199,6 +199,12 @@ void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) {
} }
} }
void DivEngine::findSongLength(bool& hasFFxx, std::vector<int>& 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 #define EXPORT_BUFSIZE 2048
double DivEngine::benchmarkPlayback() { double DivEngine::benchmarkPlayback() {

View file

@ -474,7 +474,7 @@ class DivEngine {
int midiOutTimeRate; int midiOutTimeRate;
float midiVolExp; float midiVolExp;
int softLockCount; 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 curSubSongIndex;
size_t bufferPos; size_t bufferPos;
double divider; double divider;
@ -816,6 +816,9 @@ class DivEngine {
// find song loop position // find song loop position
void walkSong(int& loopOrder, int& loopRow, int& loopEnd); 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<int>& orders, int& length, int loopOrder, int loopRow, int loopEnd);
// play (returns whether successful) // play (returns whether successful)
bool play(); bool play();
@ -1007,6 +1010,21 @@ class DivEngine {
// is exporting // is exporting
bool isExporting(); 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 // add instrument
int addInstrument(int refChan=0, DivInstrumentType fallbackType=DIV_INS_STD); int addInstrument(int refChan=0, DivInstrumentType fallbackType=DIV_INS_STD);
@ -1400,6 +1418,7 @@ class DivEngine {
totalLoops(0), totalLoops(0),
lastLoopPos(0), lastLoopPos(0),
exportLoopCount(0), exportLoopCount(0),
curExportChan(0),
nextSpeed(3), nextSpeed(3),
elapsedBars(0), elapsedBars(0),
elapsedBeats(0), elapsedBeats(0),

View file

@ -102,6 +102,134 @@ bool DivSubSong::walk(int& loopOrder, int& loopRow, int& loopEnd, int chans, int
return false; return false;
} }
void DivSubSong::findLength(bool& hasFFxx, std::vector<int>& 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; i<ordersLen; i++)
{
bool jumped = false;
for (int j=0; j<chans; j++)
{
subPat[j]=pat[j].getPattern(orders.ord[j][i],false);
}
if (i>lastSuspectedLoopEnd)
{
lastSuspectedLoopEnd=i;
}
for (int j=nextRow; j<patLen; j++)
{
nextRow=0;
bool changingOrder=false;
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<chans; k++)
{
for (int l=0; l<pat[k].effectCols; l++)
{
effectVal=subPat[k]->data[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 ((i<ordersLen-1 || !ignoreJumpAtEnd))
{
nextOrder=i+1;
nextRow=effectVal;
jumpingOrder=true;
}
}
else if (jumpTreatment==1)
{
if (nextOrder==-1 && (i<ordersLen-1 || !ignoreJumpAtEnd))
{
nextOrder=i+1;
nextRow=effectVal;
jumpingOrder=true;
}
}
else
{
if ((i<ordersLen-1 || !ignoreJumpAtEnd))
{
if (!changingOrder)
{
nextOrder=i+1;
}
jumpingOrder=true;
nextRow=effectVal;
}
}
}
else if (subPat[k]->data[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() { void DivSubSong::clearData() {
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
pat[i].wipePatterns(); pat[i].wipePatterns();

View file

@ -185,6 +185,11 @@ struct DivSubSong {
*/ */
bool walk(int& loopOrder, int& loopRow, int& loopEnd, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat=0); bool walk(int& loopOrder, int& loopRow, int& loopEnd, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat=0);
/**
* find song length in rows (up to specified loop point). Also find length of every row
*/
void findLength(bool& hasFFxx, std::vector<int>& orders, int& length, int loopOrder, int loopRow, int loopEnd, int chans, int jumpTreatment, int ignoreJumpAtEnd, int firstPat=0);
void clearData(); void clearData();
void optimizePatterns(); void optimizePatterns();
void rearrangePatterns(); void rearrangePatterns();

View file

@ -33,6 +33,77 @@ bool DivEngine::isExporting() {
return exporting; 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 #ifdef HAVE_SNDFILE
void DivEngine::runExportThread() { void DivEngine::runExportThread() {
size_t fadeOutSamples=got.rate*exportFadeOut; size_t fadeOutSamples=got.rate*exportFadeOut;
@ -140,7 +211,11 @@ void DivEngine::runExportThread() {
sf[i]=NULL; sf[i]=NULL;
si[i].samplerate=got.rate; si[i].samplerate=got.rate;
si[i].channels=disCont[i].dispatch->getOutputCount(); 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; i<song.systemLen; i++) { for (int i=0; i<song.systemLen; i++) {
@ -247,6 +322,8 @@ void DivEngine::runExportThread() {
// take control of audio output // take control of audio output
deinitAudioBackend(); deinitAudioBackend();
curExportChan = 0;
float* outBuf[DIV_MAX_OUTPUTS]; float* outBuf[DIV_MAX_OUTPUTS];
float* outBufFinal; float* outBufFinal;
for (int i=0; i<exportOutputs; i++) { for (int i=0; i<exportOutputs; i++) {
@ -258,6 +335,7 @@ void DivEngine::runExportThread() {
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
if (!exportChannelMask[i]) continue; if (!exportChannelMask[i]) continue;
SNDFILE* sf; SNDFILE* sf;
SF_INFO si; SF_INFO si;
SFWrapper sfWrap; SFWrapper sfWrap;
@ -338,6 +416,8 @@ void DivEngine::runExportThread() {
} }
} }
curExportChan++;
if (sfWrap.doClose()!=0) { if (sfWrap.doClose()!=0) {
logE("could not close audio file!"); logE("could not close audio file!");
} }
@ -378,6 +458,7 @@ void DivEngine::runExportThread() {
} }
logI("done!"); logI("done!");
exporting=false; exporting=false;
curExportChan = 0;
break; break;
} }
} }

View file

@ -995,7 +995,7 @@ Collapsed=0\n\
\n\ \n\
[Window][Rendering...]\n\ [Window][Rendering...]\n\
Pos=585,342\n\ Pos=585,342\n\
Size=114,71\n\ Size=600,100\n\
Collapsed=0\n\ Collapsed=0\n\
\n\ \n\
[Window][Export VGM##FileDialog]\n\ [Window][Export VGM##FileDialog]\n\
@ -2582,6 +2582,12 @@ int FurnaceGUI::loadStream(String path) {
void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) { void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) {
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
e->saveAudio(path.c_str(),audioExportOptions); e->saveAudio(path.c_str(),audioExportOptions);
displayExporting=true; displayExporting=true;
} }
@ -5820,8 +5826,49 @@ bool FurnaceGUI::loop() {
MEASURE_BEGIN(popup); MEASURE_BEGIN(popup);
centerNextWindow(_("Rendering..."),canvasW,canvasH); centerNextWindow(_("Rendering..."),canvasW,canvasH);
if (ImGui::BeginPopupModal(_("Rendering..."),NULL,ImGuiWindowFlags_AlwaysAutoResize)) { if (ImGui::BeginPopupModal(_("Rendering..."),NULL,ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) {
ImGui::Text(_("Please wait...")); ImGui::Text(_("Please wait..."));
int songLengthLambda = songLength;
std::vector<int>* 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 (ImGui::Button(_("Abort"))) {
if (e->haltAudioFile()) { if (e->haltAudioFile()) {
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();

View file

@ -1723,6 +1723,10 @@ class FurnaceGUI {
char emptyLabel[32]; char emptyLabel[32];
char emptyLabel2[32]; char emptyLabel2[32];
std::vector<int> 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 { struct Settings {
bool settingsChanged; bool settingsChanged;
int mainFontSize, patFontSize, headFontSize, iconSize; int mainFontSize, patFontSize, headFontSize, iconSize;