looks like it works, no account for fadeout tho
This commit is contained in:
parent
a77d8bc87e
commit
d79bad195a
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
if (exportFormat==DIV_EXPORT_FORMAT_S16) {
|
||||||
si[i].format=SF_FORMAT_WAV|SF_FORMAT_PCM_16;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue