Merge branch 'master' of https://github.com/tildearrow/furnace into k053260
This commit is contained in:
commit
548aa5e6ac
|
@ -24,7 +24,7 @@ jobs:
|
||||||
#- { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 }
|
#- { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 }
|
||||||
- { name: 'macOS x86_64', os: macos-latest, arch: x86_64 }
|
- { name: 'macOS x86_64', os: macos-latest, arch: x86_64 }
|
||||||
- { name: 'macOS ARM', os: macos-latest, arch: arm64 }
|
- { name: 'macOS ARM', os: macos-latest, arch: arm64 }
|
||||||
- { name: 'Linux x86_64', os: ubuntu-18.04, arch: x86_64 }
|
- { name: 'Linux x86_64', os: ubuntu-20.04, arch: x86_64 }
|
||||||
#- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf }
|
#- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf }
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
|
|
|
@ -793,7 +793,7 @@ endif()
|
||||||
if(ANDROID AND NOT TERMUX)
|
if(ANDROID AND NOT TERMUX)
|
||||||
add_library(furnace SHARED ${USED_SOURCES})
|
add_library(furnace SHARED ${USED_SOURCES})
|
||||||
elseif(WIN32)
|
elseif(WIN32)
|
||||||
add_executable(furnace WIN32 ${USED_SOURCES})
|
add_executable(furnace ${USED_SOURCES})
|
||||||
else()
|
else()
|
||||||
add_executable(furnace ${USED_SOURCES})
|
add_executable(furnace ${USED_SOURCES})
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
# to-do for 0.6pre5
|
||||||
|
|
||||||
|
- tutorial
|
||||||
|
- ease-of-use improvements... ideas:
|
||||||
|
- preset compat flags
|
||||||
|
- setting to toggle the Choose a System screen on new project
|
||||||
|
- maybe reduced set of presets for the sake of simplicity
|
||||||
|
- a more preferable highlight/drag system
|
||||||
|
- some speed/intuitive workflow improvements that go a long way
|
||||||
|
- Had a hard time finding the docs on github and in Furnace's folder.
|
||||||
|
- make .pdf manual out of papers/doc/
|
||||||
|
- you're going too fast; please slow down
|
||||||
|
- break compatibility if it relieves complexity
|
||||||
|
- ins/wave/sample organization (folders and all)
|
||||||
|
- multi-key binds
|
||||||
|
- bug fixes
|
Binary file not shown.
Binary file not shown.
|
@ -837,6 +837,8 @@ CODE
|
||||||
#include <stdint.h> // intptr_t
|
#include <stdint.h> // intptr_t
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "../../src/fileutils.h"
|
||||||
|
|
||||||
// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
|
// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
|
||||||
#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
|
#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
|
||||||
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
|
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
|
||||||
|
@ -12457,14 +12459,47 @@ void ImGui::ClearIniSettings()
|
||||||
g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
|
g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
|
bool ImGui::LoadIniSettingsFromDisk(const char* ini_filename, bool redundancy)
|
||||||
{
|
{
|
||||||
size_t file_data_size = 0;
|
size_t file_data_size = 0;
|
||||||
char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
|
char* file_data = NULL;
|
||||||
|
|
||||||
|
if (redundancy) {
|
||||||
|
char fileName[4096];
|
||||||
|
|
||||||
|
for (int i=0; i<5; i++) {
|
||||||
|
bool viable=false;
|
||||||
|
if (i>0) {
|
||||||
|
snprintf(fileName,4095,"%s.%d",ini_filename,i);
|
||||||
|
} else {
|
||||||
|
strncpy(fileName,ini_filename,4095);
|
||||||
|
}
|
||||||
|
file_data=(char*)ImFileLoadToMemory(fileName, "rb", &file_data_size);
|
||||||
|
if (!file_data) continue;
|
||||||
|
|
||||||
|
for (size_t j=0; j<file_data_size; j++) {
|
||||||
|
if (file_data[j]!='\r' && file_data[j]!='\n' && file_data[j]!=' ') {
|
||||||
|
viable=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!viable) {
|
||||||
|
IM_FREE(file_data);
|
||||||
|
file_data=NULL;
|
||||||
|
file_data_size=0;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
file_data=(char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
|
||||||
|
}
|
||||||
if (!file_data)
|
if (!file_data)
|
||||||
return;
|
return false;
|
||||||
LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
|
LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
|
||||||
IM_FREE(file_data);
|
IM_FREE(file_data);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero-tolerance, no error reporting, cheap .ini parsing
|
// Zero-tolerance, no error reporting, cheap .ini parsing
|
||||||
|
@ -12538,20 +12573,45 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
|
||||||
g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
|
g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
|
bool ImGui::SaveIniSettingsToDisk(const char* ini_filename, bool redundancy)
|
||||||
{
|
{
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *GImGui;
|
||||||
g.SettingsDirtyTimer = 0.0f;
|
g.SettingsDirtyTimer = 0.0f;
|
||||||
if (!ini_filename)
|
if (!ini_filename)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
|
if (redundancy) {
|
||||||
|
char oldPath[4096];
|
||||||
|
char newPath[4096];
|
||||||
|
|
||||||
|
if (fileExists(ini_filename)==1) {
|
||||||
|
for (int i=4; i>=0; i--) {
|
||||||
|
if (i>0) {
|
||||||
|
snprintf(oldPath,4095,"%s.%d",ini_filename,i);
|
||||||
|
} else {
|
||||||
|
strncpy(oldPath,ini_filename,4095);
|
||||||
|
}
|
||||||
|
snprintf(newPath,4095,"%s.%d",ini_filename,i+1);
|
||||||
|
|
||||||
|
if (i>=4) {
|
||||||
|
deleteFile(oldPath);
|
||||||
|
} else {
|
||||||
|
moveFiles(oldPath,newPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size_t ini_data_size = 0;
|
size_t ini_data_size = 0;
|
||||||
const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
|
const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
|
||||||
ImFileHandle f = ImFileOpen(ini_filename, "wt");
|
ImFileHandle f = ImFileOpen(ini_filename, "wt");
|
||||||
if (!f)
|
if (!f)
|
||||||
return;
|
return false;
|
||||||
ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
|
bool areEqual=ImFileWrite(ini_data, sizeof(char), ini_data_size, f)==ini_data_size;
|
||||||
|
IM_ASSERT_USER_ERROR(areEqual, "ImFileWrite failed to write file!");
|
||||||
ImFileClose(f);
|
ImFileClose(f);
|
||||||
|
if (!areEqual && redundancy) deleteFile(ini_filename);
|
||||||
|
return areEqual;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
|
// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
|
||||||
|
|
|
@ -950,9 +950,9 @@ namespace ImGui
|
||||||
// - The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini").
|
// - The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini").
|
||||||
// - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually.
|
// - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually.
|
||||||
// - Important: default value "imgui.ini" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables).
|
// - Important: default value "imgui.ini" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables).
|
||||||
IMGUI_API void LoadIniSettingsFromDisk(const char* ini_filename); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename).
|
IMGUI_API bool LoadIniSettingsFromDisk(const char* ini_filename, bool redundancy=false); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename).
|
||||||
IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source.
|
IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source.
|
||||||
IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext).
|
IMGUI_API bool SaveIniSettingsToDisk(const char* ini_filename, bool redundancy=false); // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext).
|
||||||
IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings.
|
IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings.
|
||||||
|
|
||||||
// Debug Utilities
|
// Debug Utilities
|
||||||
|
|
|
@ -32,6 +32,15 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
|
||||||
|
|
||||||
the format versions are:
|
the format versions are:
|
||||||
|
|
||||||
|
- 152: Furnace dev152
|
||||||
|
- 151: Furnace dev151
|
||||||
|
- 150: Furnace dev150
|
||||||
|
- 149: Furnace dev149
|
||||||
|
- 148: Furnace dev148
|
||||||
|
- 147: Furnace dev147
|
||||||
|
- 146: Furnace Pro (joke version)
|
||||||
|
- 145: Furnace dev145
|
||||||
|
- 144: Furnace dev144
|
||||||
- 143: Furnace 0.6pre4
|
- 143: Furnace 0.6pre4
|
||||||
- 142: Furnace dev142
|
- 142: Furnace dev142
|
||||||
- 141: Furnace Tournament Edition (for intro tune contest)
|
- 141: Furnace Tournament Edition (for intro tune contest)
|
||||||
|
|
|
@ -389,7 +389,7 @@ the sample map format:
|
||||||
```
|
```
|
||||||
size | description
|
size | description
|
||||||
-----|------------------------------------
|
-----|------------------------------------
|
||||||
2 | note to play
|
2 | note to play (>=152) or reserved
|
||||||
2 | sample to play
|
2 | sample to play
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -23,21 +23,54 @@
|
||||||
#include "../fileutils.h"
|
#include "../fileutils.h"
|
||||||
#include <fmt/printf.h>
|
#include <fmt/printf.h>
|
||||||
|
|
||||||
bool DivConfig::save(const char* path) {
|
#define REDUNDANCY_NUM_ATTEMPTS 5
|
||||||
|
#define CHECK_BUF_SIZE 8192
|
||||||
|
|
||||||
|
bool DivConfig::save(const char* path, bool redundancy) {
|
||||||
|
if (redundancy) {
|
||||||
|
char oldPath[4096];
|
||||||
|
char newPath[4096];
|
||||||
|
|
||||||
|
if (fileExists(path)==1) {
|
||||||
|
logD("rotating config files...");
|
||||||
|
for (int i=4; i>=0; i--) {
|
||||||
|
if (i>0) {
|
||||||
|
snprintf(oldPath,4095,"%s.%d",path,i);
|
||||||
|
} else {
|
||||||
|
strncpy(oldPath,path,4095);
|
||||||
|
}
|
||||||
|
snprintf(newPath,4095,"%s.%d",path,i+1);
|
||||||
|
|
||||||
|
if (i>=4) {
|
||||||
|
logV("remove %s",oldPath);
|
||||||
|
deleteFile(oldPath);
|
||||||
|
} else {
|
||||||
|
logV("move %s to %s",oldPath,newPath);
|
||||||
|
moveFiles(oldPath,newPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logD("opening config for write: %s",path);
|
||||||
FILE* f=ps_fopen(path,"wb");
|
FILE* f=ps_fopen(path,"wb");
|
||||||
if (f==NULL) {
|
if (f==NULL) {
|
||||||
logW("could not write config file! %s",strerror(errno));
|
logW("could not write config file! %s",strerror(errno));
|
||||||
|
reportError(fmt::sprintf("could not write config file! %s",strerror(errno)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (auto& i: conf) {
|
for (auto& i: conf) {
|
||||||
String toWrite=fmt::sprintf("%s=%s\n",i.first,i.second);
|
String toWrite=fmt::sprintf("%s=%s\n",i.first,i.second);
|
||||||
if (fwrite(toWrite.c_str(),1,toWrite.size(),f)!=toWrite.size()) {
|
if (fwrite(toWrite.c_str(),1,toWrite.size(),f)!=toWrite.size()) {
|
||||||
logW("could not write config file! %s",strerror(errno));
|
logW("could not write config file! %s",strerror(errno));
|
||||||
|
reportError(fmt::sprintf("could not write config file! %s",strerror(errno)));
|
||||||
|
logV("removing config file");
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
deleteFile(path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
logD("config file written successfully.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +96,7 @@ void DivConfig::parseLine(const char* line) {
|
||||||
String value="";
|
String value="";
|
||||||
bool keyOrValue=false;
|
bool keyOrValue=false;
|
||||||
for (const char* i=line; *i; i++) {
|
for (const char* i=line; *i; i++) {
|
||||||
|
if (*i=='\r') continue;
|
||||||
if (*i=='\n') continue;
|
if (*i=='\n') continue;
|
||||||
if (keyOrValue) {
|
if (keyOrValue) {
|
||||||
value+=*i;
|
value+=*i;
|
||||||
|
@ -79,17 +113,94 @@ void DivConfig::parseLine(const char* line) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DivConfig::loadFromFile(const char* path, bool createOnFail) {
|
bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundancy) {
|
||||||
char line[4096];
|
char line[4096];
|
||||||
FILE* f=ps_fopen(path,"rb");
|
logD("opening config for read: %s",path);
|
||||||
if (f==NULL) {
|
|
||||||
if (createOnFail) {
|
FILE* f=NULL;
|
||||||
logI("creating default config.");
|
|
||||||
return save(path);
|
if (redundancy) {
|
||||||
} else {
|
unsigned char* readBuf=new unsigned char[CHECK_BUF_SIZE];
|
||||||
return false;
|
size_t readBufLen=0;
|
||||||
|
for (int i=0; i<REDUNDANCY_NUM_ATTEMPTS; i++) {
|
||||||
|
bool viable=false;
|
||||||
|
if (i>0) {
|
||||||
|
snprintf(line,4095,"%s.%d",path,i);
|
||||||
|
} else {
|
||||||
|
strncpy(line,path,4095);
|
||||||
|
}
|
||||||
|
logV("trying: %s",line);
|
||||||
|
|
||||||
|
// try to open config
|
||||||
|
f=ps_fopen(line,"rb");
|
||||||
|
// check whether we could open it
|
||||||
|
if (f==NULL) {
|
||||||
|
logV("fopen(): %s",strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether there's something
|
||||||
|
while (!feof(f)) {
|
||||||
|
readBufLen=fread(readBuf,1,CHECK_BUF_SIZE,f);
|
||||||
|
if (ferror(f)) {
|
||||||
|
logV("fread(): %s",strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t j=0; j<readBufLen; j++) {
|
||||||
|
if (readBuf[j]!='\r' && readBuf[j]!='\n' && readBuf[j]!=' ') {
|
||||||
|
viable=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viable) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's something
|
||||||
|
if (viable) {
|
||||||
|
if (fseek(f,0,SEEK_SET)==-1) {
|
||||||
|
logV("fseek(): %s",strerror(errno));
|
||||||
|
viable=false;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// close it (because there's nothing)
|
||||||
|
fclose(f);
|
||||||
|
f=NULL;
|
||||||
|
}
|
||||||
|
delete[] readBuf;
|
||||||
|
|
||||||
|
// we couldn't read at all
|
||||||
|
if (f==NULL) {
|
||||||
|
logD("config does not exist");
|
||||||
|
if (createOnFail) {
|
||||||
|
logI("creating default config.");
|
||||||
|
reportError(fmt::sprintf("Creating default config: %s",strerror(errno)));
|
||||||
|
return save(path,redundancy);
|
||||||
|
} else {
|
||||||
|
reportError(fmt::sprintf("COULD NOT LOAD CONFIG %s",strerror(errno)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f=ps_fopen(path,"rb");
|
||||||
|
if (f==NULL) {
|
||||||
|
logD("config does not exist");
|
||||||
|
if (createOnFail) {
|
||||||
|
logI("creating default config.");
|
||||||
|
reportError(fmt::sprintf("Creating default config: %s",strerror(errno)));
|
||||||
|
return save(path);
|
||||||
|
} else {
|
||||||
|
reportError(fmt::sprintf("COULD NOT LOAD CONFIG %s",strerror(errno)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
logI("loading config.");
|
logI("loading config.");
|
||||||
while (!feof(f)) {
|
while (!feof(f)) {
|
||||||
if (fgets(line,4095,f)==NULL) {
|
if (fgets(line,4095,f)==NULL) {
|
||||||
|
@ -97,6 +208,7 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail) {
|
||||||
}
|
}
|
||||||
parseLine(line);
|
parseLine(line);
|
||||||
}
|
}
|
||||||
|
logD("end of file (%s)",strerror(errno));
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -175,6 +287,33 @@ String DivConfig::getString(String key, String fallback) const {
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<int> DivConfig::getIntList(String key, std::initializer_list<int> fallback) const {
|
||||||
|
String next;
|
||||||
|
std::vector<int> ret;
|
||||||
|
try {
|
||||||
|
String val=conf.at(key);
|
||||||
|
|
||||||
|
for (char i: val) {
|
||||||
|
if (i==',') {
|
||||||
|
int num=std::stoi(next);
|
||||||
|
ret.push_back(num);
|
||||||
|
next="";
|
||||||
|
} else {
|
||||||
|
next+=i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!next.empty()) {
|
||||||
|
int num=std::stoi(next);
|
||||||
|
ret.push_back(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
} catch (std::out_of_range& e) {
|
||||||
|
} catch (std::invalid_argument& e) {
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
bool DivConfig::has(String key) const {
|
bool DivConfig::has(String key) const {
|
||||||
try {
|
try {
|
||||||
String test=conf.at(key);
|
String test=conf.at(key);
|
||||||
|
@ -212,6 +351,17 @@ void DivConfig::set(String key, String value) {
|
||||||
conf[key]=value;
|
conf[key]=value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DivConfig::set(String key, const std::vector<int>& value) {
|
||||||
|
String val;
|
||||||
|
bool comma=false;
|
||||||
|
for (int i: value) {
|
||||||
|
if (comma) val+=',';
|
||||||
|
val+=fmt::sprintf("%d",i);
|
||||||
|
comma=true;
|
||||||
|
}
|
||||||
|
conf[key]=val;
|
||||||
|
}
|
||||||
|
|
||||||
bool DivConfig::remove(String key) {
|
bool DivConfig::remove(String key) {
|
||||||
return conf.erase(key);
|
return conf.erase(key);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
|
|
||||||
#include "../ta-utils.h"
|
#include "../ta-utils.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
class DivConfig {
|
class DivConfig {
|
||||||
std::map<String,String> conf;
|
std::map<String,String> conf;
|
||||||
|
@ -30,10 +32,10 @@ class DivConfig {
|
||||||
// config loading/saving
|
// config loading/saving
|
||||||
bool loadFromMemory(const char* buf);
|
bool loadFromMemory(const char* buf);
|
||||||
bool loadFromBase64(const char* buf);
|
bool loadFromBase64(const char* buf);
|
||||||
bool loadFromFile(const char* path, bool createOnFail=true);
|
bool loadFromFile(const char* path, bool createOnFail=true, bool redundancy=false);
|
||||||
String toString();
|
String toString();
|
||||||
String toBase64();
|
String toBase64();
|
||||||
bool save(const char* path);
|
bool save(const char* path, bool redundancy=false);
|
||||||
|
|
||||||
// get the map
|
// get the map
|
||||||
const std::map<String,String>& configMap();
|
const std::map<String,String>& configMap();
|
||||||
|
@ -44,6 +46,7 @@ class DivConfig {
|
||||||
float getFloat(String key, float fallback) const;
|
float getFloat(String key, float fallback) const;
|
||||||
double getDouble(String key, double fallback) const;
|
double getDouble(String key, double fallback) const;
|
||||||
String getString(String key, String fallback) const;
|
String getString(String key, String fallback) const;
|
||||||
|
std::vector<int> getIntList(String key, std::initializer_list<int> fallback) const;
|
||||||
|
|
||||||
// check for existence
|
// check for existence
|
||||||
bool has(String key) const;
|
bool has(String key) const;
|
||||||
|
@ -55,6 +58,7 @@ class DivConfig {
|
||||||
void set(String key, double value);
|
void set(String key, double value);
|
||||||
void set(String key, const char* value);
|
void set(String key, const char* value);
|
||||||
void set(String key, String value);
|
void set(String key, String value);
|
||||||
|
void set(String key, const std::vector<int>& value);
|
||||||
|
|
||||||
// remove a config value
|
// remove a config value
|
||||||
bool remove(String key);
|
bool remove(String key);
|
||||||
|
|
|
@ -110,12 +110,12 @@ void DivEngine::initConfDir() {
|
||||||
|
|
||||||
bool DivEngine::saveConf() {
|
bool DivEngine::saveConf() {
|
||||||
configFile=configPath+String(CONFIG_FILE);
|
configFile=configPath+String(CONFIG_FILE);
|
||||||
return conf.save(configFile.c_str());
|
return conf.save(configFile.c_str(),true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DivEngine::loadConf() {
|
bool DivEngine::loadConf() {
|
||||||
configFile=configPath+String(CONFIG_FILE);
|
configFile=configPath+String(CONFIG_FILE);
|
||||||
return conf.loadFromFile(configFile.c_str());
|
return conf.loadFromFile(configFile.c_str(),true,true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DivEngine::getConfBool(String key, bool fallback) {
|
bool DivEngine::getConfBool(String key, bool fallback) {
|
||||||
|
|
|
@ -2441,6 +2441,16 @@ void DivEngine::stop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset all chan oscs
|
||||||
|
for (int i=0; i<chans; i++) {
|
||||||
|
DivDispatchOscBuffer* buf=disCont[dispatchOfChan[i]].dispatch->getOscBuffer(dispatchChanOfChan[i]);
|
||||||
|
if (buf!=NULL) {
|
||||||
|
memset(buf->data,0,65536*sizeof(short));
|
||||||
|
buf->needle=0;
|
||||||
|
buf->readNeedle=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,8 +53,8 @@
|
||||||
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
|
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
|
||||||
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
|
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
|
||||||
|
|
||||||
#define DIV_VERSION "dev147"
|
#define DIV_VERSION "dev152"
|
||||||
#define DIV_ENGINE_VERSION 147
|
#define DIV_ENGINE_VERSION 152
|
||||||
// for imports
|
// for imports
|
||||||
#define DIV_VERSION_MOD 0xff01
|
#define DIV_VERSION_MOD 0xff01
|
||||||
#define DIV_VERSION_FC 0xff02
|
#define DIV_VERSION_FC 0xff02
|
||||||
|
|
|
@ -696,6 +696,13 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
ds.wave.push_back(wave);
|
ds.wave.push_back(wave);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sometimes there's a single length 0 wavetable in the file. I don't know why.
|
||||||
|
if (ds.waveLen==1) {
|
||||||
|
if (ds.wave[0]->len==0) {
|
||||||
|
ds.clearWavetables();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logV("%x",reader.tell());
|
logV("%x",reader.tell());
|
||||||
|
@ -850,6 +857,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
sample->rate=22050;
|
sample->rate=22050;
|
||||||
if (ds.version>=0x0b) {
|
if (ds.version>=0x0b) {
|
||||||
sample->rate=fileToDivRate(reader.readC());
|
sample->rate=fileToDivRate(reader.readC());
|
||||||
|
sample->centerRate=sample->rate;
|
||||||
pitch=reader.readC();
|
pitch=reader.readC();
|
||||||
vol=reader.readC();
|
vol=reader.readC();
|
||||||
}
|
}
|
||||||
|
@ -874,24 +882,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
// what the hell man...
|
// what the hell man...
|
||||||
cutStart=reader.readI();
|
cutStart=reader.readI();
|
||||||
cutEnd=reader.readI();
|
cutEnd=reader.readI();
|
||||||
if (cutStart<0 || cutStart>length) {
|
logV("cutStart: %d cutEnd: %d",cutStart,cutEnd);
|
||||||
logE("cutStart is out of range! (%d)",cutStart);
|
|
||||||
lastError="file is corrupt or unreadable at samples";
|
|
||||||
delete[] file;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cutEnd<0 || cutEnd>length) {
|
|
||||||
logE("cutEnd is out of range! (%d)",cutEnd);
|
|
||||||
lastError="file is corrupt or unreadable at samples";
|
|
||||||
delete[] file;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cutEnd<cutStart) {
|
|
||||||
logE("cutEnd %d is before cutStart %d. what's going on?",cutEnd,cutStart);
|
|
||||||
lastError="file is corrupt or unreadable at samples";
|
|
||||||
delete[] file;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (length>0) {
|
if (length>0) {
|
||||||
if (ds.version>0x08) {
|
if (ds.version>0x08) {
|
||||||
|
@ -903,19 +894,6 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
data=new short[length];
|
data=new short[length];
|
||||||
reader.read(data,length*2);
|
reader.read(data,length*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ds.version>0x1b) {
|
|
||||||
if (cutStart!=0 || cutEnd!=length) {
|
|
||||||
// cut data
|
|
||||||
short* newData=new short[cutEnd-cutStart];
|
|
||||||
memcpy(newData,&data[cutStart],(cutEnd-cutStart)*sizeof(short));
|
|
||||||
delete[] data;
|
|
||||||
data=newData;
|
|
||||||
length=cutEnd-cutStart;
|
|
||||||
cutStart=0;
|
|
||||||
cutEnd=length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef TA_BIG_ENDIAN
|
#ifdef TA_BIG_ENDIAN
|
||||||
// convert to big-endian
|
// convert to big-endian
|
||||||
|
@ -924,27 +902,73 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (pitch!=5) {
|
int scaledLen=(double)length/samplePitches[pitch];
|
||||||
|
|
||||||
|
if (scaledLen>0) {
|
||||||
|
// resample
|
||||||
logD("%d: scaling from %d...",i,pitch);
|
logD("%d: scaling from %d...",i,pitch);
|
||||||
}
|
|
||||||
|
short* newData=new short[scaledLen];
|
||||||
// render data
|
int k=0;
|
||||||
if (!sample->init((double)length/samplePitches[pitch])) {
|
float mult=(float)(vol)/50.0f;
|
||||||
logE("%d: error while initializing sample!",i);
|
for (double j=0; j<length; j+=samplePitches[pitch]) {
|
||||||
}
|
if (k>=scaledLen) {
|
||||||
|
break;
|
||||||
unsigned int k=0;
|
}
|
||||||
float mult=(float)(vol)/50.0f;
|
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
|
||||||
for (double j=0; j<length; j+=samplePitches[pitch]) {
|
float next=(float)(data[(unsigned int)j]-0x80)*mult;
|
||||||
if (k>=sample->samples) {
|
newData[k++]=fmin(fmax(next,-128),127);
|
||||||
break;
|
} else {
|
||||||
|
float next=(float)data[(unsigned int)j]*mult;
|
||||||
|
newData[k++]=fmin(fmax(next,-32768),32767);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
|
|
||||||
float next=(float)(data[(unsigned int)j]-0x80)*mult;
|
delete[] data;
|
||||||
sample->data8[k++]=fmin(fmax(next,-128),127);
|
data=newData;
|
||||||
} else {
|
}
|
||||||
float next=(float)data[(unsigned int)j]*mult;
|
|
||||||
sample->data16[k++]=fmin(fmax(next,-32768),32767);
|
if (ds.version>=0x1b) {
|
||||||
|
if (cutStart<0 || cutStart>scaledLen) {
|
||||||
|
logE("cutStart is out of range! (%d)",cutStart);
|
||||||
|
lastError="file is corrupt or unreadable at samples";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cutEnd<0 || cutEnd>scaledLen) {
|
||||||
|
logE("cutEnd is out of range! (%d)",cutEnd);
|
||||||
|
lastError="file is corrupt or unreadable at samples";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cutEnd<cutStart) {
|
||||||
|
logE("cutEnd %d is before cutStart %d. what's going on?",cutEnd,cutStart);
|
||||||
|
lastError="file is corrupt or unreadable at samples";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cutStart!=0 || cutEnd!=scaledLen) {
|
||||||
|
// cut data
|
||||||
|
short* newData=new short[cutEnd-cutStart];
|
||||||
|
memcpy(newData,&data[cutStart],(cutEnd-cutStart)*sizeof(short));
|
||||||
|
delete[] data;
|
||||||
|
data=newData;
|
||||||
|
scaledLen=cutEnd-cutStart;
|
||||||
|
cutStart=0;
|
||||||
|
cutEnd=scaledLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy data
|
||||||
|
if (!sample->init(scaledLen)) {
|
||||||
|
logE("%d: error while initializing sample!",i);
|
||||||
|
} else {
|
||||||
|
for (int i=0; i<scaledLen; i++) {
|
||||||
|
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
|
||||||
|
sample->data8[i]=data[i];
|
||||||
|
} else {
|
||||||
|
sample->data16[i]=data[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5183,7 +5207,6 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
for (int i=0; i<song.insLen; i++) {
|
for (int i=0; i<song.insLen; i++) {
|
||||||
DivInstrument* ins=song.ins[i];
|
DivInstrument* ins=song.ins[i];
|
||||||
insPtr.push_back(w->tell());
|
insPtr.push_back(w->tell());
|
||||||
logV("writing instrument %d...",i);
|
|
||||||
ins->putInsData2(w,false);
|
ins->putInsData2(w,false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -214,7 +214,6 @@ bool DivInstrumentSNES::operator==(const DivInstrumentSNES& other) {
|
||||||
#undef _C
|
#undef _C
|
||||||
|
|
||||||
#define FEATURE_BEGIN(x) \
|
#define FEATURE_BEGIN(x) \
|
||||||
logV("- %s",x); \
|
|
||||||
w->write(x,2); \
|
w->write(x,2); \
|
||||||
size_t featStartSeek=w->tell(); \
|
size_t featStartSeek=w->tell(); \
|
||||||
w->writeS(0);
|
w->writeS(0);
|
||||||
|
@ -2103,6 +2102,12 @@ void DivInstrument::readFeatureSM(SafeReader& reader, short version) {
|
||||||
amiga.noteMap[note].freq=reader.readS();
|
amiga.noteMap[note].freq=reader.readS();
|
||||||
amiga.noteMap[note].map=reader.readS();
|
amiga.noteMap[note].map=reader.readS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version<152) {
|
||||||
|
for (int note=0; note<120; note++) {
|
||||||
|
amiga.noteMap[note].freq=note;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
READ_FEAT_END;
|
READ_FEAT_END;
|
||||||
|
@ -2970,6 +2975,12 @@ DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
|
||||||
for (int note=0; note<120; note++) {
|
for (int note=0; note<120; note++) {
|
||||||
amiga.noteMap[note].map=reader.readS();
|
amiga.noteMap[note].map=reader.readS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version<152) {
|
||||||
|
for (int note=0; note<120; note++) {
|
||||||
|
amiga.noteMap[note].freq=note;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -415,7 +415,7 @@ struct DivInstrumentAmiga {
|
||||||
if (note>119) note=119;
|
if (note>119) note=119;
|
||||||
return noteMap[note].freq;
|
return noteMap[note].freq;
|
||||||
}
|
}
|
||||||
return -1;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
DivInstrumentAmiga():
|
DivInstrumentAmiga():
|
||||||
|
@ -424,8 +424,9 @@ struct DivInstrumentAmiga {
|
||||||
useSample(false),
|
useSample(false),
|
||||||
useWave(false),
|
useWave(false),
|
||||||
waveLen(31) {
|
waveLen(31) {
|
||||||
for (SampleMap& elem: noteMap) {
|
for (int i=0; i<120; i++) {
|
||||||
elem=SampleMap();
|
noteMap[i].map=-1;
|
||||||
|
noteMap[i].freq=i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -560,7 +560,10 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
chan[c.chan].useWave=false;
|
chan[c.chan].useWave=false;
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
|
|
@ -292,8 +292,6 @@ void DivPlatformAY8910::tick(bool sysTick) {
|
||||||
if (chan[i].std.phaseReset.val==1) {
|
if (chan[i].std.phaseReset.val==1) {
|
||||||
if (chan[i].nextPSGMode.dac) {
|
if (chan[i].nextPSGMode.dac) {
|
||||||
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
||||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AY);
|
|
||||||
chan[i].dac.sample=ins->amiga.getSample(chan[i].note);
|
|
||||||
if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) {
|
if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) {
|
||||||
if (dumpWrites) {
|
if (dumpWrites) {
|
||||||
rWrite(0x08+i,0);
|
rWrite(0x08+i,0);
|
||||||
|
@ -405,7 +403,10 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
||||||
if (chan[c.chan].nextPSGMode.dac) {
|
if (chan[c.chan].nextPSGMode.dac) {
|
||||||
if (skipRegisterWrites) break;
|
if (skipRegisterWrites) break;
|
||||||
if (!parent->song.disableSampleMacro && (ins->type==DIV_INS_AMIGA || ins->amiga.useSample)) {
|
if (!parent->song.disableSampleMacro && (ins->type==DIV_INS_AMIGA || ins->amiga.useSample)) {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
|
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].dac.sample=-1;
|
chan[c.chan].dac.sample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||||
|
|
|
@ -280,8 +280,6 @@ void DivPlatformAY8930::tick(bool sysTick) {
|
||||||
if (chan[i].std.phaseReset.val==1) {
|
if (chan[i].std.phaseReset.val==1) {
|
||||||
if (chan[i].nextPSGMode.dac) {
|
if (chan[i].nextPSGMode.dac) {
|
||||||
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
||||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AY8930);
|
|
||||||
chan[i].dac.sample=ins->amiga.getSample(chan[i].note);
|
|
||||||
if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) {
|
if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) {
|
||||||
if (dumpWrites) {
|
if (dumpWrites) {
|
||||||
rWrite(0x08+i,0);
|
rWrite(0x08+i,0);
|
||||||
|
@ -406,7 +404,10 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
|
||||||
if (chan[c.chan].nextPSGMode.dac) {
|
if (chan[c.chan].nextPSGMode.dac) {
|
||||||
if (skipRegisterWrites) break;
|
if (skipRegisterWrites) break;
|
||||||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
|
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].dac.sample=-1;
|
chan[c.chan].dac.sample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||||
|
|
|
@ -410,7 +410,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
if (chan[i].pcmChanged.changed) {
|
if (chan[i].pcmChanged.changed) {
|
||||||
if (chan[i].pcmChanged.index) {
|
if (chan[i].pcmChanged.index) {
|
||||||
const int next=chan[i].pcm.next;
|
const int next=chan[i].pcm.next;
|
||||||
bool sampleVaild=false;
|
bool sampleValid=false;
|
||||||
if (((ins->amiga.useNoteMap) && (next>=0 && next<120)) ||
|
if (((ins->amiga.useNoteMap) && (next>=0 && next<120)) ||
|
||||||
((!ins->amiga.useNoteMap) && (next>=0 && next<parent->song.sampleLen))) {
|
((!ins->amiga.useNoteMap) && (next>=0 && next<parent->song.sampleLen))) {
|
||||||
DivInstrumentAmiga::SampleMap& noteMapind=ins->amiga.noteMap[next];
|
DivInstrumentAmiga::SampleMap& noteMapind=ins->amiga.noteMap[next];
|
||||||
|
@ -420,7 +420,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||||
const unsigned int offES5506=sampleOffES5506[sample];
|
const unsigned int offES5506=sampleOffES5506[sample];
|
||||||
sampleVaild=true;
|
sampleValid=true;
|
||||||
chan[i].pcm.index=sample;
|
chan[i].pcm.index=sample;
|
||||||
chan[i].pcm.isNoteMap=ins->amiga.useNoteMap;
|
chan[i].pcm.isNoteMap=ins->amiga.useNoteMap;
|
||||||
DivSample* s=parent->getSample(sample);
|
DivSample* s=parent->getSample(sample);
|
||||||
|
@ -459,7 +459,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sampleVaild) {
|
if (sampleValid) {
|
||||||
if (!chan[i].keyOn) {
|
if (!chan[i].keyOn) {
|
||||||
pageWrite(0x20|i,0x03,(chan[i].pcm.direction)?chan[i].pcm.end:chan[i].pcm.start);
|
pageWrite(0x20|i,0x03,(chan[i].pcm.direction)?chan[i].pcm.end:chan[i].pcm.start);
|
||||||
}
|
}
|
||||||
|
@ -750,12 +750,13 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
||||||
switch (c.cmd) {
|
switch (c.cmd) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_ES5506);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_ES5506);
|
||||||
bool sampleVaild=false;
|
bool sampleValid=false;
|
||||||
if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) ||
|
if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) ||
|
||||||
((!ins->amiga.useNoteMap) && (ins->amiga.initSample>=0 && ins->amiga.initSample<parent->song.sampleLen))) {
|
((!ins->amiga.useNoteMap) && (ins->amiga.initSample>=0 && ins->amiga.initSample<parent->song.sampleLen))) {
|
||||||
int sample=ins->amiga.getSample(c.value);
|
int sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||||
sampleVaild=true;
|
sampleValid=true;
|
||||||
chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xfff;
|
chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xfff;
|
||||||
chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xfff;
|
chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xfff;
|
||||||
chan[c.chan].pcm.note=c.value;
|
chan[c.chan].pcm.note=c.value;
|
||||||
|
@ -764,7 +765,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
||||||
chan[c.chan].envelope=ins->es5506.envelope;
|
chan[c.chan].envelope=ins->es5506.envelope;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!sampleVaild) {
|
if (!sampleValid) {
|
||||||
chan[c.chan].pcm.index=chan[c.chan].pcm.next=-1;
|
chan[c.chan].pcm.index=chan[c.chan].pcm.next=-1;
|
||||||
chan[c.chan].filter=DivInstrumentES5506::Filter();
|
chan[c.chan].filter=DivInstrumentES5506::Filter();
|
||||||
chan[c.chan].envelope=DivInstrumentES5506::Envelope();
|
chan[c.chan].envelope=DivInstrumentES5506::Envelope();
|
||||||
|
|
|
@ -200,7 +200,10 @@ int DivPlatformGA20::dispatch(DivCommand c) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -681,7 +681,10 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
if (c.chan>=5 && chan[c.chan].dacMode) {
|
if (c.chan>=5 && chan[c.chan].dacMode) {
|
||||||
//if (skipRegisterWrites) break;
|
//if (skipRegisterWrites) break;
|
||||||
if (ins->type==DIV_INS_AMIGA) { // Furnace mode
|
if (ins->type==DIV_INS_AMIGA) { // Furnace mode
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].dacSample=-1;
|
chan[c.chan].dacSample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002,0);
|
if (dumpWrites) addWrite(0xffff0002,0);
|
||||||
|
|
|
@ -274,7 +274,10 @@ int DivPlatformK007232::dispatch(DivCommand c) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:15;
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:15;
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,13 +261,14 @@ int DivPlatformLynx::dispatch(DivCommand c) {
|
||||||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
|
||||||
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
|
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
|
||||||
if (chan[c.chan].pcm) {
|
if (chan[c.chan].pcm) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
chan[c.chan].sampleBaseFreq=NOTE_FREQUENCY(c.value);
|
chan[c.chan].sampleBaseFreq=NOTE_FREQUENCY(c.value);
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
|
||||||
chan[c.chan].sampleAccum=0;
|
chan[c.chan].sampleAccum=0;
|
||||||
chan[c.chan].samplePos=0;
|
chan[c.chan].samplePos=0;
|
||||||
}
|
}
|
||||||
|
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
chan[c.chan].actualNote=c.value;
|
chan[c.chan].actualNote=c.value;
|
||||||
|
|
|
@ -176,7 +176,10 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
|
||||||
if (c.chan==2) { // PCM
|
if (c.chan==2) { // PCM
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
|
||||||
if (ins->type==DIV_INS_AMIGA) {
|
if (ins->type==DIV_INS_AMIGA) {
|
||||||
if (c.value!=DIV_NOTE_NULL) dacSample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
dacSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002,0);
|
if (dumpWrites) addWrite(0xffff0002,0);
|
||||||
|
|
|
@ -361,7 +361,10 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
||||||
if (c.chan==4) { // PCM
|
if (c.chan==4) { // PCM
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
|
||||||
if (ins->type==DIV_INS_AMIGA) {
|
if (ins->type==DIV_INS_AMIGA) {
|
||||||
if (c.value!=DIV_NOTE_NULL) dacSample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
dacSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0);
|
if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0);
|
||||||
|
|
|
@ -856,7 +856,10 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
immWrite(18,chan[c.chan].outVol);
|
immWrite(18,chan[c.chan].outVol);
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
immWrite(8,0);
|
immWrite(8,0);
|
||||||
|
|
|
@ -282,7 +282,10 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
||||||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||||
chan[c.chan].furnaceDac=true;
|
chan[c.chan].furnaceDac=true;
|
||||||
if (skipRegisterWrites) break;
|
if (skipRegisterWrites) break;
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].dacSample=-1;
|
chan[c.chan].dacSample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||||
|
|
|
@ -271,7 +271,10 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[0].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[0].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
chan[0].useWave=false;
|
chan[0].useWave=false;
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
|
|
@ -450,7 +450,10 @@ int DivPlatformQSound::dispatch(DivCommand c) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
chan[c.chan].isNewQSound=(ins->type==DIV_INS_QSOUND);
|
chan[c.chan].isNewQSound=(ins->type==DIV_INS_QSOUND);
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value);
|
chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,7 +182,10 @@ int DivPlatformRF5C68::dispatch(DivCommand c) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,7 +186,10 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
||||||
if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SEGAPCM) {
|
if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SEGAPCM) {
|
||||||
chan[c.chan].macroVolMul=(ins->type==DIV_INS_AMIGA)?64:127;
|
chan[c.chan].macroVolMul=(ins->type==DIV_INS_AMIGA)?64:127;
|
||||||
chan[c.chan].isNewSegaPCM=(ins->type==DIV_INS_SEGAPCM);
|
chan[c.chan].isNewSegaPCM=(ins->type==DIV_INS_SEGAPCM);
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].pcm.sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].pcm.sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
|
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].pcm.sample=-1;
|
chan[c.chan].pcm.sample=-1;
|
||||||
rWrite(0x86+(c.chan<<3),3);
|
rWrite(0x86+(c.chan<<3),3);
|
||||||
|
@ -480,7 +483,11 @@ void DivPlatformSegaPCM::renderSamples(int sysID) {
|
||||||
if (memPos>=16777216) break;
|
if (memPos>=16777216) break;
|
||||||
sampleOffSegaPCM[i]=memPos;
|
sampleOffSegaPCM[i]=memPos;
|
||||||
for (unsigned int j=0; j<alignedSize; j++) {
|
for (unsigned int j=0; j<alignedSize; j++) {
|
||||||
sampleMem[memPos++]=((unsigned char)sample->data8[j]+0x80);
|
if (j>=sample->samples) {
|
||||||
|
sampleMem[memPos++]=0;
|
||||||
|
} else {
|
||||||
|
sampleMem[memPos++]=((unsigned char)sample->data8[j]+0x80);
|
||||||
|
}
|
||||||
sampleEndSegaPCM[i]=((memPos+0xff)>>8)-1;
|
sampleEndSegaPCM[i]=((memPos+0xff)>>8)-1;
|
||||||
if (memPos>=16777216) break;
|
if (memPos>=16777216) break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -336,7 +336,10 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
chan[c.chan].ws.init(ins,chan[c.chan].wtLen,15,chan[c.chan].insChanged);
|
chan[c.chan].ws.init(ins,chan[c.chan].wtLen,15,chan[c.chan].insChanged);
|
||||||
} else {
|
} else {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
chan[c.chan].useWave=false;
|
chan[c.chan].useWave=false;
|
||||||
}
|
}
|
||||||
if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
||||||
|
|
|
@ -261,7 +261,10 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
||||||
dacPos=0;
|
dacPos=0;
|
||||||
dacPeriod=0;
|
dacPeriod=0;
|
||||||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||||
if (c.value!=DIV_NOTE_NULL) dacSample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
dacSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
if (dumpWrites) postWrite(0xffff0002,0);
|
if (dumpWrites) postWrite(0xffff0002,0);
|
||||||
|
|
|
@ -235,7 +235,11 @@ int DivPlatformVERA::dispatch(DivCommand c) {
|
||||||
if (c.chan<16) {
|
if (c.chan<16) {
|
||||||
rWriteLo(c.chan,2,chan[c.chan].vol);
|
rWriteLo(c.chan,2,chan[c.chan].vol);
|
||||||
} else {
|
} else {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[16].pcm.sample=parent->getIns(chan[16].ins,DIV_INS_VERA)->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
DivInstrument* ins=parent->getIns(chan[16].ins,DIV_INS_VERA);
|
||||||
|
chan[16].pcm.sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) {
|
if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) {
|
||||||
chan[16].pcm.sample=-1;
|
chan[16].pcm.sample=-1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,8 +179,6 @@ void DivPlatformVRC6::tick(bool sysTick) {
|
||||||
if (chan[i].std.phaseReset.val && chan[i].active) {
|
if (chan[i].std.phaseReset.val && chan[i].active) {
|
||||||
if ((i!=2) && (!chan[i].pcm)) {
|
if ((i!=2) && (!chan[i].pcm)) {
|
||||||
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
||||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_VRC6);
|
|
||||||
chan[i].dacSample=ins->amiga.getSample(chan[i].note);
|
|
||||||
if (chan[i].dacSample<0 || chan[i].dacSample>=parent->song.sampleLen) {
|
if (chan[i].dacSample<0 || chan[i].dacSample>=parent->song.sampleLen) {
|
||||||
if (dumpWrites) {
|
if (dumpWrites) {
|
||||||
chWrite(i,2,0x80);
|
chWrite(i,2,0x80);
|
||||||
|
@ -242,7 +240,10 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
|
||||||
if (chan[c.chan].pcm) {
|
if (chan[c.chan].pcm) {
|
||||||
if (skipRegisterWrites) break;
|
if (skipRegisterWrites) break;
|
||||||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].dacSample=-1;
|
chan[c.chan].dacSample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||||
|
|
|
@ -540,7 +540,10 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
|
||||||
if (chan[c.chan].furnacePCM) {
|
if (chan[c.chan].furnacePCM) {
|
||||||
chan[c.chan].pcm=true;
|
chan[c.chan].pcm=true;
|
||||||
chan[c.chan].macroInit(ins);
|
chan[c.chan].macroInit(ins);
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
if (isBanked) {
|
if (isBanked) {
|
||||||
|
|
|
@ -896,7 +896,10 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
immWrite(0x10b,chan[c.chan].outVol);
|
immWrite(0x10b,chan[c.chan].outVol);
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
immWrite(0x100,0x01); // reset
|
immWrite(0x100,0x01); // reset
|
||||||
|
|
|
@ -827,7 +827,10 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
immWrite(0x1b,chan[c.chan].outVol);
|
immWrite(0x1b,chan[c.chan].outVol);
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
immWrite(0x12,(sampleOffB[chan[c.chan].sample]>>8)&0xff);
|
immWrite(0x12,(sampleOffB[chan[c.chan].sample]>>8)&0xff);
|
||||||
|
|
|
@ -894,7 +894,10 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
immWrite(0x1b,chan[c.chan].outVol);
|
immWrite(0x1b,chan[c.chan].outVol);
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
immWrite(0x12,(sampleOffB[chan[c.chan].sample]>>8)&0xff);
|
immWrite(0x12,(sampleOffB[chan[c.chan].sample]>>8)&0xff);
|
||||||
|
|
|
@ -212,7 +212,10 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
chan[c.chan].isNewYMZ=ins->type==DIV_INS_YMZ280B;
|
chan[c.chan].isNewYMZ=ins->type==DIV_INS_YMZ280B;
|
||||||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,10 +169,11 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) {
|
||||||
} else {
|
} else {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
curSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
curSample=ins->amiga.getSample(c.value);
|
|
||||||
// TODO support offset commands
|
// TODO support offset commands
|
||||||
curSamplePos=0;
|
curSamplePos=0;
|
||||||
curSamplePeriod=0;
|
curSamplePeriod=0;
|
||||||
|
|
|
@ -1412,6 +1412,15 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
||||||
ret=true;
|
ret=true;
|
||||||
shallStop=false;
|
shallStop=false;
|
||||||
shallStopSched=false;
|
shallStopSched=false;
|
||||||
|
// reset all chan oscs
|
||||||
|
for (int i=0; i<chans; i++) {
|
||||||
|
DivDispatchOscBuffer* buf=disCont[dispatchOfChan[i]].dispatch->getOscBuffer(dispatchChanOfChan[i]);
|
||||||
|
if (buf!=NULL) {
|
||||||
|
memset(buf->data,0,65536*sizeof(short));
|
||||||
|
buf->needle=0;
|
||||||
|
buf->readNeedle=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -212,7 +212,6 @@ void DivWaveSynth::setWidth(int val) {
|
||||||
|
|
||||||
void DivWaveSynth::changeWave1(int num) {
|
void DivWaveSynth::changeWave1(int num) {
|
||||||
DivWavetable* w1=e->getWave(num);
|
DivWavetable* w1=e->getWave(num);
|
||||||
logV("changeWave1 (%d)",width);
|
|
||||||
if (width<1) return;
|
if (width<1) return;
|
||||||
for (int i=0; i<width; i++) {
|
for (int i=0; i<width; i++) {
|
||||||
if (w1->max<1 || w1->len<1) {
|
if (w1->max<1 || w1->len<1) {
|
||||||
|
|
|
@ -20,6 +20,13 @@
|
||||||
#include "fileutils.h"
|
#include "fileutils.h"
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "utfutils.h"
|
#include "utfutils.h"
|
||||||
|
#include <windows.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
#include <shlwapi.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FILE* ps_fopen(const char* path, const char* mode) {
|
FILE* ps_fopen(const char* path, const char* mode) {
|
||||||
|
@ -29,3 +36,67 @@ FILE* ps_fopen(const char* path, const char* mode) {
|
||||||
return fopen(path,mode);
|
return fopen(path,mode);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: copy in case of failure
|
||||||
|
bool moveFiles(const char* src, const char* dest) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return MoveFileW(utf8To16(src).c_str(),utf8To16(dest).c_str());
|
||||||
|
#else
|
||||||
|
if (rename(src,dest)==-1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deleteFile(const char* path) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return DeleteFileW(utf8To16(path).c_str());
|
||||||
|
#else
|
||||||
|
return (unlink(path)==0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileExists(const char* path) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (PathFileExistsW(utf8To16(path).c_str())) return 1;
|
||||||
|
// which errors could PathFileExists possibly throw?
|
||||||
|
switch (GetLastError()) {
|
||||||
|
case ERROR_FILE_EXISTS:
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
case ERROR_FILE_NOT_FOUND:
|
||||||
|
case ERROR_PATH_NOT_FOUND:
|
||||||
|
case ERROR_INVALID_DRIVE:
|
||||||
|
case ERROR_DEV_NOT_EXIST:
|
||||||
|
case ERROR_NETNAME_DELETED:
|
||||||
|
case ERROR_BAD_NET_NAME:
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
if (access(path,F_OK)==0) return 1;
|
||||||
|
if (errno==ENOENT) return 0;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dirExists(const char* what) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
WString ws=utf8To16(what);
|
||||||
|
return (PathIsDirectoryW(ws.c_str())!=FALSE);
|
||||||
|
#else
|
||||||
|
struct stat st;
|
||||||
|
if (stat(what,&st)<0) return false;
|
||||||
|
return (st.st_mode&S_IFDIR);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool makeDir(const char* path) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return (SHCreateDirectory(NULL,utf8To16(path).c_str())==ERROR_SUCCESS);
|
||||||
|
#else
|
||||||
|
return (mkdir(path,0755)==0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
@ -22,5 +22,11 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
FILE* ps_fopen(const char* path, const char* mode);
|
FILE* ps_fopen(const char* path, const char* mode);
|
||||||
|
bool moveFiles(const char* src, const char* dest);
|
||||||
|
bool deleteFile(const char* path);
|
||||||
|
// returns 1 if file exists, 0 if it doesn't and -1 on error.
|
||||||
|
int fileExists(const char* path);
|
||||||
|
bool dirExists(const char* what);
|
||||||
|
bool makeDir(const char* path);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -50,9 +50,7 @@ void FurnaceGUI::doAction(int what) {
|
||||||
if (modified) {
|
if (modified) {
|
||||||
showWarning("Unsaved changes! Save changes before opening backup?",GUI_WARN_OPEN_BACKUP);
|
showWarning("Unsaved changes! Save changes before opening backup?",GUI_WARN_OPEN_BACKUP);
|
||||||
} else {
|
} else {
|
||||||
if (load(backupPath)>0) {
|
openFileDialog(GUI_FILE_OPEN_BACKUP);
|
||||||
showError("No backup available! (or unable to open it)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GUI_ACTION_SAVE:
|
case GUI_ACTION_SAVE:
|
||||||
|
|
|
@ -524,6 +524,11 @@ void FurnaceGUI::drawMobileControls() {
|
||||||
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
|
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Restore Backup")) {
|
||||||
|
mobileMenuOpen=false;
|
||||||
|
doAction(GUI_ACTION_OPEN_BACKUP);
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
if (ImGui::BeginTabBar("MobileSong")) {
|
if (ImGui::BeginTabBar("MobileSong")) {
|
||||||
|
|
214
src/gui/gui.cpp
214
src/gui/gui.cpp
|
@ -50,13 +50,15 @@
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
#include "../utfutils.h"
|
#include "../utfutils.h"
|
||||||
#define LAYOUT_INI "\\layout.ini"
|
#define LAYOUT_INI "\\layout.ini"
|
||||||
#define BACKUP_FUR "\\backup.fur"
|
#define BACKUPS_DIR "\\backups"
|
||||||
#else
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#define LAYOUT_INI "/layout.ini"
|
#define LAYOUT_INI "/layout.ini"
|
||||||
#define BACKUP_FUR "/backup.fur"
|
#define BACKUPS_DIR "/backups"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef IS_MOBILE
|
#ifdef IS_MOBILE
|
||||||
|
@ -528,18 +530,22 @@ void FurnaceGUI::setFileName(String name) {
|
||||||
if (index>=4095) break;
|
if (index>=4095) break;
|
||||||
}
|
}
|
||||||
ret[index]=0;
|
ret[index]=0;
|
||||||
|
backupLock.lock();
|
||||||
if (GetFullPathNameW(ws.c_str(),4095,ret,NULL)==0) {
|
if (GetFullPathNameW(ws.c_str(),4095,ret,NULL)==0) {
|
||||||
curFileName=name;
|
curFileName=name;
|
||||||
} else {
|
} else {
|
||||||
curFileName=utf16To8(ret);
|
curFileName=utf16To8(ret);
|
||||||
}
|
}
|
||||||
|
backupLock.unlock();
|
||||||
#else
|
#else
|
||||||
char ret[4096];
|
char ret[4096];
|
||||||
|
backupLock.lock();
|
||||||
if (realpath(name.c_str(),ret)==NULL) {
|
if (realpath(name.c_str(),ret)==NULL) {
|
||||||
curFileName=name;
|
curFileName=name;
|
||||||
} else {
|
} else {
|
||||||
curFileName=ret;
|
curFileName=ret;
|
||||||
}
|
}
|
||||||
|
backupLock.unlock();
|
||||||
#endif
|
#endif
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
pushRecentFile(curFileName);
|
pushRecentFile(curFileName);
|
||||||
|
@ -1076,10 +1082,6 @@ void FurnaceGUI::stop() {
|
||||||
e->stop();
|
e->stop();
|
||||||
curNibble=false;
|
curNibble=false;
|
||||||
orderNibble=false;
|
orderNibble=false;
|
||||||
activeNotes.clear();
|
|
||||||
memset(chanOscVol,0,DIV_MAX_CHANS*sizeof(float));
|
|
||||||
memset(chanOscPitch,0,DIV_MAX_CHANS*sizeof(float));
|
|
||||||
memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) {
|
void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) {
|
||||||
|
@ -1458,15 +1460,8 @@ void FurnaceGUI::keyUp(SDL_Event& ev) {
|
||||||
// nothing for now
|
// nothing for now
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dirExists(String what) {
|
bool dirExists(String s) {
|
||||||
#ifdef _WIN32
|
return dirExists(s.c_str());
|
||||||
WString ws=utf8To16(what.c_str());
|
|
||||||
return (PathIsDirectoryW(ws.c_str())!=FALSE);
|
|
||||||
#else
|
|
||||||
struct stat st;
|
|
||||||
if (stat(what.c_str(),&st)<0) return false;
|
|
||||||
return (st.st_mode&S_IFDIR);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
||||||
|
@ -1483,6 +1478,19 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
||||||
dpiScale
|
dpiScale
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case GUI_FILE_OPEN_BACKUP:
|
||||||
|
if (!dirExists(backupPath)) {
|
||||||
|
showError("no backups made yet!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hasOpened=fileDialog->openLoad(
|
||||||
|
"Restore Backup",
|
||||||
|
{"Furnace song", "*.fur"},
|
||||||
|
"Furnace song{.fur}",
|
||||||
|
backupPath+String(DIR_SEPARATOR_STR),
|
||||||
|
dpiScale
|
||||||
|
);
|
||||||
|
break;
|
||||||
case GUI_FILE_SAVE:
|
case GUI_FILE_SAVE:
|
||||||
if (!dirExists(workingDirSong)) workingDirSong=getHomeDir();
|
if (!dirExists(workingDirSong)) workingDirSong=getHomeDir();
|
||||||
hasOpened=fileDialog->openSave(
|
hasOpened=fileDialog->openSave(
|
||||||
|
@ -2009,7 +2017,9 @@ int FurnaceGUI::save(String path, int dmfVersion) {
|
||||||
#endif
|
#endif
|
||||||
fclose(outFile);
|
fclose(outFile);
|
||||||
w->finish();
|
w->finish();
|
||||||
|
backupLock.lock();
|
||||||
curFileName=path;
|
curFileName=path;
|
||||||
|
backupLock.unlock();
|
||||||
modified=false;
|
modified=false;
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
if (!e->getWarnings().empty()) {
|
if (!e->getWarnings().empty()) {
|
||||||
|
@ -2074,7 +2084,9 @@ int FurnaceGUI::load(String path) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
backupLock.lock();
|
||||||
curFileName=path;
|
curFileName=path;
|
||||||
|
backupLock.unlock();
|
||||||
modified=false;
|
modified=false;
|
||||||
curNibble=false;
|
curNibble=false;
|
||||||
orderNibble=false;
|
orderNibble=false;
|
||||||
|
@ -2100,7 +2112,7 @@ int FurnaceGUI::load(String path) {
|
||||||
|
|
||||||
void FurnaceGUI::pushRecentFile(String path) {
|
void FurnaceGUI::pushRecentFile(String path) {
|
||||||
if (path.empty()) return;
|
if (path.empty()) return;
|
||||||
if (path==backupPath) return;
|
if (path.find(backupPath)==0) return;
|
||||||
for (int i=0; i<(int)recentFile.size(); i++) {
|
for (int i=0; i<(int)recentFile.size(); i++) {
|
||||||
if (recentFile[i]==path) {
|
if (recentFile[i]==path) {
|
||||||
recentFile.erase(recentFile.begin()+i);
|
recentFile.erase(recentFile.begin()+i);
|
||||||
|
@ -2114,6 +2126,44 @@ void FurnaceGUI::pushRecentFile(String path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FurnaceGUI::delFirstBackup(String name) {
|
||||||
|
std::vector<String> listOfFiles;
|
||||||
|
#ifdef _WIN32
|
||||||
|
String findPath=backupPath+String(DIR_SEPARATOR_STR)+name+String("*.fur");
|
||||||
|
WIN32_FIND_DATAW next;
|
||||||
|
HANDLE backDir=FindFirstFileW(utf8To16(findPath.c_str()).c_str(),&next);
|
||||||
|
if (backDir!=INVALID_HANDLE_VALUE) {
|
||||||
|
do {
|
||||||
|
listOfFiles.push_back(utf16To8(next.cFileName));
|
||||||
|
} while (FindNextFileW(backDir,&next)!=0);
|
||||||
|
FindClose(backDir);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
DIR* backDir=opendir(backupPath.c_str());
|
||||||
|
if (backDir==NULL) {
|
||||||
|
logW("could not open backups dir!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
struct dirent* next=readdir(backDir);
|
||||||
|
if (next==NULL) break;
|
||||||
|
if (strstr(next->d_name,name.c_str())!=next->d_name) continue;
|
||||||
|
listOfFiles.push_back(String(next->d_name));
|
||||||
|
}
|
||||||
|
closedir(backDir);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::sort(listOfFiles.begin(),listOfFiles.end(),[](const String& a, const String& b) -> bool {
|
||||||
|
return strcmp(a.c_str(),b.c_str())<0;
|
||||||
|
});
|
||||||
|
|
||||||
|
int totalDelete=((int)listOfFiles.size())-5;
|
||||||
|
for (int i=0; i<totalDelete; i++) {
|
||||||
|
String toDelete=backupPath+String(DIR_SEPARATOR_STR)+listOfFiles[i];
|
||||||
|
deleteFile(toDelete.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int FurnaceGUI::loadStream(String path) {
|
int FurnaceGUI::loadStream(String path) {
|
||||||
if (!path.empty()) {
|
if (!path.empty()) {
|
||||||
logI("loading stream...");
|
logI("loading stream...");
|
||||||
|
@ -2786,7 +2836,9 @@ void FurnaceGUI::editOptions(bool topMenu) {
|
||||||
void FurnaceGUI::toggleMobileUI(bool enable, bool force) {
|
void FurnaceGUI::toggleMobileUI(bool enable, bool force) {
|
||||||
if (mobileUI!=enable || force) {
|
if (mobileUI!=enable || force) {
|
||||||
if (!mobileUI && enable) {
|
if (!mobileUI && enable) {
|
||||||
ImGui::SaveIniSettingsToDisk(finalLayoutPath);
|
if (!ImGui::SaveIniSettingsToDisk(finalLayoutPath,true)) {
|
||||||
|
reportError(fmt::sprintf("could NOT save layout! %s",strerror(errno)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mobileUI=enable;
|
mobileUI=enable;
|
||||||
if (mobileUI) {
|
if (mobileUI) {
|
||||||
|
@ -2796,7 +2848,10 @@ void FurnaceGUI::toggleMobileUI(bool enable, bool force) {
|
||||||
fileDialog->mobileUI=true;
|
fileDialog->mobileUI=true;
|
||||||
} else {
|
} else {
|
||||||
ImGui::GetIO().IniFilename=NULL;
|
ImGui::GetIO().IniFilename=NULL;
|
||||||
ImGui::LoadIniSettingsFromDisk(finalLayoutPath);
|
if (!ImGui::LoadIniSettingsFromDisk(finalLayoutPath,true)) {
|
||||||
|
reportError(fmt::sprintf("could NOT load layout! %s",strerror(errno)));
|
||||||
|
ImGui::LoadIniSettingsFromMemory(defaultLayout);
|
||||||
|
}
|
||||||
ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_InertialScrollEnable;
|
ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_InertialScrollEnable;
|
||||||
ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NoHoverColors;
|
ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NoHoverColors;
|
||||||
fileDialog->mobileUI=false;
|
fileDialog->mobileUI=false;
|
||||||
|
@ -3568,6 +3623,23 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!e->isRunning()) {
|
||||||
|
activeNotes.clear();
|
||||||
|
memset(chanOscVol,0,DIV_MAX_CHANS*sizeof(float));
|
||||||
|
memset(chanOscPitch,0,DIV_MAX_CHANS*sizeof(float));
|
||||||
|
memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float));
|
||||||
|
|
||||||
|
e->synchronized([this]() {
|
||||||
|
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||||
|
DivDispatchOscBuffer* buf=e->getOscBuffer(i);
|
||||||
|
if (buf!=NULL) {
|
||||||
|
buf->needle=0;
|
||||||
|
buf->readNeedle=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
layoutTimeBegin=SDL_GetPerformanceCounter();
|
layoutTimeBegin=SDL_GetPerformanceCounter();
|
||||||
|
|
||||||
ImGui_ImplSDLRenderer_NewFrame();
|
ImGui_ImplSDLRenderer_NewFrame();
|
||||||
|
@ -3623,7 +3695,7 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (ImGui::MenuItem("save",BIND_FOR(GUI_ACTION_SAVE))) {
|
if (ImGui::MenuItem("save",BIND_FOR(GUI_ACTION_SAVE))) {
|
||||||
if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) {
|
if (curFileName=="" || (curFileName.find(backupPath)==0) || e->song.version>=0xff00) {
|
||||||
openFileDialog(GUI_FILE_SAVE);
|
openFileDialog(GUI_FILE_SAVE);
|
||||||
} else {
|
} else {
|
||||||
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
|
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
|
||||||
|
@ -4318,6 +4390,8 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_FILE_TEST_SAVE:
|
case GUI_FILE_TEST_SAVE:
|
||||||
workingDirTest=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
workingDirTest=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
||||||
break;
|
break;
|
||||||
|
case GUI_FILE_OPEN_BACKUP:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (fileDialog->isError()) {
|
if (fileDialog->isError()) {
|
||||||
#if defined(_WIN32) || defined(__APPLE__)
|
#if defined(_WIN32) || defined(__APPLE__)
|
||||||
|
@ -4394,6 +4468,7 @@ bool FurnaceGUI::loop() {
|
||||||
String copyOfName=fileName;
|
String copyOfName=fileName;
|
||||||
switch (curFileDialog) {
|
switch (curFileDialog) {
|
||||||
case GUI_FILE_OPEN:
|
case GUI_FILE_OPEN:
|
||||||
|
case GUI_FILE_OPEN_BACKUP:
|
||||||
if (load(copyOfName)>0) {
|
if (load(copyOfName)>0) {
|
||||||
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
|
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
|
||||||
}
|
}
|
||||||
|
@ -4422,9 +4497,7 @@ bool FurnaceGUI::loop() {
|
||||||
nextFile="";
|
nextFile="";
|
||||||
break;
|
break;
|
||||||
case GUI_WARN_OPEN_BACKUP:
|
case GUI_WARN_OPEN_BACKUP:
|
||||||
if (load(backupPath)>0) {
|
openFileDialog(GUI_FILE_OPEN_BACKUP);
|
||||||
showError("No backup available! (or unable to open it)");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -4891,7 +4964,7 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_WARN_QUIT:
|
case GUI_WARN_QUIT:
|
||||||
if (ImGui::Button("Yes")) {
|
if (ImGui::Button("Yes")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) {
|
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
|
||||||
openFileDialog(GUI_FILE_SAVE);
|
openFileDialog(GUI_FILE_SAVE);
|
||||||
postWarnAction=GUI_WARN_QUIT;
|
postWarnAction=GUI_WARN_QUIT;
|
||||||
} else {
|
} else {
|
||||||
|
@ -4915,7 +4988,7 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_WARN_NEW:
|
case GUI_WARN_NEW:
|
||||||
if (ImGui::Button("Yes")) {
|
if (ImGui::Button("Yes")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) {
|
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
|
||||||
openFileDialog(GUI_FILE_SAVE);
|
openFileDialog(GUI_FILE_SAVE);
|
||||||
postWarnAction=GUI_WARN_NEW;
|
postWarnAction=GUI_WARN_NEW;
|
||||||
} else {
|
} else {
|
||||||
|
@ -4939,7 +5012,7 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_WARN_OPEN:
|
case GUI_WARN_OPEN:
|
||||||
if (ImGui::Button("Yes")) {
|
if (ImGui::Button("Yes")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) {
|
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
|
||||||
openFileDialog(GUI_FILE_SAVE);
|
openFileDialog(GUI_FILE_SAVE);
|
||||||
postWarnAction=GUI_WARN_OPEN;
|
postWarnAction=GUI_WARN_OPEN;
|
||||||
} else {
|
} else {
|
||||||
|
@ -4963,25 +5036,21 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_WARN_OPEN_BACKUP:
|
case GUI_WARN_OPEN_BACKUP:
|
||||||
if (ImGui::Button("Yes")) {
|
if (ImGui::Button("Yes")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) {
|
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
|
||||||
openFileDialog(GUI_FILE_SAVE);
|
openFileDialog(GUI_FILE_SAVE);
|
||||||
postWarnAction=GUI_WARN_OPEN_BACKUP;
|
postWarnAction=GUI_WARN_OPEN_BACKUP;
|
||||||
} else {
|
} else {
|
||||||
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
|
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
|
||||||
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
|
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
|
||||||
} else {
|
} else {
|
||||||
if (load(backupPath)>0) {
|
openFileDialog(GUI_FILE_OPEN_BACKUP);
|
||||||
showError("No backup available! (or unable to open it)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("No")) {
|
if (ImGui::Button("No")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (load(backupPath)>0) {
|
openFileDialog(GUI_FILE_OPEN_BACKUP);
|
||||||
showError("No backup available! (or unable to open it)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Cancel")) {
|
if (ImGui::Button("Cancel")) {
|
||||||
|
@ -4991,7 +5060,7 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_WARN_OPEN_DROP:
|
case GUI_WARN_OPEN_DROP:
|
||||||
if (ImGui::Button("Yes")) {
|
if (ImGui::Button("Yes")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) {
|
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
|
||||||
openFileDialog(GUI_FILE_SAVE);
|
openFileDialog(GUI_FILE_SAVE);
|
||||||
postWarnAction=GUI_WARN_OPEN_DROP;
|
postWarnAction=GUI_WARN_OPEN_DROP;
|
||||||
} else {
|
} else {
|
||||||
|
@ -5025,7 +5094,9 @@ bool FurnaceGUI::loop() {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (!mobileUI) {
|
if (!mobileUI) {
|
||||||
ImGui::LoadIniSettingsFromMemory(defaultLayout);
|
ImGui::LoadIniSettingsFromMemory(defaultLayout);
|
||||||
ImGui::SaveIniSettingsToDisk(finalLayoutPath);
|
if (!ImGui::SaveIniSettingsToDisk(finalLayoutPath,true)) {
|
||||||
|
reportError(fmt::sprintf("could NOT save layout! %s",strerror(errno)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
@ -5401,16 +5472,73 @@ bool FurnaceGUI::loop() {
|
||||||
backupTimer=(backupTimer-ImGui::GetIO().DeltaTime);
|
backupTimer=(backupTimer-ImGui::GetIO().DeltaTime);
|
||||||
if (backupTimer<=0) {
|
if (backupTimer<=0) {
|
||||||
backupTask=std::async(std::launch::async,[this]() -> bool {
|
backupTask=std::async(std::launch::async,[this]() -> bool {
|
||||||
if (backupPath==curFileName) {
|
backupLock.lock();
|
||||||
|
logV("backupPath: %s",backupPath);
|
||||||
|
logV("curFileName: %s",curFileName);
|
||||||
|
if (curFileName.find(backupPath)==0) {
|
||||||
logD("backup file open. not saving backup.");
|
logD("backup file open. not saving backup.");
|
||||||
|
backupTimer=30.0;
|
||||||
|
backupLock.unlock();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!dirExists(backupPath.c_str())) {
|
||||||
|
if (!makeDir(backupPath.c_str())) {
|
||||||
|
logW("could not create backup directory!");
|
||||||
|
backupTimer=30.0;
|
||||||
|
backupLock.unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
logD("saving backup...");
|
logD("saving backup...");
|
||||||
SafeWriter* w=e->saveFur(true);
|
SafeWriter* w=e->saveFur(true);
|
||||||
logV("writing file...");
|
logV("writing file...");
|
||||||
|
|
||||||
if (w!=NULL) {
|
if (w!=NULL) {
|
||||||
FILE* outFile=ps_fopen(backupPath.c_str(),"wb");
|
size_t sepPos=curFileName.rfind(DIR_SEPARATOR);
|
||||||
|
String backupPreBaseName;
|
||||||
|
String backupBaseName;
|
||||||
|
String backupFileName;
|
||||||
|
if (sepPos==String::npos) {
|
||||||
|
backupPreBaseName=curFileName;
|
||||||
|
} else {
|
||||||
|
backupPreBaseName=curFileName.substr(sepPos+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t dotPos=backupPreBaseName.rfind('.');
|
||||||
|
if (dotPos!=String::npos) {
|
||||||
|
backupPreBaseName=backupPreBaseName.substr(0,dotPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (char i: backupPreBaseName) {
|
||||||
|
if (backupBaseName.size()>=48) break;
|
||||||
|
if ((i>='0' && i<='9') || (i>='A' && i<='Z') || (i>='a' && i<='z') || i=='_' || i=='-' || i==' ') backupBaseName+=i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backupBaseName.empty()) backupBaseName="untitled";
|
||||||
|
|
||||||
|
backupFileName=backupBaseName;
|
||||||
|
|
||||||
|
time_t curTime=time(NULL);
|
||||||
|
struct tm curTM;
|
||||||
|
#ifdef _WIN32
|
||||||
|
struct tm* tempTM=localtime(&curTime);
|
||||||
|
if (tempTM==NULL) {
|
||||||
|
backupFileName+="-unknownTime.fur";
|
||||||
|
} else {
|
||||||
|
curTM=*tempTM;
|
||||||
|
backupFileName+=fmt::sprintf("-%d%.2d%.2d-%.2d%.2d%.2d.fur",curTM.tm_year+1900,curTM.tm_mon+1,curTM.tm_mday,curTM.tm_hour,curTM.tm_min,curTM.tm_sec);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (localtime_r(&curTime,&curTM)==NULL) {
|
||||||
|
backupFileName+="-unknownTime.fur";
|
||||||
|
} else {
|
||||||
|
backupFileName+=fmt::sprintf("-%d%.2d%.2d-%.2d%.2d%.2d.fur",curTM.tm_year+1900,curTM.tm_mon+1,curTM.tm_mday,curTM.tm_hour,curTM.tm_min,curTM.tm_sec);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
String finalPath=backupPath+String(DIR_SEPARATOR_STR)+backupFileName;
|
||||||
|
|
||||||
|
FILE* outFile=ps_fopen(finalPath.c_str(),"wb");
|
||||||
if (outFile!=NULL) {
|
if (outFile!=NULL) {
|
||||||
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
|
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
|
||||||
logW("did not write backup entirely: %s!",strerror(errno));
|
logW("did not write backup entirely: %s!",strerror(errno));
|
||||||
|
@ -5421,9 +5549,13 @@ bool FurnaceGUI::loop() {
|
||||||
logW("could not save backup: %s!",strerror(errno));
|
logW("could not save backup: %s!",strerror(errno));
|
||||||
w->finish();
|
w->finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete previous backup if there are too many
|
||||||
|
delFirstBackup(backupBaseName);
|
||||||
}
|
}
|
||||||
logD("backup saved.");
|
logD("backup saved.");
|
||||||
backupTimer=30.0;
|
backupTimer=30.0;
|
||||||
|
backupLock.unlock();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5837,7 +5969,11 @@ bool FurnaceGUI::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(finalLayoutPath,(e->getConfigPath()+String(LAYOUT_INI)).c_str(),4095);
|
strncpy(finalLayoutPath,(e->getConfigPath()+String(LAYOUT_INI)).c_str(),4095);
|
||||||
backupPath=e->getConfigPath()+String(BACKUP_FUR);
|
backupPath=e->getConfigPath();
|
||||||
|
if (backupPath.size()>0) {
|
||||||
|
if (backupPath[backupPath.size()-1]==DIR_SEPARATOR) backupPath.resize(backupPath.size()-1);
|
||||||
|
}
|
||||||
|
backupPath+=String(BACKUPS_DIR);
|
||||||
prepareLayout();
|
prepareLayout();
|
||||||
|
|
||||||
ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_DockingEnable;
|
ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_DockingEnable;
|
||||||
|
@ -5907,7 +6043,9 @@ bool FurnaceGUI::init() {
|
||||||
|
|
||||||
void FurnaceGUI::commitState() {
|
void FurnaceGUI::commitState() {
|
||||||
if (!mobileUI) {
|
if (!mobileUI) {
|
||||||
ImGui::SaveIniSettingsToDisk(finalLayoutPath);
|
if (!ImGui::SaveIniSettingsToDisk(finalLayoutPath,true)) {
|
||||||
|
reportError(fmt::sprintf("could NOT save layout! %s",strerror(errno)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e->setConf("configVersion",(int)DIV_ENGINE_VERSION);
|
e->setConf("configVersion",(int)DIV_ENGINE_VERSION);
|
||||||
|
|
|
@ -347,6 +347,7 @@ enum FurnaceGUIMobileScenes {
|
||||||
|
|
||||||
enum FurnaceGUIFileDialogs {
|
enum FurnaceGUIFileDialogs {
|
||||||
GUI_FILE_OPEN,
|
GUI_FILE_OPEN,
|
||||||
|
GUI_FILE_OPEN_BACKUP,
|
||||||
GUI_FILE_SAVE,
|
GUI_FILE_SAVE,
|
||||||
GUI_FILE_SAVE_DMF,
|
GUI_FILE_SAVE_DMF,
|
||||||
GUI_FILE_SAVE_DMF_LEGACY,
|
GUI_FILE_SAVE_DMF_LEGACY,
|
||||||
|
@ -2080,6 +2081,7 @@ class FurnaceGUI {
|
||||||
int loadStream(String path);
|
int loadStream(String path);
|
||||||
void pushRecentFile(String path);
|
void pushRecentFile(String path);
|
||||||
void exportAudio(String path, DivAudioExportModes mode);
|
void exportAudio(String path, DivAudioExportModes mode);
|
||||||
|
void delFirstBackup(String name);
|
||||||
|
|
||||||
bool parseSysEx(unsigned char* data, size_t len);
|
bool parseSysEx(unsigned char* data, size_t len);
|
||||||
|
|
||||||
|
|
|
@ -4406,11 +4406,10 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
ImGui::BeginDisabled(ins->amiga.useWave);
|
ImGui::BeginDisabled(ins->amiga.useWave);
|
||||||
P(ImGui::Checkbox("Use sample map",&ins->amiga.useNoteMap));
|
P(ImGui::Checkbox("Use sample map",&ins->amiga.useNoteMap));
|
||||||
if (ins->amiga.useNoteMap) {
|
if (ins->amiga.useNoteMap) {
|
||||||
// TODO: frequency map?
|
if (ImGui::BeginTable("NoteMap",3,ImGuiTableFlags_ScrollY|ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) {
|
||||||
if (ImGui::BeginTable("NoteMap",2/*3*/,ImGuiTableFlags_ScrollY|ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) {
|
|
||||||
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
|
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
|
||||||
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch);
|
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch);
|
||||||
//ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
|
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
|
||||||
|
|
||||||
ImGui::TableSetupScrollFreeze(0,1);
|
ImGui::TableSetupScrollFreeze(0,1);
|
||||||
|
|
||||||
|
@ -4418,8 +4417,8 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Sample");
|
ImGui::Text("Sample");
|
||||||
/*ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Frequency");*/
|
ImGui::Text("Note");
|
||||||
for (int i=0; i<120; i++) {
|
for (int i=0; i<120; i++) {
|
||||||
DivInstrumentAmiga::SampleMap& sampleMap=ins->amiga.noteMap[i];
|
DivInstrumentAmiga::SampleMap& sampleMap=ins->amiga.noteMap[i];
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
@ -4443,17 +4442,28 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
id=fmt::sprintf("%d: %s",j,e->song.sample[j]->name);
|
id=fmt::sprintf("%d: %s",j,e->song.sample[j]->name);
|
||||||
if (ImGui::Selectable(id.c_str(),sampleMap.map==j)) { PARAMETER
|
if (ImGui::Selectable(id.c_str(),sampleMap.map==j)) { PARAMETER
|
||||||
sampleMap.map=j;
|
sampleMap.map=j;
|
||||||
if (sampleMap.freq<=0) sampleMap.freq=(int)((double)e->song.sample[j]->centerRate*pow(2.0,((double)i-48.0)/12.0));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
/*ImGui::TableNextColumn();
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
if (ImGui::InputInt("##SF",&sampleMap.freq,50,500)) { PARAMETER
|
const char* nName="???";
|
||||||
if (sampleMap.freq<0) sampleMap.freq=0;
|
if ((sampleMap.freq+60)>0 && (sampleMap.freq+60)<180) {
|
||||||
if (sampleMap.freq>262144) sampleMap.freq=262144;
|
nName=noteNames[sampleMap.freq+60];
|
||||||
}*/
|
}
|
||||||
|
if (ImGui::BeginCombo("##SN",nName)) {
|
||||||
|
for (int j=0; j<180; j++) {
|
||||||
|
const char* nName2="???";
|
||||||
|
nName2=noteNames[j];
|
||||||
|
if (ImGui::Selectable(nName2,(sampleMap.freq+60)==j)) {
|
||||||
|
sampleMap.freq=j-60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
|
|
|
@ -1109,11 +1109,23 @@ void FurnaceGUI::initSystemPresets() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
ENTRY(
|
ENTRY(
|
||||||
"Commander X16", {
|
"Commander X16 (VERA only)", {
|
||||||
|
CH(DIV_SYSTEM_VERA, 1.0f, 0, "")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ENTRY(
|
||||||
|
"Commander X16 (with OPM)", {
|
||||||
CH(DIV_SYSTEM_VERA, 1.0f, 0, ""),
|
CH(DIV_SYSTEM_VERA, 1.0f, 0, ""),
|
||||||
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "")
|
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "")
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
ENTRY(
|
||||||
|
"Commander X16 (with Twin OPL3)", {
|
||||||
|
CH(DIV_SYSTEM_VERA, 1.0f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_OPL3, 1.0f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_OPL3, 1.0f, 0, "")
|
||||||
|
}
|
||||||
|
);
|
||||||
ENTRY(
|
ENTRY(
|
||||||
"TI-99/4A", {
|
"TI-99/4A", {
|
||||||
CH(DIV_SYSTEM_SMS, 1.0f, 0,
|
CH(DIV_SYSTEM_SMS, 1.0f, 0,
|
||||||
|
|
39
src/log.cpp
39
src/log.cpp
|
@ -18,13 +18,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ta-log.h"
|
#include "ta-log.h"
|
||||||
|
#include "fileutils.h"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef IS_MOBILE
|
#ifdef IS_MOBILE
|
||||||
int logLevel=LOGLEVEL_TRACE;
|
int logLevel=LOGLEVEL_TRACE;
|
||||||
#else
|
#else
|
||||||
int logLevel=LOGLEVEL_INFO;
|
int logLevel=LOGLEVEL_TRACE; // until done
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FILE* logFile;
|
FILE* logFile;
|
||||||
|
@ -133,7 +138,16 @@ int writeLog(int level, const char* msg, fmt::printf_args args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void initLog() {
|
void initLog() {
|
||||||
// initalize log buffer
|
// initialize coloring on Windows
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE winout=GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
int termprop=0;
|
||||||
|
GetConsoleMode(winout,(LPDWORD)&termprop);
|
||||||
|
termprop|=ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||||
|
SetConsoleMode(winout,termprop);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// initialize log buffer
|
||||||
logPosition=0;
|
logPosition=0;
|
||||||
for (int i=0; i<TA_LOG_SIZE; i++) {
|
for (int i=0; i<TA_LOG_SIZE; i++) {
|
||||||
logEntries[i].text.reserve(128);
|
logEntries[i].text.reserve(128);
|
||||||
|
@ -169,9 +183,28 @@ bool startLogFile(const char* path) {
|
||||||
if (logFileAvail) return true;
|
if (logFileAvail) return true;
|
||||||
|
|
||||||
// rotate log file if possible
|
// rotate log file if possible
|
||||||
|
char oldPath[4096];
|
||||||
|
char newPath[4096];
|
||||||
|
|
||||||
|
if (fileExists(path)==1) {
|
||||||
|
for (int i=4; i>=0; i--) {
|
||||||
|
if (i>0) {
|
||||||
|
snprintf(oldPath,4095,"%s.%d",path,i);
|
||||||
|
} else {
|
||||||
|
strncpy(oldPath,path,4095);
|
||||||
|
}
|
||||||
|
snprintf(newPath,4095,"%s.%d",path,i+1);
|
||||||
|
|
||||||
|
if (i>=4) {
|
||||||
|
deleteFile(oldPath);
|
||||||
|
} else {
|
||||||
|
moveFiles(oldPath,newPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// open log file
|
// open log file
|
||||||
if ((logFile=fopen(path,"w+"))==NULL) {
|
if ((logFile=ps_fopen(path,"w+"))==NULL) {
|
||||||
logFileAvail=false;
|
logFileAvail=false;
|
||||||
logW("could not open log file! (%s)",strerror(errno));
|
logW("could not open log file! (%s)",strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <combaseapi.h>
|
#include <combaseapi.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
|
|
@ -66,4 +66,6 @@ struct TAParam {
|
||||||
func(f) {}
|
func(f) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void reportError(String what);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue