Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt
# Conflicts: # src/gui/insEdit.cpp # src/gui/sampleEdit.cpp
This commit is contained in:
commit
97d2bddf1f
|
@ -49,12 +49,27 @@ the coding style is described here:
|
||||||
- `size_t` are 32-bit or 64-bit, depending on architecture.
|
- `size_t` are 32-bit or 64-bit, depending on architecture.
|
||||||
- in float/double operations, always use decimal and `f` if single-precision.
|
- in float/double operations, always use decimal and `f` if single-precision.
|
||||||
- e.g. `1.0f` or `1.0` instead of `1`.
|
- e.g. `1.0f` or `1.0` instead of `1`.
|
||||||
|
- prefer `NULL` over `nullptr` or any other proprietary null.
|
||||||
- don't use `auto` unless needed.
|
- don't use `auto` unless needed.
|
||||||
|
- use `String` for `std::string` (this is typedef'd in ta-utils.h).
|
||||||
|
- prefer using operator for String (std::string) comparisons (a=="").
|
||||||
|
|
||||||
some files (particularly the ones in `src/engine/platform/sound` and `extern/`) don't follow this style.
|
some files (particularly the ones in `src/engine/platform/sound` and `extern/`) don't follow this style.
|
||||||
|
|
||||||
you don't have to follow this style. I will fix it after I accept your contribution.
|
you don't have to follow this style. I will fix it after I accept your contribution.
|
||||||
|
|
||||||
|
additional guidelines:
|
||||||
|
|
||||||
|
- in general **strongly** avoid breaking compatibility.
|
||||||
|
- do not touch loadFur/saveFur unless you know what you're doing!
|
||||||
|
- new fields must be at the end of each block to ensure forward compatibility
|
||||||
|
- likewise, the instrument read/write functions in DivInstrument have to be handled carefully
|
||||||
|
- any change to the format requires a version bump (see `src/engine/engine.h`).
|
||||||
|
- do not bump the version number under any circumstances!
|
||||||
|
- if you are making major changes to the playback routine, make sure to test with older songs to ensure nothing breaks.
|
||||||
|
- I will run a test suite to make sure this is the case.
|
||||||
|
- if something breaks, you might want to add a compatibility flag (this requires changing the format though).
|
||||||
|
|
||||||
## Demo Songs
|
## Demo Songs
|
||||||
|
|
||||||
just put your demo song in `demos/`!
|
just put your demo song in `demos/`!
|
||||||
|
|
39
extern/igfd/ImGuiFileDialog.cpp
vendored
39
extern/igfd/ImGuiFileDialog.cpp
vendored
|
@ -3295,7 +3295,8 @@ namespace IGFD
|
||||||
const std::string& vFileName,
|
const std::string& vFileName,
|
||||||
const int& vCountSelectionMax,
|
const int& vCountSelectionMax,
|
||||||
UserDatas vUserDatas,
|
UserDatas vUserDatas,
|
||||||
ImGuiFileDialogFlags vFlags)
|
ImGuiFileDialogFlags vFlags,
|
||||||
|
SelectFun vSelectFun)
|
||||||
{
|
{
|
||||||
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
||||||
return;
|
return;
|
||||||
|
@ -3306,6 +3307,7 @@ namespace IGFD
|
||||||
prFileDialogInternal.puDLGtitle = vTitle;
|
prFileDialogInternal.puDLGtitle = vTitle;
|
||||||
prFileDialogInternal.puDLGuserDatas = vUserDatas;
|
prFileDialogInternal.puDLGuserDatas = vUserDatas;
|
||||||
prFileDialogInternal.puDLGflags = vFlags;
|
prFileDialogInternal.puDLGflags = vFlags;
|
||||||
|
prFileDialogInternal.puDLGselFun = vSelectFun;
|
||||||
prFileDialogInternal.puDLGoptionsPane = nullptr;
|
prFileDialogInternal.puDLGoptionsPane = nullptr;
|
||||||
prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f;
|
prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f;
|
||||||
prFileDialogInternal.puDLGmodal = false;
|
prFileDialogInternal.puDLGmodal = false;
|
||||||
|
@ -3335,7 +3337,8 @@ namespace IGFD
|
||||||
const std::string& vFilePathName,
|
const std::string& vFilePathName,
|
||||||
const int& vCountSelectionMax,
|
const int& vCountSelectionMax,
|
||||||
UserDatas vUserDatas,
|
UserDatas vUserDatas,
|
||||||
ImGuiFileDialogFlags vFlags)
|
ImGuiFileDialogFlags vFlags,
|
||||||
|
SelectFun vSelectFun)
|
||||||
{
|
{
|
||||||
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
||||||
return;
|
return;
|
||||||
|
@ -3348,6 +3351,7 @@ namespace IGFD
|
||||||
prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f;
|
prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f;
|
||||||
prFileDialogInternal.puDLGuserDatas = vUserDatas;
|
prFileDialogInternal.puDLGuserDatas = vUserDatas;
|
||||||
prFileDialogInternal.puDLGflags = vFlags;
|
prFileDialogInternal.puDLGflags = vFlags;
|
||||||
|
prFileDialogInternal.puDLGselFun = vSelectFun;
|
||||||
prFileDialogInternal.puDLGmodal = false;
|
prFileDialogInternal.puDLGmodal = false;
|
||||||
|
|
||||||
auto ps = IGFD::Utils::ParsePathFileName(vFilePathName);
|
auto ps = IGFD::Utils::ParsePathFileName(vFilePathName);
|
||||||
|
@ -3390,7 +3394,8 @@ namespace IGFD
|
||||||
const float& vSidePaneWidth,
|
const float& vSidePaneWidth,
|
||||||
const int& vCountSelectionMax,
|
const int& vCountSelectionMax,
|
||||||
UserDatas vUserDatas,
|
UserDatas vUserDatas,
|
||||||
ImGuiFileDialogFlags vFlags)
|
ImGuiFileDialogFlags vFlags,
|
||||||
|
SelectFun vSelectFun)
|
||||||
{
|
{
|
||||||
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
||||||
return;
|
return;
|
||||||
|
@ -3401,6 +3406,7 @@ namespace IGFD
|
||||||
prFileDialogInternal.puDLGtitle = vTitle;
|
prFileDialogInternal.puDLGtitle = vTitle;
|
||||||
prFileDialogInternal.puDLGuserDatas = vUserDatas;
|
prFileDialogInternal.puDLGuserDatas = vUserDatas;
|
||||||
prFileDialogInternal.puDLGflags = vFlags;
|
prFileDialogInternal.puDLGflags = vFlags;
|
||||||
|
prFileDialogInternal.puDLGselFun = vSelectFun;
|
||||||
prFileDialogInternal.puDLGoptionsPane = vSidePane;
|
prFileDialogInternal.puDLGoptionsPane = vSidePane;
|
||||||
prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth;
|
prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth;
|
||||||
prFileDialogInternal.puDLGmodal = false;
|
prFileDialogInternal.puDLGmodal = false;
|
||||||
|
@ -3435,7 +3441,8 @@ namespace IGFD
|
||||||
const float& vSidePaneWidth,
|
const float& vSidePaneWidth,
|
||||||
const int& vCountSelectionMax,
|
const int& vCountSelectionMax,
|
||||||
UserDatas vUserDatas,
|
UserDatas vUserDatas,
|
||||||
ImGuiFileDialogFlags vFlags)
|
ImGuiFileDialogFlags vFlags,
|
||||||
|
SelectFun vSelectFun)
|
||||||
{
|
{
|
||||||
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
||||||
return;
|
return;
|
||||||
|
@ -3448,6 +3455,7 @@ namespace IGFD
|
||||||
prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth;
|
prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth;
|
||||||
prFileDialogInternal.puDLGuserDatas = vUserDatas;
|
prFileDialogInternal.puDLGuserDatas = vUserDatas;
|
||||||
prFileDialogInternal.puDLGflags = vFlags;
|
prFileDialogInternal.puDLGflags = vFlags;
|
||||||
|
prFileDialogInternal.puDLGselFun = vSelectFun;
|
||||||
prFileDialogInternal.puDLGmodal = false;
|
prFileDialogInternal.puDLGmodal = false;
|
||||||
|
|
||||||
auto ps = IGFD::Utils::ParsePathFileName(vFilePathName);
|
auto ps = IGFD::Utils::ParsePathFileName(vFilePathName);
|
||||||
|
@ -3489,7 +3497,8 @@ namespace IGFD
|
||||||
const std::string& vFileName,
|
const std::string& vFileName,
|
||||||
const int& vCountSelectionMax,
|
const int& vCountSelectionMax,
|
||||||
UserDatas vUserDatas,
|
UserDatas vUserDatas,
|
||||||
ImGuiFileDialogFlags vFlags)
|
ImGuiFileDialogFlags vFlags,
|
||||||
|
SelectFun vSelectFun)
|
||||||
{
|
{
|
||||||
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
||||||
return;
|
return;
|
||||||
|
@ -3497,7 +3506,7 @@ namespace IGFD
|
||||||
OpenDialog(
|
OpenDialog(
|
||||||
vKey, vTitle, vFilters,
|
vKey, vTitle, vFilters,
|
||||||
vPath, vFileName,
|
vPath, vFileName,
|
||||||
vCountSelectionMax, vUserDatas, vFlags);
|
vCountSelectionMax, vUserDatas, vFlags, vSelectFun);
|
||||||
|
|
||||||
prFileDialogInternal.puDLGmodal = true;
|
prFileDialogInternal.puDLGmodal = true;
|
||||||
}
|
}
|
||||||
|
@ -3509,7 +3518,8 @@ namespace IGFD
|
||||||
const std::string& vFilePathName,
|
const std::string& vFilePathName,
|
||||||
const int& vCountSelectionMax,
|
const int& vCountSelectionMax,
|
||||||
UserDatas vUserDatas,
|
UserDatas vUserDatas,
|
||||||
ImGuiFileDialogFlags vFlags)
|
ImGuiFileDialogFlags vFlags,
|
||||||
|
SelectFun vSelectFun)
|
||||||
{
|
{
|
||||||
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
||||||
return;
|
return;
|
||||||
|
@ -3517,7 +3527,7 @@ namespace IGFD
|
||||||
OpenDialog(
|
OpenDialog(
|
||||||
vKey, vTitle, vFilters,
|
vKey, vTitle, vFilters,
|
||||||
vFilePathName,
|
vFilePathName,
|
||||||
vCountSelectionMax, vUserDatas, vFlags);
|
vCountSelectionMax, vUserDatas, vFlags, vSelectFun);
|
||||||
|
|
||||||
prFileDialogInternal.puDLGmodal = true;
|
prFileDialogInternal.puDLGmodal = true;
|
||||||
}
|
}
|
||||||
|
@ -3534,7 +3544,8 @@ namespace IGFD
|
||||||
const float& vSidePaneWidth,
|
const float& vSidePaneWidth,
|
||||||
const int& vCountSelectionMax,
|
const int& vCountSelectionMax,
|
||||||
UserDatas vUserDatas,
|
UserDatas vUserDatas,
|
||||||
ImGuiFileDialogFlags vFlags)
|
ImGuiFileDialogFlags vFlags,
|
||||||
|
SelectFun vSelectFun)
|
||||||
{
|
{
|
||||||
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
||||||
return;
|
return;
|
||||||
|
@ -3543,7 +3554,7 @@ namespace IGFD
|
||||||
vKey, vTitle, vFilters,
|
vKey, vTitle, vFilters,
|
||||||
vPath, vFileName,
|
vPath, vFileName,
|
||||||
vSidePane, vSidePaneWidth,
|
vSidePane, vSidePaneWidth,
|
||||||
vCountSelectionMax, vUserDatas, vFlags);
|
vCountSelectionMax, vUserDatas, vFlags, vSelectFun);
|
||||||
|
|
||||||
prFileDialogInternal.puDLGmodal = true;
|
prFileDialogInternal.puDLGmodal = true;
|
||||||
}
|
}
|
||||||
|
@ -3559,7 +3570,8 @@ namespace IGFD
|
||||||
const float& vSidePaneWidth,
|
const float& vSidePaneWidth,
|
||||||
const int& vCountSelectionMax,
|
const int& vCountSelectionMax,
|
||||||
UserDatas vUserDatas,
|
UserDatas vUserDatas,
|
||||||
ImGuiFileDialogFlags vFlags)
|
ImGuiFileDialogFlags vFlags,
|
||||||
|
SelectFun vSelectFun)
|
||||||
{
|
{
|
||||||
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
if (prFileDialogInternal.puShowDialog) // if already opened, quit
|
||||||
return;
|
return;
|
||||||
|
@ -3568,7 +3580,7 @@ namespace IGFD
|
||||||
vKey, vTitle, vFilters,
|
vKey, vTitle, vFilters,
|
||||||
vFilePathName,
|
vFilePathName,
|
||||||
vSidePane, vSidePaneWidth,
|
vSidePane, vSidePaneWidth,
|
||||||
vCountSelectionMax, vUserDatas, vFlags);
|
vCountSelectionMax, vUserDatas, vFlags, vSelectFun);
|
||||||
|
|
||||||
prFileDialogInternal.puDLGmodal = true;
|
prFileDialogInternal.puDLGmodal = true;
|
||||||
}
|
}
|
||||||
|
@ -3940,6 +3952,9 @@ namespace IGFD
|
||||||
return 2;
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
fdi.SelectFileName(prFileDialogInternal, vInfos);
|
fdi.SelectFileName(prFileDialogInternal, vInfos);
|
||||||
|
if (prFileDialogInternal.puDLGselFun!=NULL) {
|
||||||
|
prFileDialogInternal.puDLGselFun(GetFilePathName().c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
26
extern/igfd/ImGuiFileDialog.h
vendored
26
extern/igfd/ImGuiFileDialog.h
vendored
|
@ -1080,6 +1080,7 @@ namespace IGFD
|
||||||
|
|
||||||
typedef void* UserDatas;
|
typedef void* UserDatas;
|
||||||
typedef std::function<void(const char*, UserDatas, bool*)> PaneFun; // side pane function binding
|
typedef std::function<void(const char*, UserDatas, bool*)> PaneFun; // side pane function binding
|
||||||
|
typedef std::function<void(const char*)> SelectFun; // click on file function binding
|
||||||
class FileDialogInternal
|
class FileDialogInternal
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -1103,6 +1104,7 @@ namespace IGFD
|
||||||
ImGuiFileDialogFlags puDLGflags = ImGuiFileDialogFlags_None;
|
ImGuiFileDialogFlags puDLGflags = ImGuiFileDialogFlags_None;
|
||||||
UserDatas puDLGuserDatas = nullptr;
|
UserDatas puDLGuserDatas = nullptr;
|
||||||
PaneFun puDLGoptionsPane = nullptr;
|
PaneFun puDLGoptionsPane = nullptr;
|
||||||
|
SelectFun puDLGselFun = nullptr;
|
||||||
float puDLGoptionsPaneWidth = 0.0f;
|
float puDLGoptionsPaneWidth = 0.0f;
|
||||||
bool puDLGmodal = false;
|
bool puDLGmodal = false;
|
||||||
bool puNeedToExitDialog = false;
|
bool puNeedToExitDialog = false;
|
||||||
|
@ -1155,7 +1157,8 @@ namespace IGFD
|
||||||
const std::string& vFileName, // defaut file name
|
const std::string& vFileName, // defaut file name
|
||||||
const int& vCountSelectionMax = 1, // count selection max
|
const int& vCountSelectionMax = 1, // count selection max
|
||||||
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
||||||
ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags
|
ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags
|
||||||
|
SelectFun vSelectFun = nullptr); // function to be called on file click
|
||||||
|
|
||||||
void OpenDialog( // open simple dialog (path and filename are obtained from filePathName)
|
void OpenDialog( // open simple dialog (path and filename are obtained from filePathName)
|
||||||
const std::string& vKey, // key dialog
|
const std::string& vKey, // key dialog
|
||||||
|
@ -1164,7 +1167,8 @@ namespace IGFD
|
||||||
const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName)
|
const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName)
|
||||||
const int& vCountSelectionMax = 1, // count selection max
|
const int& vCountSelectionMax = 1, // count selection max
|
||||||
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
||||||
ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags
|
ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags
|
||||||
|
SelectFun vSelectFun = nullptr); // function to be called on file click
|
||||||
|
|
||||||
// with pane
|
// with pane
|
||||||
void OpenDialog( // open dialog with custom right pane (path and fileName can be specified)
|
void OpenDialog( // open dialog with custom right pane (path and fileName can be specified)
|
||||||
|
@ -1177,7 +1181,8 @@ namespace IGFD
|
||||||
const float& vSidePaneWidth = 250.0f, // side pane width
|
const float& vSidePaneWidth = 250.0f, // side pane width
|
||||||
const int& vCountSelectionMax = 1, // count selection max
|
const int& vCountSelectionMax = 1, // count selection max
|
||||||
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
||||||
ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags
|
ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags
|
||||||
|
SelectFun vSelectFun = nullptr); // function to be called on file click
|
||||||
|
|
||||||
void OpenDialog( // open dialog with custom right pane (path and filename are obtained from filePathName)
|
void OpenDialog( // open dialog with custom right pane (path and filename are obtained from filePathName)
|
||||||
const std::string& vKey, // key dialog
|
const std::string& vKey, // key dialog
|
||||||
|
@ -1188,7 +1193,8 @@ namespace IGFD
|
||||||
const float& vSidePaneWidth = 250.0f, // side pane width
|
const float& vSidePaneWidth = 250.0f, // side pane width
|
||||||
const int& vCountSelectionMax = 1, // count selection max
|
const int& vCountSelectionMax = 1, // count selection max
|
||||||
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
||||||
ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags
|
ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags
|
||||||
|
SelectFun vSelectFun = nullptr); // function to be called on file click
|
||||||
|
|
||||||
// modal dialog
|
// modal dialog
|
||||||
void OpenModal( // open simple modal (path and fileName can be specified)
|
void OpenModal( // open simple modal (path and fileName can be specified)
|
||||||
|
@ -1199,7 +1205,8 @@ namespace IGFD
|
||||||
const std::string& vFileName, // defaut file name
|
const std::string& vFileName, // defaut file name
|
||||||
const int& vCountSelectionMax = 1, // count selection max
|
const int& vCountSelectionMax = 1, // count selection max
|
||||||
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
||||||
ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags
|
ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags
|
||||||
|
SelectFun vSelectFun = nullptr); // function to be called on file click
|
||||||
|
|
||||||
void OpenModal( // open simple modal (path and fielname are obtained from filePathName)
|
void OpenModal( // open simple modal (path and fielname are obtained from filePathName)
|
||||||
const std::string& vKey, // key dialog
|
const std::string& vKey, // key dialog
|
||||||
|
@ -1208,7 +1215,8 @@ namespace IGFD
|
||||||
const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName)
|
const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName)
|
||||||
const int& vCountSelectionMax = 1, // count selection max
|
const int& vCountSelectionMax = 1, // count selection max
|
||||||
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
||||||
ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags
|
ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags
|
||||||
|
SelectFun vSelectFun = nullptr); // function to be called on file click
|
||||||
|
|
||||||
// with pane
|
// with pane
|
||||||
void OpenModal( // open modal with custom right pane (path and filename are obtained from filePathName)
|
void OpenModal( // open modal with custom right pane (path and filename are obtained from filePathName)
|
||||||
|
@ -1221,7 +1229,8 @@ namespace IGFD
|
||||||
const float& vSidePaneWidth = 250.0f, // side pane width
|
const float& vSidePaneWidth = 250.0f, // side pane width
|
||||||
const int& vCountSelectionMax = 1, // count selection max
|
const int& vCountSelectionMax = 1, // count selection max
|
||||||
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
||||||
ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags
|
ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags
|
||||||
|
SelectFun vSelectFun = nullptr); // function to be called on file click
|
||||||
|
|
||||||
void OpenModal( // open modal with custom right pane (path and fielname are obtained from filePathName)
|
void OpenModal( // open modal with custom right pane (path and fielname are obtained from filePathName)
|
||||||
const std::string& vKey, // key dialog
|
const std::string& vKey, // key dialog
|
||||||
|
@ -1232,7 +1241,8 @@ namespace IGFD
|
||||||
const float& vSidePaneWidth = 250.0f, // side pane width
|
const float& vSidePaneWidth = 250.0f, // side pane width
|
||||||
const int& vCountSelectionMax = 1, // count selection max
|
const int& vCountSelectionMax = 1, // count selection max
|
||||||
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane)
|
||||||
ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags
|
ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags
|
||||||
|
SelectFun vSelectFun = nullptr); // function to be called on file click
|
||||||
|
|
||||||
// Display / Close dialog form
|
// Display / Close dialog form
|
||||||
bool Display( // Display the dialog. return true if a result was obtained (Ok or not)
|
bool Display( // Display the dialog. return true if a result was obtained (Ok or not)
|
||||||
|
|
|
@ -29,6 +29,10 @@ furthermore, an `or reserved` indicates this field is always present, but is res
|
||||||
|
|
||||||
the format versions are:
|
the format versions are:
|
||||||
|
|
||||||
|
- 87: Furnace dev87
|
||||||
|
- 86: Furnace dev86
|
||||||
|
- 85: Furnace dev85
|
||||||
|
- 84: Furnace dev84
|
||||||
- 83: Furnace dev83
|
- 83: Furnace dev83
|
||||||
- 82: Furnace dev82
|
- 82: Furnace dev82
|
||||||
- 81: Furnace dev81
|
- 81: Furnace dev81
|
||||||
|
@ -269,7 +273,10 @@ size | description
|
||||||
1 | ExtCh channel state is shared (>=78) or reserved
|
1 | ExtCh channel state is shared (>=78) or reserved
|
||||||
1 | ignore DAC mode change outside of intended channel (>=83) or reserved
|
1 | ignore DAC mode change outside of intended channel (>=83) or reserved
|
||||||
1 | E1xx and E2xx also take priority over Slide00 (>=83) or reserved
|
1 | E1xx and E2xx also take priority over Slide00 (>=83) or reserved
|
||||||
23 | reserved
|
1 | new Sega PCM (with macros and proper vol/pan) (>=84) or reserved
|
||||||
|
1 | weird f-num/block-based chip pitch slides (>=85) or reserved
|
||||||
|
1 | SN duty macro always resets phase (>=86) or reserved
|
||||||
|
20 | reserved
|
||||||
```
|
```
|
||||||
|
|
||||||
# instrument
|
# instrument
|
||||||
|
@ -417,9 +424,11 @@ size | description
|
||||||
1 | reserved (>=17) or duty macro height (>=15) or reserved
|
1 | reserved (>=17) or duty macro height (>=15) or reserved
|
||||||
1 | reserved (>=17) or wave macro height (>=15) or reserved
|
1 | reserved (>=17) or wave macro height (>=15) or reserved
|
||||||
4?? | volume macro
|
4?? | volume macro
|
||||||
|
| - before version 87, if this is the C64 relative cutoff macro, its values were stored offset by 18.
|
||||||
4?? | arp macro
|
4?? | arp macro
|
||||||
| - before version 31, this macro's values were stored offset by 12.
|
| - before version 31, this macro's values were stored offset by 12.
|
||||||
4?? | duty macro
|
4?? | duty macro
|
||||||
|
| - before version 87, if this is the C64 relative duty macro, its values were stored offset by 12.
|
||||||
4?? | wave macro
|
4?? | wave macro
|
||||||
4?? | pitch macro (>=17)
|
4?? | pitch macro (>=17)
|
||||||
4?? | extra 1 macro (>=17)
|
4?? | extra 1 macro (>=17)
|
||||||
|
|
|
@ -436,6 +436,8 @@ class DivDispatch {
|
||||||
#define NOTE_PERIODIC_NOROUND(x) parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true)
|
#define NOTE_PERIODIC_NOROUND(x) parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true)
|
||||||
#define NOTE_FREQUENCY(x) parent->calcBaseFreq(chipClock,CHIP_FREQBASE,x,false)
|
#define NOTE_FREQUENCY(x) parent->calcBaseFreq(chipClock,CHIP_FREQBASE,x,false)
|
||||||
|
|
||||||
|
#define NOTE_FNUM_BLOCK(x,bits) parent->calcBaseFreqFNumBlock(chipClock,CHIP_FREQBASE,x,bits)
|
||||||
|
|
||||||
#define COLOR_NTSC (315000000.0/88.0)
|
#define COLOR_NTSC (315000000.0/88.0)
|
||||||
#define COLOR_PAL (283.75*15625.0+25.0)
|
#define COLOR_PAL (283.75*15625.0+25.0)
|
||||||
|
|
||||||
|
|
|
@ -770,6 +770,9 @@ String DivEngine::getWarnings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
DivInstrument* DivEngine::getIns(int index, DivInstrumentType fallbackType) {
|
DivInstrument* DivEngine::getIns(int index, DivInstrumentType fallbackType) {
|
||||||
|
if (index==-2 && tempIns!=NULL) {
|
||||||
|
return tempIns;
|
||||||
|
}
|
||||||
if (index<0 || index>=song.insLen) {
|
if (index<0 || index>=song.insLen) {
|
||||||
switch (fallbackType) {
|
switch (fallbackType) {
|
||||||
case DIV_INS_OPLL:
|
case DIV_INS_OPLL:
|
||||||
|
@ -918,6 +921,30 @@ double DivEngine::calcBaseFreq(double clock, double divider, int note, bool peri
|
||||||
base*(divider/clock);
|
base*(divider/clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, int note, int bits) {
|
||||||
|
int bf=calcBaseFreq(clock,divider,note,false);
|
||||||
|
int block=note/12;
|
||||||
|
if (block<0) block=0;
|
||||||
|
if (block>7) block=7;
|
||||||
|
bf>>=block;
|
||||||
|
if (bf<0) bf=0;
|
||||||
|
// octave boundaries
|
||||||
|
while (bf>0 && bf<644 && block>0) {
|
||||||
|
bf<<=1;
|
||||||
|
block--;
|
||||||
|
}
|
||||||
|
if (bf>1288) {
|
||||||
|
while (block<7) {
|
||||||
|
bf>>=1;
|
||||||
|
block++;
|
||||||
|
}
|
||||||
|
if (bf>((1<<bits)-1)) {
|
||||||
|
bf=(1<<bits)-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bf|(block<<bits);
|
||||||
|
}
|
||||||
|
|
||||||
int DivEngine::calcFreq(int base, int pitch, bool period, int octave) {
|
int DivEngine::calcFreq(int base, int pitch, bool period, int octave) {
|
||||||
if (song.linearPitch) {
|
if (song.linearPitch) {
|
||||||
// global pitch multiplier
|
// global pitch multiplier
|
||||||
|
@ -1381,6 +1408,15 @@ int DivEngine::addInstrumentPtr(DivInstrument* which) {
|
||||||
return song.insLen;
|
return song.insLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DivEngine::loadTempIns(DivInstrument* which) {
|
||||||
|
BUSY_BEGIN;
|
||||||
|
if (tempIns==NULL) {
|
||||||
|
tempIns=new DivInstrument;
|
||||||
|
}
|
||||||
|
*tempIns=*which;
|
||||||
|
BUSY_END;
|
||||||
|
}
|
||||||
|
|
||||||
void DivEngine::delInstrument(int index) {
|
void DivEngine::delInstrument(int index) {
|
||||||
BUSY_BEGIN;
|
BUSY_BEGIN;
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
|
@ -2037,7 +2073,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if ((ins<0 || ins>=song.insLen || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) {
|
if ((ins==-1 || ins>=song.insLen || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) {
|
||||||
chan[finalChan].midiNote=note;
|
chan[finalChan].midiNote=note;
|
||||||
pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true));
|
pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true));
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -43,8 +43,8 @@
|
||||||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||||
|
|
||||||
#define DIV_VERSION "dev84"
|
#define DIV_VERSION "dev87"
|
||||||
#define DIV_ENGINE_VERSION 84
|
#define DIV_ENGINE_VERSION 87
|
||||||
|
|
||||||
// for imports
|
// for imports
|
||||||
#define DIV_VERSION_MOD 0xff01
|
#define DIV_VERSION_MOD 0xff01
|
||||||
|
@ -290,6 +290,9 @@ class DivEngine {
|
||||||
void loadVGI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
void loadVGI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
void loadS3I(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
void loadS3I(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
void loadSBI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
void loadSBI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
|
void loadOPLI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
|
void loadOPNI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
|
void loadY12(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
void loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
void loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
void loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
void loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
void loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
void loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
|
@ -301,6 +304,7 @@ class DivEngine {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DivSong song;
|
DivSong song;
|
||||||
|
DivInstrument* tempIns;
|
||||||
DivSystem sysOfChan[DIV_MAX_CHANS];
|
DivSystem sysOfChan[DIV_MAX_CHANS];
|
||||||
int dispatchOfChan[DIV_MAX_CHANS];
|
int dispatchOfChan[DIV_MAX_CHANS];
|
||||||
int dispatchChanOfChan[DIV_MAX_CHANS];
|
int dispatchChanOfChan[DIV_MAX_CHANS];
|
||||||
|
@ -366,6 +370,9 @@ class DivEngine {
|
||||||
// calculate base frequency/period
|
// calculate base frequency/period
|
||||||
double calcBaseFreq(double clock, double divider, int note, bool period);
|
double calcBaseFreq(double clock, double divider, int note, bool period);
|
||||||
|
|
||||||
|
// calculate base frequency in f-num/block format
|
||||||
|
unsigned short calcBaseFreqFNumBlock(double clock, double divider, int note, int bits);
|
||||||
|
|
||||||
// calculate frequency/period
|
// calculate frequency/period
|
||||||
int calcFreq(int base, int pitch, bool period=false, int octave=0);
|
int calcFreq(int base, int pitch, bool period=false, int octave=0);
|
||||||
|
|
||||||
|
@ -521,6 +528,9 @@ class DivEngine {
|
||||||
// if the returned vector is empty then there was an error.
|
// if the returned vector is empty then there was an error.
|
||||||
std::vector<DivInstrument*> instrumentFromFile(const char* path);
|
std::vector<DivInstrument*> instrumentFromFile(const char* path);
|
||||||
|
|
||||||
|
// load temporary instrument
|
||||||
|
void loadTempIns(DivInstrument* which);
|
||||||
|
|
||||||
// delete instrument
|
// delete instrument
|
||||||
void delInstrument(int index);
|
void delInstrument(int index);
|
||||||
|
|
||||||
|
@ -796,6 +806,7 @@ class DivEngine {
|
||||||
metroAmp(0.0f),
|
metroAmp(0.0f),
|
||||||
metroVol(1.0f),
|
metroVol(1.0f),
|
||||||
totalProcessed(0),
|
totalProcessed(0),
|
||||||
|
tempIns(NULL),
|
||||||
oscBuf{NULL,NULL},
|
oscBuf{NULL,NULL},
|
||||||
oscSize(1),
|
oscSize(1),
|
||||||
oscReadPos(0),
|
oscReadPos(0),
|
||||||
|
|
|
@ -162,6 +162,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
ds.gbInsAffectsEnvelope=true;
|
ds.gbInsAffectsEnvelope=true;
|
||||||
ds.ignoreDACModeOutsideIntendedChannel=false;
|
ds.ignoreDACModeOutsideIntendedChannel=false;
|
||||||
ds.e1e2AlsoTakePriority=true;
|
ds.e1e2AlsoTakePriority=true;
|
||||||
|
ds.fbPortaPause=true;
|
||||||
|
ds.snDutyReset=true;
|
||||||
|
|
||||||
// 1.1 compat flags
|
// 1.1 compat flags
|
||||||
if (ds.version>24) {
|
if (ds.version>24) {
|
||||||
|
@ -499,9 +501,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
ins->std.dutyMacro.val[j]=reader.readI();
|
ins->std.dutyMacro.val[j]=reader.readI();
|
||||||
}
|
}
|
||||||
if ((ds.system[0]==DIV_SYSTEM_C64_8580 || ds.system[0]==DIV_SYSTEM_C64_6581) && ins->std.dutyMacro.val[j]>24) {
|
/*if ((ds.system[0]==DIV_SYSTEM_C64_8580 || ds.system[0]==DIV_SYSTEM_C64_6581) && ins->std.dutyMacro.val[j]>24) {
|
||||||
ins->std.dutyMacro.val[j]=24;
|
ins->std.dutyMacro.val[j]=24;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
if (ins->std.dutyMacro.len>0) {
|
if (ins->std.dutyMacro.len>0) {
|
||||||
ins->std.dutyMacro.open=true;
|
ins->std.dutyMacro.open=true;
|
||||||
|
@ -554,6 +556,16 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
ins->c64.bp=reader.readC();
|
ins->c64.bp=reader.readC();
|
||||||
ins->c64.lp=reader.readC();
|
ins->c64.lp=reader.readC();
|
||||||
ins->c64.ch3off=reader.readC();
|
ins->c64.ch3off=reader.readC();
|
||||||
|
|
||||||
|
// weird storage
|
||||||
|
if (ins->c64.volIsCutoff) {
|
||||||
|
for (int j=0; j<ins->std.volMacro.len; j++) {
|
||||||
|
ins->std.volMacro.val[j]-=18;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int j=0; j<ins->std.dutyMacro.len; j++) {
|
||||||
|
ins->std.dutyMacro.val[j]-=12;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ds.system[0]==DIV_SYSTEM_GB && ds.version>0x11) {
|
if (ds.system[0]==DIV_SYSTEM_GB && ds.version>0x11) {
|
||||||
|
@ -993,6 +1005,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
if (ds.version<84) {
|
if (ds.version<84) {
|
||||||
ds.newSegaPCM=false;
|
ds.newSegaPCM=false;
|
||||||
}
|
}
|
||||||
|
if (ds.version<85) {
|
||||||
|
ds.fbPortaPause=true;
|
||||||
|
}
|
||||||
|
if (ds.version<86) {
|
||||||
|
ds.snDutyReset=true;
|
||||||
|
}
|
||||||
ds.isDMF=false;
|
ds.isDMF=false;
|
||||||
|
|
||||||
reader.readS(); // reserved
|
reader.readS(); // reserved
|
||||||
|
@ -1342,7 +1360,17 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
reader.readC();
|
reader.readC();
|
||||||
}
|
}
|
||||||
for (int i=0; i<22; i++) {
|
if (ds.version>=85) {
|
||||||
|
ds.fbPortaPause=reader.readC();
|
||||||
|
} else {
|
||||||
|
reader.readC();
|
||||||
|
}
|
||||||
|
if (ds.version>=86) {
|
||||||
|
ds.snDutyReset=reader.readC();
|
||||||
|
} else {
|
||||||
|
reader.readC();
|
||||||
|
}
|
||||||
|
for (int i=0; i<20; i++) {
|
||||||
reader.readC();
|
reader.readC();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2296,7 +2324,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
w->writeC(song.ignoreDACModeOutsideIntendedChannel);
|
w->writeC(song.ignoreDACModeOutsideIntendedChannel);
|
||||||
w->writeC(song.e1e2AlsoTakePriority);
|
w->writeC(song.e1e2AlsoTakePriority);
|
||||||
w->writeC(song.newSegaPCM);
|
w->writeC(song.newSegaPCM);
|
||||||
for (int i=0; i<22; i++) {
|
w->writeC(song.fbPortaPause);
|
||||||
|
w->writeC(song.snDutyReset);
|
||||||
|
for (int i=0; i<20; i++) {
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2602,7 +2632,13 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
||||||
} else { // STD
|
} else { // STD
|
||||||
if (sys!=DIV_SYSTEM_GB) {
|
if (sys!=DIV_SYSTEM_GB) {
|
||||||
w->writeC(i->std.volMacro.len);
|
w->writeC(i->std.volMacro.len);
|
||||||
|
if ((sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) && i->c64.volIsCutoff) {
|
||||||
|
for (int j=0; j<i->std.volMacro.len; j++) {
|
||||||
|
w->writeI(i->std.volMacro.val[j]+18);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
w->write(i->std.volMacro.val,4*i->std.volMacro.len);
|
w->write(i->std.volMacro.val,4*i->std.volMacro.len);
|
||||||
|
}
|
||||||
if (i->std.volMacro.len>0) {
|
if (i->std.volMacro.len>0) {
|
||||||
w->writeC(i->std.volMacro.loop);
|
w->writeC(i->std.volMacro.loop);
|
||||||
}
|
}
|
||||||
|
@ -2622,7 +2658,13 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
||||||
w->writeC(i->std.arpMacro.mode);
|
w->writeC(i->std.arpMacro.mode);
|
||||||
|
|
||||||
w->writeC(i->std.dutyMacro.len);
|
w->writeC(i->std.dutyMacro.len);
|
||||||
|
if (sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) {
|
||||||
|
for (int j=0; j<i->std.dutyMacro.len; j++) {
|
||||||
|
w->writeI(i->std.dutyMacro.val[j]+12);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
w->write(i->std.dutyMacro.val,4*i->std.dutyMacro.len);
|
w->write(i->std.dutyMacro.val,4*i->std.dutyMacro.len);
|
||||||
|
}
|
||||||
if (i->std.dutyMacro.len>0) {
|
if (i->std.dutyMacro.len>0) {
|
||||||
w->writeC(i->std.dutyMacro.loop);
|
w->writeC(i->std.dutyMacro.loop);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,9 @@ enum DivInsFormats {
|
||||||
DIV_INSFORMAT_BTI,
|
DIV_INSFORMAT_BTI,
|
||||||
DIV_INSFORMAT_S3I,
|
DIV_INSFORMAT_S3I,
|
||||||
DIV_INSFORMAT_SBI,
|
DIV_INSFORMAT_SBI,
|
||||||
|
DIV_INSFORMAT_Y12,
|
||||||
|
DIV_INSFORMAT_OPLI,
|
||||||
|
DIV_INSFORMAT_OPNI,
|
||||||
DIV_INSFORMAT_BNK,
|
DIV_INSFORMAT_BNK,
|
||||||
DIV_INSFORMAT_OPM,
|
DIV_INSFORMAT_OPM,
|
||||||
DIV_INSFORMAT_FF,
|
DIV_INSFORMAT_FF,
|
||||||
|
@ -342,6 +345,16 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
||||||
ins->c64.bp=reader.readC();
|
ins->c64.bp=reader.readC();
|
||||||
ins->c64.lp=reader.readC();
|
ins->c64.lp=reader.readC();
|
||||||
ins->c64.ch3off=reader.readC();
|
ins->c64.ch3off=reader.readC();
|
||||||
|
|
||||||
|
// weird storage
|
||||||
|
if (ins->c64.volIsCutoff) {
|
||||||
|
for (int j=0; j<ins->std.volMacro.len; j++) {
|
||||||
|
ins->std.volMacro.val[j]-=18;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int j=0; j<ins->std.dutyMacro.len; j++) {
|
||||||
|
ins->std.dutyMacro.val[j]-=12;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ins->type==DIV_INS_GB) {
|
if (ins->type==DIV_INS_GB) {
|
||||||
ins->gb.envVol=reader.readC();
|
ins->gb.envVol=reader.readC();
|
||||||
|
@ -499,7 +512,7 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
||||||
logE("S3I PCM samples currently not supported.");
|
logE("S3I PCM samples currently not supported.");
|
||||||
}
|
}
|
||||||
ins->name = reader.readString(28);
|
ins->name = reader.readString(28);
|
||||||
ins->name = (ins->name.length() == 0) ? stripPath : ins->name;
|
ins->name = (ins->name.size() == 0) ? stripPath : ins->name;
|
||||||
|
|
||||||
int s3i_signature = reader.readI();
|
int s3i_signature = reader.readI();
|
||||||
|
|
||||||
|
@ -531,7 +544,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
||||||
|
|
||||||
// 32-byte null terminated instrument name
|
// 32-byte null terminated instrument name
|
||||||
String patchName = reader.readString(32);
|
String patchName = reader.readString(32);
|
||||||
patchName = (patchName.length() == 0) ? stripPath : patchName;
|
patchName = (patchName.size() == 0) ? stripPath : patchName;
|
||||||
|
|
||||||
auto writeOp = [](sbi_t& sbi, DivInstrumentFM::Operator& opM, DivInstrumentFM::Operator& opC) {
|
auto writeOp = [](sbi_t& sbi, DivInstrumentFM::Operator& opM, DivInstrumentFM::Operator& opC) {
|
||||||
opM.mult = sbi.Mcharacteristics & 0xF;
|
opM.mult = sbi.Mcharacteristics & 0xF;
|
||||||
|
@ -605,7 +618,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
||||||
if (is_6op) {
|
if (is_6op) {
|
||||||
// Freq Monster 801 6op SBIs use a 4+2op layout
|
// Freq Monster 801 6op SBIs use a 4+2op layout
|
||||||
// Save the 4op portion before reading the 2op part
|
// Save the 4op portion before reading the 2op part
|
||||||
ins->name = fmt::format("{0} (4op)", ins->name);
|
ins->name = fmt::sprintf("%s (4op)", ins->name);
|
||||||
ret.push_back(ins);
|
ret.push_back(ins);
|
||||||
|
|
||||||
readSbiOpData(sbi_op12, reader);
|
readSbiOpData(sbi_op12, reader);
|
||||||
|
@ -615,7 +628,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
||||||
DivInstrumentFM::Operator& opC6 = ins->fm.op[1];
|
DivInstrumentFM::Operator& opC6 = ins->fm.op[1];
|
||||||
ins->type = DIV_INS_OPL;
|
ins->type = DIV_INS_OPL;
|
||||||
ins->fm.ops = 2;
|
ins->fm.ops = 2;
|
||||||
ins->name = fmt::format("{0} (2op)", patchName);
|
ins->name = fmt::sprintf("%s (2op)", patchName);
|
||||||
writeOp(sbi_op12, opM6, opC6);
|
writeOp(sbi_op12, opM6, opC6);
|
||||||
ins->fm.alg = (sbi_op12.FeedConnect & 0x1);
|
ins->fm.alg = (sbi_op12.FeedConnect & 0x1);
|
||||||
ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7);
|
ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7);
|
||||||
|
@ -634,6 +647,204 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
||||||
delete ins;
|
delete ins;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DivEngine::loadOPLI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) {
|
||||||
|
DivInstrument* ins = new DivInstrument;
|
||||||
|
|
||||||
|
try {
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
String header = reader.readString(11);
|
||||||
|
if (header == "WOPL3-INST") {
|
||||||
|
uint16_t version = reader.readS();
|
||||||
|
if (version > 3) {
|
||||||
|
logW("Unknown OPLI version.");
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.readC(); // skip isPerc field
|
||||||
|
|
||||||
|
ins->type = DIV_INS_OPL;
|
||||||
|
String insName = reader.readString(32);
|
||||||
|
insName = (insName.size() > 0) ? insName : stripPath;
|
||||||
|
ins->name = insName;
|
||||||
|
reader.seek(7, SEEK_CUR); // skip MIDI params
|
||||||
|
uint8_t instTypeFlags = reader.readC(); // [0EEEDCBA] - see WOPL/OPLI spec
|
||||||
|
|
||||||
|
bool is_4op = ((instTypeFlags & 0x1) == 1);
|
||||||
|
bool is_2x2op = (((instTypeFlags>>1) & 0x1) == 1);
|
||||||
|
bool is_rhythm = (((instTypeFlags>>4) & 0x7) > 0);
|
||||||
|
|
||||||
|
auto readOpliOp = [](SafeReader& reader, DivInstrumentFM::Operator& op) {
|
||||||
|
uint8_t characteristics = reader.readC();
|
||||||
|
uint8_t keyScaleLevel = reader.readC();
|
||||||
|
uint8_t attackDecay = reader.readC();
|
||||||
|
uint8_t sustainRelease = reader.readC();
|
||||||
|
uint8_t waveSelect = reader.readC();
|
||||||
|
|
||||||
|
op.mult = characteristics & 0xF;
|
||||||
|
op.ksr = ((characteristics >> 4) & 0x1);
|
||||||
|
op.sus = ((characteristics >> 5) & 0x1);
|
||||||
|
op.vib = ((characteristics >> 6) & 0x1);
|
||||||
|
op.am = ((characteristics >> 7) & 0x1);
|
||||||
|
op.tl = keyScaleLevel & 0x3F;
|
||||||
|
op.ksl = ((keyScaleLevel >> 6) & 0x3);
|
||||||
|
op.ar = ((attackDecay >> 4) & 0xF);
|
||||||
|
op.dr = attackDecay & 0xF;
|
||||||
|
op.rr = sustainRelease & 0xF;
|
||||||
|
op.sl = ((sustainRelease >> 4) & 0xF);
|
||||||
|
op.ws = waveSelect;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t feedConnect = reader.readC();
|
||||||
|
uint8_t feedConnect2nd = reader.readC();
|
||||||
|
|
||||||
|
ins->fm.alg = (feedConnect & 0x1);
|
||||||
|
ins->fm.fb = ((feedConnect >> 1) & 0xF);
|
||||||
|
|
||||||
|
if (is_4op && !is_2x2op) {
|
||||||
|
ins->fm.ops = 4;
|
||||||
|
ins->fm.alg = (feedConnect & 0x1) | ((feedConnect2nd & 0x1) << 1);
|
||||||
|
for (int i : {2,0,3,1}) { // omfg >_<
|
||||||
|
readOpliOp(reader, ins->fm.op[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ins->fm.ops = 2;
|
||||||
|
for (int i : {1,0}) {
|
||||||
|
readOpliOp(reader, ins->fm.op[i]);
|
||||||
|
}
|
||||||
|
if (is_rhythm) {
|
||||||
|
ins->fm.opllPreset = (uint8_t)(1<<4);
|
||||||
|
|
||||||
|
} else if (is_2x2op) {
|
||||||
|
// Note: Pair detuning offset not mappable. Use E5xx effect :P
|
||||||
|
ins->name = fmt::sprintf("%s (1)", insName);
|
||||||
|
ret.push_back(ins);
|
||||||
|
|
||||||
|
ins = new DivInstrument;
|
||||||
|
ins->type = DIV_INS_OPL;
|
||||||
|
ins->name = fmt::sprintf("%s (2)", insName);
|
||||||
|
for (int i : {1,0}) {
|
||||||
|
readOpliOp(reader, ins->fm.op[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip rest of file
|
||||||
|
reader.seek(0, SEEK_END);
|
||||||
|
ret.push_back(ins);
|
||||||
|
}
|
||||||
|
} catch (EndOfFileException& e) {
|
||||||
|
lastError="premature end of file";
|
||||||
|
logE("premature end of file");
|
||||||
|
delete ins;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivEngine::loadOPNI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) {
|
||||||
|
DivInstrument* ins = new DivInstrument;
|
||||||
|
|
||||||
|
try {
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
String header = reader.readString(11);
|
||||||
|
if (header == "WOPN2-INST" || header == "WOPN2-IN2T") { // omfg >_<
|
||||||
|
uint16_t version = reader.readS();
|
||||||
|
if (!(version >= 2) || version > 0xF) {
|
||||||
|
// version 1 doesn't have a version field........
|
||||||
|
reader.seek(-2, SEEK_CUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.readC(); // skip isPerc
|
||||||
|
ins->type = DIV_INS_FM;
|
||||||
|
ins->fm.ops = 4;
|
||||||
|
|
||||||
|
String insName = reader.readString(32);
|
||||||
|
ins->name = (insName.size() > 0) ? insName : stripPath;
|
||||||
|
reader.seek(3, SEEK_CUR); // skip MIDI params
|
||||||
|
uint8_t feedAlgo = reader.readC();
|
||||||
|
ins->fm.alg = (feedAlgo & 0x7);
|
||||||
|
ins->fm.fb = ((feedAlgo>>3) & 0x7);
|
||||||
|
reader.readC(); // Skip global bank flags - see WOPN/OPNI spec
|
||||||
|
|
||||||
|
auto readOpniOp = [](SafeReader& reader, DivInstrumentFM::Operator& op) {
|
||||||
|
uint8_t dtMul = reader.readC();
|
||||||
|
uint8_t totalLevel = reader.readC();
|
||||||
|
uint8_t arRateScale = reader.readC();
|
||||||
|
uint8_t drAmpEnable = reader.readC();
|
||||||
|
uint8_t d2r = reader.readC();
|
||||||
|
uint8_t susRelease = reader.readC();
|
||||||
|
uint8_t ssgEg = reader.readC();
|
||||||
|
|
||||||
|
op.mult = dtMul & 0xF;
|
||||||
|
op.dt = ((dtMul >> 4) & 0x7);
|
||||||
|
op.tl = totalLevel & 0x3F;
|
||||||
|
op.rs = ((arRateScale >> 6) & 0x3);
|
||||||
|
op.ar = arRateScale & 0x1F;
|
||||||
|
op.dr = drAmpEnable & 0x1F;
|
||||||
|
op.am = ((drAmpEnable >> 7) & 0x1);
|
||||||
|
op.d2r = d2r & 0x1F;
|
||||||
|
op.rr = susRelease & 0xF;
|
||||||
|
op.sl = ((susRelease >> 4) & 0xF);
|
||||||
|
op.ssgEnv = ssgEg;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
readOpniOp(reader, ins->fm.op[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip rest of file
|
||||||
|
reader.seek(0, SEEK_END);
|
||||||
|
ret.push_back(ins);
|
||||||
|
}
|
||||||
|
} catch (EndOfFileException& e) {
|
||||||
|
lastError="premature end of file";
|
||||||
|
logE("premature end of file");
|
||||||
|
delete ins;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivEngine::loadY12(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) {
|
||||||
|
DivInstrument *ins = new DivInstrument;
|
||||||
|
|
||||||
|
try {
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
ins->type = DIV_INS_FM;
|
||||||
|
ins->fm.ops = 4;
|
||||||
|
ins->name = stripPath;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
DivInstrumentFM::Operator& insOp = ins->fm.op[i];
|
||||||
|
uint8_t tmp = reader.readC();
|
||||||
|
insOp.mult = tmp & 0xF;
|
||||||
|
insOp.dt = ((tmp >> 4) & 0x7);
|
||||||
|
insOp.tl = (reader.readC() & 0x3F);
|
||||||
|
tmp = reader.readC();
|
||||||
|
insOp.rs = ((tmp >> 6) & 0x3);
|
||||||
|
insOp.ar = tmp & 0x1F;
|
||||||
|
tmp = reader.readC();
|
||||||
|
insOp.dr = tmp & 0x1F;
|
||||||
|
insOp.am = ((tmp >> 7) & 0x1);
|
||||||
|
insOp.d2r = (reader.readC() & 0x1F);
|
||||||
|
tmp = reader.readC();
|
||||||
|
insOp.rr = tmp & 0xF;
|
||||||
|
insOp.sl = ((tmp >> 4) & 0xF);
|
||||||
|
insOp.ssgEnv = reader.readC();
|
||||||
|
if (!reader.seek(9, SEEK_CUR)) {
|
||||||
|
throw EndOfFileException(&reader, reader.tell() + 9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ins->fm.alg = reader.readC();
|
||||||
|
ins->fm.fb = reader.readC();
|
||||||
|
if (!reader.seek(62, SEEK_CUR)) {
|
||||||
|
throw EndOfFileException(&reader, reader.tell() + 62);
|
||||||
|
}
|
||||||
|
ret.push_back(ins);
|
||||||
|
} catch (EndOfFileException& e) {
|
||||||
|
lastError="premature end of file";
|
||||||
|
logE("premature end of file");
|
||||||
|
delete ins;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DivEngine::loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) {
|
void DivEngine::loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) {
|
||||||
std::vector<DivInstrument*> insList;
|
std::vector<DivInstrument*> insList;
|
||||||
std::vector<String*> instNames;
|
std::vector<String*> instNames;
|
||||||
|
@ -661,7 +872,9 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seek to BNK data
|
// Seek to BNK data
|
||||||
reader.seek(data_offset, SEEK_SET);
|
if (!reader.seek(data_offset, SEEK_SET)) {
|
||||||
|
throw EndOfFileException(&reader, data_offset);
|
||||||
|
};
|
||||||
|
|
||||||
// Read until EOF
|
// Read until EOF
|
||||||
for (int i = 0; i < readCount; ++i) {
|
for (int i = 0; i < readCount; ++i) {
|
||||||
|
@ -670,6 +883,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
||||||
auto& ins = insList[i];
|
auto& ins = insList[i];
|
||||||
|
|
||||||
ins->type = DIV_INS_OPL;
|
ins->type = DIV_INS_OPL;
|
||||||
|
ins->fm.ops = 2;
|
||||||
|
|
||||||
timbre.mode = reader.readC();
|
timbre.mode = reader.readC();
|
||||||
timbre.percVoice = reader.readC();
|
timbre.percVoice = reader.readC();
|
||||||
|
@ -706,14 +920,14 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
||||||
|
|
||||||
ins->fm.op[0].ws = reader.readC();
|
ins->fm.op[0].ws = reader.readC();
|
||||||
ins->fm.op[1].ws = reader.readC();
|
ins->fm.op[1].ws = reader.readC();
|
||||||
ins->name = instNames[i]->length() > 0 ? (*instNames[i]) : fmt::format("{0}[{1}]", stripPath, i);
|
ins->name = instNames[i]->length() > 0 ? (*instNames[i]) : fmt::sprintf("%s[%d]", stripPath, i);
|
||||||
}
|
}
|
||||||
reader.seek(0, SEEK_END);
|
reader.seek(0, SEEK_END);
|
||||||
|
|
||||||
} catch (EndOfFileException& e) {
|
} catch (EndOfFileException& e) {
|
||||||
lastError="premature end of file";
|
lastError="premature end of file";
|
||||||
logE("premature end of file");
|
logE("premature end of file");
|
||||||
for (int i = readCount; i >= 0; --i) {
|
for (int i = readCount - 1; i >= 0; --i) {
|
||||||
delete insList[i];
|
delete insList[i];
|
||||||
}
|
}
|
||||||
is_failed = true;
|
is_failed = true;
|
||||||
|
@ -798,7 +1012,7 @@ void DivEngine::loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, Str
|
||||||
} catch (EndOfFileException& e) {
|
} catch (EndOfFileException& e) {
|
||||||
lastError="premature end of file";
|
lastError="premature end of file";
|
||||||
logE("premature end of file");
|
logE("premature end of file");
|
||||||
for (int i = readCount; i >= 0; --i) {
|
for (int i = readCount - 1; i >= 0; --i) {
|
||||||
delete insList[i];
|
delete insList[i];
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -810,16 +1024,161 @@ void DivEngine::loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, Str
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivEngine::loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) {
|
void DivEngine::loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) {
|
||||||
DivInstrument* ins[128];
|
std::vector<DivInstrument*> insList;
|
||||||
memset(ins,0,128*sizeof(void*));
|
|
||||||
|
int readCount = 0;
|
||||||
|
bool is_failed = false;
|
||||||
|
|
||||||
|
bool patchNameRead = false,
|
||||||
|
lfoRead = false,
|
||||||
|
characteristicRead = false,
|
||||||
|
m1Read = false,
|
||||||
|
c1Read = false,
|
||||||
|
m2Read = false,
|
||||||
|
c2Read = false;
|
||||||
|
|
||||||
|
DivInstrument* newPatch = NULL;
|
||||||
|
|
||||||
|
auto completePatchRead = [&]() {
|
||||||
|
return patchNameRead && lfoRead && characteristicRead && m1Read && c1Read && m2Read && c2Read;
|
||||||
|
};
|
||||||
|
auto resetPatchRead = [&]() {
|
||||||
|
patchNameRead = lfoRead = characteristicRead = m1Read = c1Read = m2Read = c2Read = false;
|
||||||
|
newPatch = NULL;
|
||||||
|
};
|
||||||
|
auto readIntStrWithinRange = [](String&& input, int limitLow, int limitHigh) {
|
||||||
|
int x = std::stoi(input.c_str());
|
||||||
|
if (x > limitHigh || x < limitLow) {
|
||||||
|
throw std::invalid_argument(fmt::sprintf("%s is out of bounds of range [%d..%d]", input, limitLow, limitHigh));
|
||||||
|
}
|
||||||
|
return (x>limitHigh) ? limitHigh :
|
||||||
|
(x<limitLow) ? limitLow : x;
|
||||||
|
};
|
||||||
|
auto readOpmOperator = [&](SafeReader& reader, DivInstrumentFM::Operator& op) {
|
||||||
|
op.ar = readIntStrWithinRange(reader.readStringToken(), 0, 31);
|
||||||
|
op.dr = readIntStrWithinRange(reader.readStringToken(), 0, 31);
|
||||||
|
op.d2r = readIntStrWithinRange(reader.readStringToken(), 0, 31);
|
||||||
|
op.rr = readIntStrWithinRange(reader.readStringToken(), 0, 31);
|
||||||
|
op.sl = readIntStrWithinRange(reader.readStringToken(), 0, 15);
|
||||||
|
op.tl = readIntStrWithinRange(reader.readStringToken(), 0, 127);
|
||||||
|
op.rs = readIntStrWithinRange(reader.readStringToken(), 0, 3);;
|
||||||
|
op.mult = readIntStrWithinRange(reader.readStringToken(), 0, 15);
|
||||||
|
op.dt = readIntStrWithinRange(reader.readStringToken(), 0, 7);
|
||||||
|
op.dt = (op.dt >= 4) ? (7 - op.dt) : (op.dt + 3);
|
||||||
|
op.dt2 = readIntStrWithinRange(reader.readStringToken(), 0, 3);
|
||||||
|
op.am = readIntStrWithinRange(reader.readStringToken(), 0, 1);
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String line;
|
reader.seek(0, SEEK_SET);
|
||||||
|
while (!reader.isEOF()) {
|
||||||
|
String token = reader.readStringToken();
|
||||||
|
if (token.size() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.compare(0,2,"//") == 0) {
|
||||||
|
if (!reader.isEOF()) {
|
||||||
|
reader.readStringLine();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we know any other line would be associated with patch params
|
||||||
|
if (newPatch == NULL) {
|
||||||
|
newPatch = new DivInstrument;
|
||||||
|
newPatch->type = DIV_INS_FM;
|
||||||
|
newPatch->fm.ops = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read each line for their respective params. They may not be written in the same LINE order but they
|
||||||
|
// must absolutely be properly grouped per patch! Line prefixes must be separated by a space! (see inline comments)
|
||||||
|
|
||||||
|
if (token.size() >= 2) {
|
||||||
|
if (token[0] == '@') {
|
||||||
|
// @:123 Name of patch
|
||||||
|
// Note: Fallback to bank filename and current patch number in _file_ order (not @n order)
|
||||||
|
newPatch->name = reader.readStringLine();
|
||||||
|
newPatch->name = newPatch->name.size() > 0 ? newPatch->name : fmt::sprintf("%s[%d]", stripPath, readCount);
|
||||||
|
patchNameRead = true;
|
||||||
|
|
||||||
|
} else if (token.compare(0,3,"CH:") == 0) {
|
||||||
|
// CH: PAN FL CON AMS PMS SLOT NE
|
||||||
|
reader.readStringToken(); // skip PAN
|
||||||
|
newPatch->fm.fb = readIntStrWithinRange(reader.readStringToken(), 0, 7);
|
||||||
|
newPatch->fm.alg = readIntStrWithinRange(reader.readStringToken(), 0, 7);
|
||||||
|
newPatch->fm.ams = readIntStrWithinRange(reader.readStringToken(), 0, 4);
|
||||||
|
newPatch->fm.fms = readIntStrWithinRange(reader.readStringToken(), 0, 7);
|
||||||
|
reader.readStringToken(); // skip SLOT
|
||||||
|
reader.readStringToken(); // skip NE
|
||||||
|
characteristicRead = true;
|
||||||
|
|
||||||
|
} else if (token.compare(0,3,"C1:") == 0) {
|
||||||
|
// C1: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN
|
||||||
|
readOpmOperator(reader, newPatch->fm.op[2]);
|
||||||
|
c1Read = true;
|
||||||
|
|
||||||
|
} else if (token.compare(0,3,"C2:") == 0) {
|
||||||
|
// C2: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN
|
||||||
|
readOpmOperator(reader, newPatch->fm.op[3]);
|
||||||
|
c2Read = true;
|
||||||
|
|
||||||
|
} else if (token.compare(0,3,"M1:") == 0) {
|
||||||
|
// M1: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN
|
||||||
|
readOpmOperator(reader, newPatch->fm.op[0]);
|
||||||
|
m1Read = true;
|
||||||
|
|
||||||
|
} else if (token.compare(0,3,"M2:") == 0) {
|
||||||
|
// M2: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN
|
||||||
|
readOpmOperator(reader, newPatch->fm.op[1]);
|
||||||
|
m2Read = true;
|
||||||
|
|
||||||
|
} else if (token.compare(0,4,"LFO:") == 0) {
|
||||||
|
// LFO: LFRQ AMD PMD WF NFRQ
|
||||||
|
// Furnace patches do not store these as they are chip-global.
|
||||||
|
reader.readStringLine();
|
||||||
|
lfoRead = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// other unsupported lines ignored.
|
||||||
|
reader.readStringLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (completePatchRead()) {
|
||||||
|
insList.push_back(newPatch);
|
||||||
|
resetPatchRead();
|
||||||
|
++readCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPatch != NULL) {
|
||||||
|
addWarning("Last OPM patch read was incomplete and therefore not imported.");
|
||||||
|
logW("Last OPM patch read was incomplete and therefore not imported.");
|
||||||
|
delete newPatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < readCount; ++i) {
|
||||||
|
ret.push_back(insList[i]);
|
||||||
|
}
|
||||||
} catch (EndOfFileException& e) {
|
} catch (EndOfFileException& e) {
|
||||||
lastError="premature end of file";
|
lastError="premature end of file";
|
||||||
logE("premature end of file");
|
logE("premature end of file");
|
||||||
return;
|
is_failed = true;
|
||||||
|
} catch (std::invalid_argument& e) {
|
||||||
|
lastError=fmt::sprintf("Invalid value found in patch file. %s", e.what());
|
||||||
|
logE("Invalid value found in patch file.");
|
||||||
|
logE(e.what());
|
||||||
|
is_failed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_failed) {
|
||||||
|
for (int i = readCount - 1; i >= 0; --i) {
|
||||||
|
delete insList[i];
|
||||||
|
}
|
||||||
|
if (newPatch != NULL) {
|
||||||
|
delete newPatch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -947,6 +1306,12 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) {
|
||||||
format=DIV_INSFORMAT_S3I;
|
format=DIV_INSFORMAT_S3I;
|
||||||
} else if (extS==String(".sbi")) {
|
} else if (extS==String(".sbi")) {
|
||||||
format=DIV_INSFORMAT_SBI;
|
format=DIV_INSFORMAT_SBI;
|
||||||
|
} else if (extS==String(".opli")) {
|
||||||
|
format=DIV_INSFORMAT_OPLI;
|
||||||
|
} else if (extS==String(".opni")) {
|
||||||
|
format=DIV_INSFORMAT_OPNI;;
|
||||||
|
} else if (extS==String(".y12")) {
|
||||||
|
format=DIV_INSFORMAT_Y12;
|
||||||
} else if (extS==String(".bnk")) {
|
} else if (extS==String(".bnk")) {
|
||||||
format=DIV_INSFORMAT_BNK;
|
format=DIV_INSFORMAT_BNK;
|
||||||
} else if (extS==String(".opm")) {
|
} else if (extS==String(".opm")) {
|
||||||
|
@ -971,20 +1336,30 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) {
|
||||||
break;
|
break;
|
||||||
case DIV_INSFORMAT_BTI: // TODO
|
case DIV_INSFORMAT_BTI: // TODO
|
||||||
break;
|
break;
|
||||||
case DIV_INSFORMAT_OPM: // TODO
|
|
||||||
break;
|
|
||||||
case DIV_INSFORMAT_S3I:
|
case DIV_INSFORMAT_S3I:
|
||||||
loadS3I(reader,ret,stripPath);
|
loadS3I(reader,ret,stripPath);
|
||||||
break;
|
break;
|
||||||
case DIV_INSFORMAT_SBI:
|
case DIV_INSFORMAT_SBI:
|
||||||
loadSBI(reader,ret,stripPath);
|
loadSBI(reader,ret,stripPath);
|
||||||
break;
|
break;
|
||||||
|
case DIV_INSFORMAT_OPLI:
|
||||||
|
loadOPLI(reader,ret,stripPath);
|
||||||
|
break;
|
||||||
|
case DIV_INSFORMAT_OPNI:
|
||||||
|
loadOPNI(reader, ret, stripPath);
|
||||||
|
break;
|
||||||
|
case DIV_INSFORMAT_Y12:
|
||||||
|
loadY12(reader,ret,stripPath);
|
||||||
|
break;
|
||||||
case DIV_INSFORMAT_BNK:
|
case DIV_INSFORMAT_BNK:
|
||||||
loadBNK(reader, ret, stripPath);
|
loadBNK(reader, ret, stripPath);
|
||||||
break;
|
break;
|
||||||
case DIV_INSFORMAT_FF:
|
case DIV_INSFORMAT_FF:
|
||||||
loadFF(reader,ret,stripPath);
|
loadFF(reader,ret,stripPath);
|
||||||
break;
|
break;
|
||||||
|
case DIV_INSFORMAT_OPM:
|
||||||
|
loadOPM(reader, ret, stripPath);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader.tell()<reader.size()) {
|
if (reader.tell()<reader.size()) {
|
||||||
|
|
|
@ -643,6 +643,14 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
||||||
std.arpMacro.val[j]-=12;
|
std.arpMacro.val[j]-=12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (type==DIV_INS_C64 && version<87) {
|
||||||
|
if (c64.volIsCutoff && !c64.filterIsAbs) for (int j=0; j<std.volMacro.len; j++) {
|
||||||
|
std.volMacro.val[j]-=18;
|
||||||
|
}
|
||||||
|
if (!c64.dutyIsAbs) for (int j=0; j<std.dutyMacro.len; j++) {
|
||||||
|
std.dutyMacro.val[j]-=12;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (version>=17) {
|
if (version>=17) {
|
||||||
reader.read(std.pitchMacro.val,4*std.pitchMacro.len);
|
reader.read(std.pitchMacro.val,4*std.pitchMacro.len);
|
||||||
reader.read(std.ex1Macro.val,4*std.ex1Macro.len);
|
reader.read(std.ex1Macro.val,4*std.ex1Macro.len);
|
||||||
|
|
|
@ -34,7 +34,7 @@ class DivPlatformAmiga: public DivDispatch {
|
||||||
int audSub;
|
int audSub;
|
||||||
signed char audDat;
|
signed char audDat;
|
||||||
int sample, wave;
|
int sample, wave;
|
||||||
unsigned char ins;
|
int ins;
|
||||||
int busClock;
|
int busClock;
|
||||||
int note;
|
int note;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, setPos, useV, useP;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, setPos, useV, useP;
|
||||||
|
|
|
@ -37,7 +37,7 @@ class DivPlatformArcade: public DivDispatch {
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note;
|
||||||
unsigned char ins;
|
int ins;
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM, hardReset;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM, hardReset;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
|
|
|
@ -33,7 +33,8 @@ class DivPlatformAY8910: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, note, pitch;
|
int freq, baseFreq, note, pitch;
|
||||||
unsigned char ins, psgMode, autoEnvNum, autoEnvDen;
|
int ins;
|
||||||
|
unsigned char psgMode, autoEnvNum, autoEnvDen;
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
|
|
|
@ -29,7 +29,8 @@ class DivPlatformAY8930: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, note, pitch;
|
int freq, baseFreq, note, pitch;
|
||||||
unsigned char ins, psgMode, autoEnvNum, autoEnvDen, duty;
|
int ins;
|
||||||
|
unsigned char psgMode, autoEnvNum, autoEnvDen, duty;
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
|
|
|
@ -28,8 +28,7 @@
|
||||||
|
|
||||||
class DivPlatformBubSysWSG: public DivDispatch {
|
class DivPlatformBubSysWSG: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins;
|
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
||||||
signed char vol, outVol, wave;
|
signed char vol, outVol, wave;
|
||||||
signed char waveROM[32] = {0}; // 4 bit PROM per channel on bubble system
|
signed char waveROM[32] = {0}; // 4 bit PROM per channel on bubble system
|
||||||
|
|
|
@ -131,7 +131,7 @@ void DivPlatformC64::tick(bool sysTick) {
|
||||||
if (ins->c64.filterIsAbs) {
|
if (ins->c64.filterIsAbs) {
|
||||||
filtCut=MIN(2047,chan[i].std.vol.val);
|
filtCut=MIN(2047,chan[i].std.vol.val);
|
||||||
} else {
|
} else {
|
||||||
filtCut-=((signed char)chan[i].std.vol.val-18)*7;
|
filtCut-=((signed char)chan[i].std.vol.val)*7;
|
||||||
if (filtCut>2047) filtCut=2047;
|
if (filtCut>2047) filtCut=2047;
|
||||||
if (filtCut<0) filtCut=0;
|
if (filtCut<0) filtCut=0;
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ void DivPlatformC64::tick(bool sysTick) {
|
||||||
if (ins->c64.dutyIsAbs) {
|
if (ins->c64.dutyIsAbs) {
|
||||||
chan[i].duty=chan[i].std.duty.val;
|
chan[i].duty=chan[i].std.duty.val;
|
||||||
} else {
|
} else {
|
||||||
chan[i].duty-=((signed char)chan[i].std.duty.val-12)*4;
|
chan[i].duty-=((signed char)chan[i].std.duty.val)*4;
|
||||||
}
|
}
|
||||||
rWrite(i*7+2,chan[i].duty&0xff);
|
rWrite(i*7+2,chan[i].duty&0xff);
|
||||||
rWrite(i*7+3,chan[i].duty>>8);
|
rWrite(i*7+3,chan[i].duty>>8);
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
|
|
||||||
class DivPlatformC64: public DivDispatch {
|
class DivPlatformC64: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, prevFreq, testWhen, note;
|
int freq, baseFreq, pitch, prevFreq, testWhen, note, ins;
|
||||||
unsigned char ins, sweep, wave, attack, decay, sustain, release;
|
unsigned char sweep, wave, attack, decay, sustain, release;
|
||||||
short duty;
|
short duty;
|
||||||
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, filter;
|
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, filter;
|
||||||
bool resetMask, resetFilter, resetDuty, ring, sync;
|
bool resetMask, resetFilter, resetDuty, ring, sync;
|
||||||
|
|
|
@ -443,7 +443,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
pageWrite(0x00|i,0x06,0); // Clear ECOUNT
|
pageWrite(0x00|i,0x06,0); // Clear ECOUNT
|
||||||
pageWrite(0x20|i,0x03,chan[i].pcm.reversed?chan[i].pcm.end:chan[i].pcm.start); // Set ACCUM to start address
|
pageWrite(0x20|i,0x03,chan[i].pcm.reversed?chan[i].pcm.end:chan[i].pcm.start); // Set ACCUM to start address
|
||||||
pageWrite(0x00|i,0x07,0xffff); // Set K1 and K2 to 0xffff
|
pageWrite(0x00|i,0x07,0xffff); // Set K1 and K2 to 0xffff
|
||||||
pageWrite(0x00|i,0x09,0xffff);
|
pageWrite(0x00|i,0x09,0xffff,~0,(chanMax+1)*4*2); // needs to 4 sample period delay
|
||||||
pageWrite(0x00|i,0x01,chan[i].freq);
|
pageWrite(0x00|i,0x01,chan[i].freq);
|
||||||
pageWrite(0x20|i,0x01,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.start:chan[i].pcm.loopStart);
|
pageWrite(0x20|i,0x01,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.start:chan[i].pcm.loopStart);
|
||||||
pageWrite(0x20|i,0x02,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.end:chan[i].pcm.loopEnd);
|
pageWrite(0x20|i,0x02,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.end:chan[i].pcm.loopEnd);
|
||||||
|
@ -451,7 +451,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
pageWrite(0x00|i,0x03,((unsigned char)chan[i].envelope.lVRamp)<<8);
|
pageWrite(0x00|i,0x03,((unsigned char)chan[i].envelope.lVRamp)<<8);
|
||||||
pageWrite(0x00|i,0x05,((unsigned char)chan[i].envelope.rVRamp)<<8);
|
pageWrite(0x00|i,0x05,((unsigned char)chan[i].envelope.rVRamp)<<8);
|
||||||
pageWrite(0x00|i,0x0a,(((unsigned char)chan[i].envelope.k1Ramp)<<8)|(chan[i].envelope.k1Slow?1:0));
|
pageWrite(0x00|i,0x0a,(((unsigned char)chan[i].envelope.k1Ramp)<<8)|(chan[i].envelope.k1Slow?1:0));
|
||||||
pageWrite(0x00|i,0x08,(((unsigned char)chan[i].envelope.k2Ramp)<<8)|(chan[i].envelope.k2Slow?1:0),~0,(chanMax+1)*4*2); // needs to 4 sample period delay
|
pageWrite(0x00|i,0x08,(((unsigned char)chan[i].envelope.k2Ramp)<<8)|(chan[i].envelope.k2Slow?1:0));
|
||||||
// initialize filter
|
// initialize filter
|
||||||
pageWriteMask(0x00|i,0x5f,0x00,(chan[i].pcm.bank<<14)|(chan[i].filter.mode<<8),0xc300);
|
pageWriteMask(0x00|i,0x5f,0x00,(chan[i].pcm.bank<<14)|(chan[i].filter.mode<<8),0xc300);
|
||||||
if ((chan[i].std.ex2.mode==0) && (chan[i].std.ex2.had)) {
|
if ((chan[i].std.ex2.mode==0) && (chan[i].std.ex2.had)) {
|
||||||
|
|
|
@ -54,10 +54,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
||||||
loopEnd(0),
|
loopEnd(0),
|
||||||
loopMode(DIV_SAMPLE_LOOPMODE_ONESHOT) {}
|
loopMode(DIV_SAMPLE_LOOPMODE_ONESHOT) {}
|
||||||
} pcm;
|
} pcm;
|
||||||
int freq, baseFreq, pitch;
|
int freq, baseFreq, pitch, note, ins, sample, wave;
|
||||||
int sample, wave;
|
|
||||||
unsigned char ins;
|
|
||||||
int note;
|
|
||||||
bool active, insChanged, freqChanged, volChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop;
|
bool active, insChanged, freqChanged, volChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop;
|
||||||
|
|
||||||
struct FilterChanged { // Filter changed flags
|
struct FilterChanged { // Filter changed flags
|
||||||
|
@ -103,9 +100,10 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
||||||
freq(0),
|
freq(0),
|
||||||
baseFreq(0),
|
baseFreq(0),
|
||||||
pitch(0),
|
pitch(0),
|
||||||
sample(-1),
|
|
||||||
ins(-1),
|
|
||||||
note(0),
|
note(0),
|
||||||
|
ins(-1),
|
||||||
|
sample(-1),
|
||||||
|
wave(-1),
|
||||||
active(false),
|
active(false),
|
||||||
insChanged(true),
|
insChanged(true),
|
||||||
freqChanged(false),
|
freqChanged(false),
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
|
|
||||||
class DivPlatformFDS: public DivDispatch {
|
class DivPlatformFDS: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, prevFreq, note, modFreq;
|
int freq, baseFreq, pitch, prevFreq, note, modFreq, ins;
|
||||||
unsigned char ins, duty, sweep, modDepth, modPos;
|
unsigned char duty, sweep, modDepth, modPos;
|
||||||
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, modOn;
|
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, modOn;
|
||||||
signed char vol, outVol, wave;
|
signed char vol, outVol, wave;
|
||||||
signed char modTable[32];
|
signed char modTable[32];
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
|
|
||||||
class DivPlatformGB: public DivDispatch {
|
class DivPlatformGB: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins, duty, sweep;
|
unsigned char duty, sweep;
|
||||||
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta;
|
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta;
|
||||||
signed char vol, outVol, wave;
|
signed char vol, outVol, wave;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
|
|
|
@ -86,14 +86,22 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s
|
||||||
static int os[2];
|
static int os[2];
|
||||||
|
|
||||||
for (size_t h=start; h<start+len; h++) {
|
for (size_t h=start; h<start+len; h++) {
|
||||||
|
if (!dacReady) {
|
||||||
|
dacDelay+=32000;
|
||||||
|
if (dacDelay>=rate) {
|
||||||
|
dacDelay-=rate;
|
||||||
|
dacReady=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (dacMode && dacSample!=-1) {
|
if (dacMode && dacSample!=-1) {
|
||||||
dacPeriod-=6;
|
dacPeriod+=dacRate;
|
||||||
if (dacPeriod<1) {
|
if (dacPeriod>=rate) {
|
||||||
DivSample* s=parent->getSample(dacSample);
|
DivSample* s=parent->getSample(dacSample);
|
||||||
if (s->samples>0) {
|
if (s->samples>0) {
|
||||||
if (!isMuted[5]) {
|
if (!isMuted[5]) {
|
||||||
if (writes.size()<16) {
|
if (dacReady && writes.size()<16) {
|
||||||
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
||||||
|
dacReady=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dacPos++;
|
dacPos++;
|
||||||
|
@ -107,7 +115,7 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dacPeriod+=MAX(40,dacRate);
|
while (dacPeriod>=rate) dacPeriod-=rate;
|
||||||
} else {
|
} else {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
}
|
}
|
||||||
|
@ -156,14 +164,22 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
|
||||||
static int os[2];
|
static int os[2];
|
||||||
|
|
||||||
for (size_t h=start; h<start+len; h++) {
|
for (size_t h=start; h<start+len; h++) {
|
||||||
|
if (!dacReady) {
|
||||||
|
dacDelay+=32000;
|
||||||
|
if (dacDelay>=rate) {
|
||||||
|
dacDelay-=rate;
|
||||||
|
dacReady=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (dacMode && dacSample!=-1) {
|
if (dacMode && dacSample!=-1) {
|
||||||
dacPeriod-=24;
|
dacPeriod+=dacRate;
|
||||||
if (dacPeriod<1) {
|
if (dacPeriod>=rate) {
|
||||||
DivSample* s=parent->getSample(dacSample);
|
DivSample* s=parent->getSample(dacSample);
|
||||||
if (s->samples>0) {
|
if (s->samples>0) {
|
||||||
if (!isMuted[5]) {
|
if (!isMuted[5]) {
|
||||||
if (writes.size()<16) {
|
if (dacReady && writes.size()<16) {
|
||||||
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
||||||
|
dacReady=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dacPos++;
|
dacPos++;
|
||||||
|
@ -177,7 +193,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dacPeriod+=MAX(40,dacRate);
|
while (dacPeriod>=rate) dacPeriod-=rate;
|
||||||
} else {
|
} else {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
}
|
}
|
||||||
|
@ -247,15 +263,15 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
||||||
if (chan[i].std.arp.had) {
|
if (chan[i].std.arp.had) {
|
||||||
if (!chan[i].inPorta) {
|
if (!chan[i].inPorta) {
|
||||||
if (chan[i].std.arp.mode) {
|
if (chan[i].std.arp.mode) {
|
||||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
|
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].std.arp.val,11);
|
||||||
} else {
|
} else {
|
||||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp.val);
|
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note+(signed char)chan[i].std.arp.val,11);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chan[i].freqChanged=true;
|
chan[i].freqChanged=true;
|
||||||
} else {
|
} else {
|
||||||
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
||||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
|
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note,11);
|
||||||
chan[i].freqChanged=true;
|
chan[i].freqChanged=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -396,11 +412,20 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
||||||
for (int i=0; i<6; i++) {
|
for (int i=0; i<6; i++) {
|
||||||
if (i==2 && extMode) continue;
|
if (i==2 && extMode) continue;
|
||||||
if (chan[i].freqChanged) {
|
if (chan[i].freqChanged) {
|
||||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq));
|
int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4)+chan[i].std.pitch.val;
|
||||||
if (chan[i].freq>262143) chan[i].freq=262143;
|
int block=(chan[i].baseFreq&0xf800)>>11;
|
||||||
int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val;
|
if (fNum<0) fNum=0;
|
||||||
immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8);
|
if (fNum>2047) {
|
||||||
immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff);
|
while (block<7) {
|
||||||
|
fNum>>=1;
|
||||||
|
block++;
|
||||||
|
}
|
||||||
|
if (fNum>2047) fNum=2047;
|
||||||
|
}
|
||||||
|
chan[i].freq=(block<<11)|fNum;
|
||||||
|
if (chan[i].freq>0x3fff) chan[i].freq=0x3fff;
|
||||||
|
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
|
||||||
|
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
|
||||||
if (chan[i].furnaceDac && dacMode) {
|
if (chan[i].furnaceDac && dacMode) {
|
||||||
double off=1.0;
|
double off=1.0;
|
||||||
if (dacSample>=0 && dacSample<parent->song.sampleLen) {
|
if (dacSample>=0 && dacSample<parent->song.sampleLen) {
|
||||||
|
@ -408,12 +433,13 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
||||||
if (s->centerRate<1) {
|
if (s->centerRate<1) {
|
||||||
off=1.0;
|
off=1.0;
|
||||||
} else {
|
} else {
|
||||||
off=8363.0/(double)s->centerRate;
|
off=(double)s->centerRate/8363.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dacRate=(1280000*1.25*off)/MAX(1,chan[i].baseFreq);
|
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4)+chan[i].std.pitch.val;
|
||||||
|
dacRate=chan[i].freq*off;
|
||||||
if (dacRate<1) dacRate=1;
|
if (dacRate<1) dacRate=1;
|
||||||
if (dumpWrites) addWrite(0xffff0001,1280000/dacRate);
|
if (dumpWrites) addWrite(0xffff0001,dacRate);
|
||||||
}
|
}
|
||||||
chan[i].freqChanged=false;
|
chan[i].freqChanged=false;
|
||||||
}
|
}
|
||||||
|
@ -424,47 +450,6 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformGenesis::octave(int freq) {
|
|
||||||
if (freq>=82432) {
|
|
||||||
return 128;
|
|
||||||
} else if (freq>=41216) {
|
|
||||||
return 64;
|
|
||||||
} else if (freq>=20608) {
|
|
||||||
return 32;
|
|
||||||
} else if (freq>=10304) {
|
|
||||||
return 16;
|
|
||||||
} else if (freq>=5152) {
|
|
||||||
return 8;
|
|
||||||
} else if (freq>=2576) {
|
|
||||||
return 4;
|
|
||||||
} else if (freq>=1288) {
|
|
||||||
return 2;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DivPlatformGenesis::toFreq(int freq) {
|
|
||||||
if (freq>=82432) {
|
|
||||||
return 0x3800|((freq>>7)&0x7ff);
|
|
||||||
} else if (freq>=41216) {
|
|
||||||
return 0x3000|((freq>>6)&0x7ff);
|
|
||||||
} else if (freq>=20608) {
|
|
||||||
return 0x2800|((freq>>5)&0x7ff);
|
|
||||||
} else if (freq>=10304) {
|
|
||||||
return 0x2000|((freq>>4)&0x7ff);
|
|
||||||
} else if (freq>=5152) {
|
|
||||||
return 0x1800|((freq>>3)&0x7ff);
|
|
||||||
} else if (freq>=2576) {
|
|
||||||
return 0x1000|((freq>>2)&0x7ff);
|
|
||||||
} else if (freq>=1288) {
|
|
||||||
return 0x800|((freq>>1)&0x7ff);
|
|
||||||
} else {
|
|
||||||
return freq&0x7ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DivPlatformGenesis::muteChannel(int ch, bool mute) {
|
void DivPlatformGenesis::muteChannel(int ch, bool mute) {
|
||||||
isMuted[ch]=mute;
|
isMuted[ch]=mute;
|
||||||
for (int j=0; j<4; j++) {
|
for (int j=0; j<4; j++) {
|
||||||
|
@ -511,7 +496,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
dacPos=0;
|
dacPos=0;
|
||||||
dacPeriod=0;
|
dacPeriod=0;
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
}
|
}
|
||||||
chan[c.chan].furnaceDac=true;
|
chan[c.chan].furnaceDac=true;
|
||||||
|
@ -530,7 +515,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
dacPos=0;
|
dacPos=0;
|
||||||
dacPeriod=0;
|
dacPeriod=0;
|
||||||
dacRate=1280000/MAX(1,parent->getSample(dacSample)->rate);
|
dacRate=MAX(1,parent->getSample(dacSample)->rate);
|
||||||
if (dumpWrites) addWrite(0xffff0001,parent->getSample(dacSample)->rate);
|
if (dumpWrites) addWrite(0xffff0001,parent->getSample(dacSample)->rate);
|
||||||
chan[c.chan].furnaceDac=false;
|
chan[c.chan].furnaceDac=false;
|
||||||
}
|
}
|
||||||
|
@ -578,7 +563,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
chan[c.chan].insChanged=false;
|
chan[c.chan].insChanged=false;
|
||||||
|
|
||||||
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_FNUM_BLOCK(c.value,11);
|
||||||
chan[c.chan].portaPause=false;
|
chan[c.chan].portaPause=false;
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
|
@ -658,31 +643,65 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_NOTE_PORTA: {
|
case DIV_CMD_NOTE_PORTA: {
|
||||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
if (c.chan==5 && chan[c.chan].furnaceDac) {
|
||||||
int newFreq;
|
int destFreq=parent->calcBaseFreq(1,1,c.value2,false);
|
||||||
bool return2=false;
|
bool return2=false;
|
||||||
if (destFreq>chan[c.chan].baseFreq) {
|
if (destFreq>chan[c.chan].baseFreq) {
|
||||||
newFreq=chan[c.chan].baseFreq+c.value*octave(chan[c.chan].baseFreq);
|
chan[c.chan].baseFreq+=c.value*16;
|
||||||
|
if (chan[c.chan].baseFreq>=destFreq) {
|
||||||
|
chan[c.chan].baseFreq=destFreq;
|
||||||
|
return2=true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chan[c.chan].baseFreq-=c.value*16;
|
||||||
|
if (chan[c.chan].baseFreq<=destFreq) {
|
||||||
|
chan[c.chan].baseFreq=destFreq;
|
||||||
|
return2=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
if (return2) {
|
||||||
|
chan[c.chan].inPorta=false;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||||
|
int newFreq;
|
||||||
|
bool return2=false;
|
||||||
|
if (chan[c.chan].portaPause) {
|
||||||
|
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
|
||||||
|
}
|
||||||
|
if (destFreq>chan[c.chan].baseFreq) {
|
||||||
|
newFreq=chan[c.chan].baseFreq+c.value;
|
||||||
if (newFreq>=destFreq) {
|
if (newFreq>=destFreq) {
|
||||||
newFreq=destFreq;
|
newFreq=destFreq;
|
||||||
return2=true;
|
return2=true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq);
|
newFreq=chan[c.chan].baseFreq-c.value;
|
||||||
if (newFreq<=destFreq) {
|
if (newFreq<=destFreq) {
|
||||||
newFreq=destFreq;
|
newFreq=destFreq;
|
||||||
return2=true;
|
return2=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// check for octave boundary
|
||||||
|
// what the heck!
|
||||||
if (!chan[c.chan].portaPause) {
|
if (!chan[c.chan].portaPause) {
|
||||||
if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) {
|
if ((newFreq&0x7ff)>1288 && (newFreq&0xf800)<0x3800) {
|
||||||
|
chan[c.chan].portaPauseFreq=(644)|((newFreq+0x800)&0xf800);
|
||||||
|
chan[c.chan].portaPause=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((newFreq&0x7ff)<644 && (newFreq&0xf800)>0) {
|
||||||
|
chan[c.chan].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800);
|
||||||
chan[c.chan].portaPause=true;
|
chan[c.chan].portaPause=true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chan[c.chan].baseFreq=newFreq;
|
|
||||||
chan[c.chan].portaPause=false;
|
chan[c.chan].portaPause=false;
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
|
chan[c.chan].baseFreq=newFreq;
|
||||||
if (return2) {
|
if (return2) {
|
||||||
chan[c.chan].inPorta=false;
|
chan[c.chan].inPorta=false;
|
||||||
return 2;
|
return 2;
|
||||||
|
@ -701,7 +720,11 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_LEGATO: {
|
case DIV_CMD_LEGATO: {
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
if (c.chan==5 && chan[c.chan].furnaceDac) {
|
||||||
|
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
|
||||||
|
} else {
|
||||||
|
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||||
|
}
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
break;
|
break;
|
||||||
|
@ -853,6 +876,8 @@ void DivPlatformGenesis::reset() {
|
||||||
dacPeriod=0;
|
dacPeriod=0;
|
||||||
dacPos=0;
|
dacPos=0;
|
||||||
dacRate=0;
|
dacRate=0;
|
||||||
|
dacDelay=0;
|
||||||
|
dacReady=true;
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
sampleBank=0;
|
sampleBank=0;
|
||||||
lfoValue=8;
|
lfoValue=8;
|
||||||
|
|
|
@ -36,8 +36,8 @@ class DivPlatformGenesis: public DivDispatch {
|
||||||
DivInstrumentFM state;
|
DivInstrumentFM state;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, portaPauseFreq, note;
|
||||||
unsigned char ins;
|
int ins;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
unsigned char pan;
|
unsigned char pan;
|
||||||
|
@ -47,6 +47,7 @@ class DivPlatformGenesis: public DivDispatch {
|
||||||
freq(0),
|
freq(0),
|
||||||
baseFreq(0),
|
baseFreq(0),
|
||||||
pitch(0),
|
pitch(0),
|
||||||
|
portaPauseFreq(0),
|
||||||
note(0),
|
note(0),
|
||||||
ins(-1),
|
ins(-1),
|
||||||
active(false),
|
active(false),
|
||||||
|
@ -84,6 +85,8 @@ class DivPlatformGenesis: public DivDispatch {
|
||||||
int dacRate;
|
int dacRate;
|
||||||
unsigned int dacPos;
|
unsigned int dacPos;
|
||||||
int dacSample;
|
int dacSample;
|
||||||
|
int dacDelay;
|
||||||
|
bool dacReady;
|
||||||
unsigned char sampleBank;
|
unsigned char sampleBank;
|
||||||
unsigned char lfoValue;
|
unsigned char lfoValue;
|
||||||
|
|
||||||
|
@ -93,9 +96,6 @@ class DivPlatformGenesis: public DivDispatch {
|
||||||
short oldWrites[512];
|
short oldWrites[512];
|
||||||
short pendingWrites[512];
|
short pendingWrites[512];
|
||||||
|
|
||||||
int octave(int freq);
|
|
||||||
int toFreq(int freq);
|
|
||||||
|
|
||||||
friend void putDispatchChan(void*,int,int);
|
friend void putDispatchChan(void*,int,int);
|
||||||
|
|
||||||
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
|
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
|
||||||
|
|
|
@ -72,7 +72,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
||||||
opChan[ch].insChanged=false;
|
opChan[ch].insChanged=false;
|
||||||
|
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
opChan[ch].baseFreq=NOTE_FREQUENCY(c.value);
|
opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||||
opChan[ch].portaPause=false;
|
opChan[ch].portaPause=false;
|
||||||
opChan[ch].freqChanged=true;
|
opChan[ch].freqChanged=true;
|
||||||
}
|
}
|
||||||
|
@ -127,31 +127,49 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_NOTE_PORTA: {
|
case DIV_CMD_NOTE_PORTA: {
|
||||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||||
int newFreq;
|
int newFreq;
|
||||||
bool return2=false;
|
bool return2=false;
|
||||||
|
if (opChan[ch].portaPause) {
|
||||||
|
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
|
||||||
|
}
|
||||||
if (destFreq>opChan[ch].baseFreq) {
|
if (destFreq>opChan[ch].baseFreq) {
|
||||||
newFreq=opChan[ch].baseFreq+c.value*octave(opChan[ch].baseFreq);
|
newFreq=opChan[ch].baseFreq+c.value;
|
||||||
if (newFreq>=destFreq) {
|
if (newFreq>=destFreq) {
|
||||||
newFreq=destFreq;
|
newFreq=destFreq;
|
||||||
return2=true;
|
return2=true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newFreq=opChan[ch].baseFreq-c.value*octave(opChan[ch].baseFreq);
|
newFreq=opChan[ch].baseFreq-c.value;
|
||||||
if (newFreq<=destFreq) {
|
if (newFreq<=destFreq) {
|
||||||
newFreq=destFreq;
|
newFreq=destFreq;
|
||||||
return2=true;
|
return2=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// what the heck!
|
||||||
if (!opChan[ch].portaPause) {
|
if (!opChan[ch].portaPause) {
|
||||||
if (octave(opChan[ch].baseFreq)!=octave(newFreq)) {
|
if ((newFreq&0x7ff)>1288 && (newFreq&0xf800)<0x3800) {
|
||||||
|
if (parent->song.fbPortaPause) {
|
||||||
|
opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800);
|
||||||
opChan[ch].portaPause=true;
|
opChan[ch].portaPause=true;
|
||||||
break;
|
break;
|
||||||
|
} else {
|
||||||
|
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((newFreq&0x7ff)<644 && (newFreq&0xf800)>0) {
|
||||||
|
if (parent->song.fbPortaPause) {
|
||||||
|
opChan[ch].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800);
|
||||||
|
opChan[ch].portaPause=true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
opChan[ch].baseFreq=newFreq;
|
|
||||||
opChan[ch].portaPause=false;
|
opChan[ch].portaPause=false;
|
||||||
opChan[ch].freqChanged=true;
|
opChan[ch].freqChanged=true;
|
||||||
|
opChan[ch].baseFreq=newFreq;
|
||||||
if (return2) return 2;
|
if (return2) return 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -172,7 +190,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_LEGATO: {
|
case DIV_CMD_LEGATO: {
|
||||||
opChan[ch].baseFreq=NOTE_FREQUENCY(c.value);
|
opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||||
opChan[ch].freqChanged=true;
|
opChan[ch].freqChanged=true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -289,35 +307,20 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
||||||
unsigned char writeMask=2;
|
unsigned char writeMask=2;
|
||||||
if (extMode) for (int i=0; i<4; i++) {
|
if (extMode) for (int i=0; i<4; i++) {
|
||||||
if (opChan[i].freqChanged) {
|
if (opChan[i].freqChanged) {
|
||||||
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch);
|
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4)+opChan[i].std.pitch.val;
|
||||||
if (opChan[i].freq>262143) opChan[i].freq=262143;
|
int block=(opChan[i].baseFreq&0xf800)>>11;
|
||||||
if (opChan[i].freq>=82432) {
|
if (fNum<0) fNum=0;
|
||||||
opChan[i].freqH=((opChan[i].freq>>15)&7)|0x38;
|
if (fNum>2047) {
|
||||||
opChan[i].freqL=(opChan[i].freq>>7)&0xff;
|
while (block<7) {
|
||||||
} else if (opChan[i].freq>=41216) {
|
fNum>>=1;
|
||||||
opChan[i].freqH=((opChan[i].freq>>14)&7)|0x30;
|
block++;
|
||||||
opChan[i].freqL=(opChan[i].freq>>6)&0xff;
|
|
||||||
} else if (opChan[i].freq>=20608) {
|
|
||||||
opChan[i].freqH=((opChan[i].freq>>13)&7)|0x28;
|
|
||||||
opChan[i].freqL=(opChan[i].freq>>5)&0xff;
|
|
||||||
} else if (opChan[i].freq>=10304) {
|
|
||||||
opChan[i].freqH=((opChan[i].freq>>12)&7)|0x20;
|
|
||||||
opChan[i].freqL=(opChan[i].freq>>4)&0xff;
|
|
||||||
} else if (opChan[i].freq>=5152) {
|
|
||||||
opChan[i].freqH=((opChan[i].freq>>11)&7)|0x18;
|
|
||||||
opChan[i].freqL=(opChan[i].freq>>3)&0xff;
|
|
||||||
} else if (opChan[i].freq>=2576) {
|
|
||||||
opChan[i].freqH=((opChan[i].freq>>10)&7)|0x10;
|
|
||||||
opChan[i].freqL=(opChan[i].freq>>2)&0xff;
|
|
||||||
} else if (opChan[i].freq>=1288) {
|
|
||||||
opChan[i].freqH=((opChan[i].freq>>9)&7)|0x08;
|
|
||||||
opChan[i].freqL=(opChan[i].freq>>1)&0xff;
|
|
||||||
} else {
|
|
||||||
opChan[i].freqH=(opChan[i].freq>>8)&7;
|
|
||||||
opChan[i].freqL=opChan[i].freq&0xff;
|
|
||||||
}
|
}
|
||||||
immWrite(opChanOffsH[i],opChan[i].freqH);
|
if (fNum>2047) fNum=2047;
|
||||||
immWrite(opChanOffsL[i],opChan[i].freqL);
|
}
|
||||||
|
opChan[i].freq=(block<<11)|fNum;
|
||||||
|
if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff;
|
||||||
|
immWrite(opChanOffsH[i],opChan[i].freq>>8);
|
||||||
|
immWrite(opChanOffsL[i],opChan[i].freq&0xff);
|
||||||
}
|
}
|
||||||
writeMask|=opChan[i].active<<(4+i);
|
writeMask|=opChan[i].active<<(4+i);
|
||||||
if (opChan[i].keyOn) {
|
if (opChan[i].keyOn) {
|
||||||
|
|
|
@ -25,13 +25,27 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
|
||||||
struct OpChannel {
|
struct OpChannel {
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch;
|
int freq, baseFreq, pitch, portaPauseFreq, ins;
|
||||||
unsigned char ins;
|
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
||||||
int vol;
|
int vol;
|
||||||
unsigned char pan;
|
unsigned char pan;
|
||||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
|
OpChannel():
|
||||||
|
freqH(0),
|
||||||
|
freqL(0),
|
||||||
|
freq(0),
|
||||||
|
baseFreq(0),
|
||||||
|
pitch(0),
|
||||||
|
portaPauseFreq(0),
|
||||||
|
ins(-1),
|
||||||
|
active(false),
|
||||||
|
insChanged(true),
|
||||||
|
freqChanged(false),
|
||||||
|
keyOn(false),
|
||||||
|
keyOff(false),
|
||||||
|
portaPause(false),
|
||||||
|
vol(0),
|
||||||
|
pan(3) {}
|
||||||
};
|
};
|
||||||
OpChannel opChan[4];
|
OpChannel opChan[4];
|
||||||
bool isOpMuted[4];
|
bool isOpMuted[4];
|
||||||
|
|
|
@ -44,8 +44,8 @@ class DivPlatformLynx: public DivDispatch {
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
MikeyFreqDiv fd;
|
MikeyFreqDiv fd;
|
||||||
MikeyDuty duty;
|
MikeyDuty duty;
|
||||||
int baseFreq, pitch, note, actualNote, lfsr;
|
int baseFreq, pitch, note, actualNote, lfsr, ins;
|
||||||
unsigned char ins, pan;
|
unsigned char pan;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
||||||
signed char vol, outVol;
|
signed char vol, outVol;
|
||||||
Channel():
|
Channel():
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
|
|
||||||
class DivPlatformMMC5: public DivDispatch {
|
class DivPlatformMMC5: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, prevFreq, note;
|
int freq, baseFreq, pitch, prevFreq, note, ins;
|
||||||
unsigned char ins, duty, sweep;
|
unsigned char duty, sweep;
|
||||||
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac;
|
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac;
|
||||||
signed char vol, outVol, wave;
|
signed char vol, outVol, wave;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
|
|
||||||
class DivPlatformNES: public DivDispatch {
|
class DivPlatformNES: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, prevFreq, note;
|
int freq, baseFreq, pitch, prevFreq, note, ins;
|
||||||
unsigned char ins, duty, sweep;
|
unsigned char duty, sweep;
|
||||||
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac;
|
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac;
|
||||||
signed char vol, outVol, wave;
|
signed char vol, outVol, wave;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
|
|
|
@ -548,6 +548,9 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
if (chan[c.chan].insChanged) {
|
if (chan[c.chan].insChanged) {
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
chan[c.chan].fourOp=(ops==4);
|
chan[c.chan].fourOp=(ops==4);
|
||||||
|
if (chan[c.chan].fourOp) {
|
||||||
|
chan[c.chan+1].std.init(NULL);
|
||||||
|
}
|
||||||
update4OpMask=true;
|
update4OpMask=true;
|
||||||
for (int i=0; i<ops; i++) {
|
for (int i=0; i<ops; i++) {
|
||||||
unsigned char slot=slots[i][c.chan];
|
unsigned char slot=slots[i][c.chan];
|
||||||
|
|
|
@ -30,8 +30,7 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
DivInstrumentFM state;
|
DivInstrumentFM state;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins;
|
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, fourOp;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, fourOp;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
unsigned char pan;
|
unsigned char pan;
|
||||||
|
|
|
@ -33,8 +33,7 @@ class DivPlatformOPLL: public DivDispatch {
|
||||||
DivInstrumentFM state;
|
DivInstrumentFM state;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins;
|
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
unsigned char pan;
|
unsigned char pan;
|
||||||
|
|
|
@ -31,8 +31,8 @@ class DivPlatformPCE: public DivDispatch {
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note;
|
||||||
int dacPeriod, dacRate;
|
int dacPeriod, dacRate;
|
||||||
unsigned int dacPos;
|
unsigned int dacPos;
|
||||||
int dacSample;
|
int dacSample, ins;
|
||||||
unsigned char ins, pan;
|
unsigned char pan;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, furnaceDac;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, furnaceDac;
|
||||||
signed char vol, outVol, wave;
|
signed char vol, outVol, wave;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
|
|
|
@ -53,6 +53,7 @@ void DivPlatformPCSpeaker::acquire_unfilt(short* bufL, short* bufR, size_t start
|
||||||
for (size_t i=start; i<start+len; i++) {
|
for (size_t i=start; i<start+len; i++) {
|
||||||
if (on) {
|
if (on) {
|
||||||
pos-=PCSPKR_DIVIDER;
|
pos-=PCSPKR_DIVIDER;
|
||||||
|
if (pos>freq) pos=freq;
|
||||||
while (pos<0) {
|
while (pos<0) {
|
||||||
if (freq<1) {
|
if (freq<1) {
|
||||||
pos=1;
|
pos=1;
|
||||||
|
@ -71,6 +72,7 @@ void DivPlatformPCSpeaker::acquire_cone(short* bufL, short* bufR, size_t start,
|
||||||
for (size_t i=start; i<start+len; i++) {
|
for (size_t i=start; i<start+len; i++) {
|
||||||
if (on) {
|
if (on) {
|
||||||
pos-=PCSPKR_DIVIDER;
|
pos-=PCSPKR_DIVIDER;
|
||||||
|
if (pos>freq) pos=freq;
|
||||||
while (pos<0) {
|
while (pos<0) {
|
||||||
if (freq<1) {
|
if (freq<1) {
|
||||||
pos=1;
|
pos=1;
|
||||||
|
@ -95,6 +97,7 @@ void DivPlatformPCSpeaker::acquire_piezo(short* bufL, short* bufR, size_t start,
|
||||||
for (size_t i=start; i<start+len; i++) {
|
for (size_t i=start; i<start+len; i++) {
|
||||||
if (on) {
|
if (on) {
|
||||||
pos-=PCSPKR_DIVIDER;
|
pos-=PCSPKR_DIVIDER;
|
||||||
|
if (pos>freq) pos=freq;
|
||||||
while (pos<0) {
|
while (pos<0) {
|
||||||
if (freq<1) {
|
if (freq<1) {
|
||||||
pos=1;
|
pos=1;
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
|
|
||||||
class DivPlatformPCSpeaker: public DivDispatch {
|
class DivPlatformPCSpeaker: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins, duty, sweep;
|
unsigned char duty, sweep;
|
||||||
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac;
|
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac;
|
||||||
signed char vol, outVol, wave;
|
signed char vol, outVol, wave;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
|
|
|
@ -25,8 +25,7 @@
|
||||||
|
|
||||||
class DivPlatformPET: public DivDispatch {
|
class DivPlatformPET: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins;
|
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
||||||
int vol, outVol, wave;
|
int vol, outVol, wave;
|
||||||
unsigned char sreg;
|
unsigned char sreg;
|
||||||
|
|
|
@ -30,8 +30,7 @@ class DivPlatformQSound: public DivDispatch {
|
||||||
int freq, baseFreq, pitch;
|
int freq, baseFreq, pitch;
|
||||||
unsigned short audLen;
|
unsigned short audLen;
|
||||||
unsigned int audPos;
|
unsigned int audPos;
|
||||||
int sample, wave;
|
int sample, wave, ins;
|
||||||
unsigned char ins;
|
|
||||||
int note;
|
int note;
|
||||||
int panning;
|
int panning;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave;
|
||||||
|
|
|
@ -35,8 +35,8 @@ class DivPlatformSAA1099: public DivDispatch {
|
||||||
protected:
|
protected:
|
||||||
struct Channel {
|
struct Channel {
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins, psgMode;
|
unsigned char psgMode;
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
|
|
|
@ -29,8 +29,7 @@ class DivPlatformSegaPCM: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins;
|
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
|
|
|
@ -86,12 +86,14 @@ void DivPlatformSMS::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
if (i==3) {
|
if (i==3) {
|
||||||
if (chan[i].std.duty.had) {
|
if (chan[i].std.duty.had) {
|
||||||
|
if (chan[i].std.duty.val!=snNoiseMode || parent->song.snDutyReset) {
|
||||||
snNoiseMode=chan[i].std.duty.val;
|
snNoiseMode=chan[i].std.duty.val;
|
||||||
if (chan[i].std.duty.val<2) {
|
if (chan[i].std.duty.val<2) {
|
||||||
chan[3].freqChanged=false;
|
chan[3].freqChanged=false;
|
||||||
}
|
}
|
||||||
updateSNMode=true;
|
updateSNMode=true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (chan[i].std.phaseReset.had) {
|
if (chan[i].std.phaseReset.had) {
|
||||||
if (chan[i].std.phaseReset.val==1) {
|
if (chan[i].std.phaseReset.val==1) {
|
||||||
updateSNMode=true;
|
updateSNMode=true;
|
||||||
|
|
|
@ -26,8 +26,7 @@
|
||||||
|
|
||||||
class DivPlatformSMS: public DivDispatch {
|
class DivPlatformSMS: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, note, actualNote;
|
int freq, baseFreq, pitch, note, actualNote, ins;
|
||||||
unsigned char ins;
|
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
||||||
signed char vol, outVol;
|
signed char vol, outVol;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
|
|
|
@ -226,6 +226,7 @@ void DivPlatformSwan::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chan[3].std.duty.had) {
|
if (chan[3].std.duty.had) {
|
||||||
|
if (noise!=chan[3].std.duty.val) {
|
||||||
noise=chan[3].std.duty.val;
|
noise=chan[3].std.duty.val;
|
||||||
if (noise>0) {
|
if (noise>0) {
|
||||||
rWrite(0x0e,((noise-1)&0x07)|0x18);
|
rWrite(0x0e,((noise-1)&0x07)|0x18);
|
||||||
|
@ -234,6 +235,7 @@ void DivPlatformSwan::tick(bool sysTick) {
|
||||||
sndCtrl&=~0x80;
|
sndCtrl&=~0x80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
rWrite(0x10,sndCtrl);
|
rWrite(0x10,sndCtrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@
|
||||||
|
|
||||||
class DivPlatformSwan: public DivDispatch {
|
class DivPlatformSwan: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins, pan;
|
unsigned char pan;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
||||||
int vol, outVol, wave;
|
int vol, outVol, wave;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
class DivPlatformTIA: public DivDispatch {
|
class DivPlatformTIA: public DivDispatch {
|
||||||
protected:
|
protected:
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins, shape;
|
unsigned char shape;
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
|
|
|
@ -35,8 +35,7 @@ class DivPlatformTX81Z: public DivDispatch {
|
||||||
DivInstrumentFM state;
|
DivInstrumentFM state;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins;
|
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM, hardReset;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM, hardReset;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
|
|
|
@ -29,8 +29,8 @@ struct VERA_PCM;
|
||||||
class DivPlatformVERA: public DivDispatch {
|
class DivPlatformVERA: public DivDispatch {
|
||||||
protected:
|
protected:
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins, pan;
|
unsigned char pan;
|
||||||
bool active, freqChanged, inPorta;
|
bool active, freqChanged, inPorta;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
unsigned accum;
|
unsigned accum;
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
|
|
||||||
class DivPlatformVIC20: public DivDispatch {
|
class DivPlatformVIC20: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins, pan;
|
unsigned char pan;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
||||||
int vol, outVol, wave, waveWriteCycle;
|
int vol, outVol, wave, waveWriteCycle;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
|
|
|
@ -31,8 +31,8 @@ class DivPlatformVRC6: public DivDispatch {
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note;
|
||||||
int dacPeriod, dacRate, dacOut;
|
int dacPeriod, dacRate, dacOut;
|
||||||
unsigned int dacPos;
|
unsigned int dacPos;
|
||||||
int dacSample;
|
int dacSample, ins;
|
||||||
unsigned char ins, duty;
|
unsigned char duty;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, pcm, furnaceDac;
|
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, pcm, furnaceDac;
|
||||||
signed char vol, outVol;
|
signed char vol, outVol;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
|
|
|
@ -43,8 +43,8 @@ class DivPlatformYM2610: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
DivInstrumentFM state;
|
DivInstrumentFM state;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins, psgMode, autoEnvNum, autoEnvDen;
|
unsigned char psgMode, autoEnvNum, autoEnvDen;
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
|
|
|
@ -35,8 +35,8 @@ class DivPlatformYM2610B: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
DivInstrumentFM state;
|
DivInstrumentFM state;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note, ins;
|
||||||
unsigned char ins, psgMode, autoEnvNum, autoEnvDen;
|
unsigned char psgMode, autoEnvNum, autoEnvDen;
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
|
|
|
@ -25,8 +25,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B {
|
||||||
struct OpChannel {
|
struct OpChannel {
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch;
|
int freq, baseFreq, pitch, ins;
|
||||||
unsigned char ins;
|
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
||||||
int vol;
|
int vol;
|
||||||
|
|
|
@ -25,8 +25,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 {
|
||||||
struct OpChannel {
|
struct OpChannel {
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch;
|
int freq, baseFreq, pitch, ins;
|
||||||
unsigned char ins;
|
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
||||||
int vol;
|
int vol;
|
||||||
|
|
|
@ -144,7 +144,9 @@ String SafeReader::readString(size_t stlen) {
|
||||||
logD("SR: reading string len %d at %x",stlen,curSeek);
|
logD("SR: reading string len %d at %x",stlen,curSeek);
|
||||||
#endif
|
#endif
|
||||||
size_t curPos=0;
|
size_t curPos=0;
|
||||||
while (curPos<stlen) {
|
if (isEOF()) throw EndOfFileException(this, len);
|
||||||
|
|
||||||
|
while (!isEOF() && curPos<stlen) {
|
||||||
unsigned char c=readC();
|
unsigned char c=readC();
|
||||||
if (c!=0) ret.push_back(c);
|
if (c!=0) ret.push_back(c);
|
||||||
curPos++;
|
curPos++;
|
||||||
|
@ -155,8 +157,48 @@ String SafeReader::readString(size_t stlen) {
|
||||||
String SafeReader::readString() {
|
String SafeReader::readString() {
|
||||||
String ret;
|
String ret;
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
while ((c=readC())!=0) {
|
if (isEOF()) throw EndOfFileException(this, len);
|
||||||
|
|
||||||
|
while (!isEOF() && (c=readC())!=0) {
|
||||||
ret.push_back(c);
|
ret.push_back(c);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String SafeReader::readStringLine() {
|
||||||
|
String ret;
|
||||||
|
unsigned char c;
|
||||||
|
if (isEOF()) throw EndOfFileException(this, len);
|
||||||
|
|
||||||
|
while (!isEOF() && (c = readC()) != 0) {
|
||||||
|
if (c=='\r'||c=='\n') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret.push_back(c);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
String SafeReader::readStringToken(unsigned char delim) {
|
||||||
|
String ret;
|
||||||
|
unsigned char c;
|
||||||
|
if (isEOF()) throw EndOfFileException(this, len);
|
||||||
|
|
||||||
|
while (!isEOF() && (c=readC())!=0) {
|
||||||
|
if (c == '\r' || c == '\n') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c == delim) {
|
||||||
|
if (ret.length() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret.push_back(c);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
String SafeReader::readStringToken() {
|
||||||
|
return readStringToken(' ');
|
||||||
|
}
|
||||||
|
|
|
@ -66,6 +66,10 @@ class SafeReader {
|
||||||
double readD_BE();
|
double readD_BE();
|
||||||
String readString();
|
String readString();
|
||||||
String readString(size_t len);
|
String readString(size_t len);
|
||||||
|
String readStringLine();
|
||||||
|
String readStringToken(unsigned char delim);
|
||||||
|
String readStringToken();
|
||||||
|
inline bool isEOF() { return curSeek >= len; };
|
||||||
|
|
||||||
SafeReader(void* b, size_t l):
|
SafeReader(void* b, size_t l):
|
||||||
buf((unsigned char*)b),
|
buf((unsigned char*)b),
|
||||||
|
|
|
@ -19,21 +19,57 @@
|
||||||
|
|
||||||
#include "song.h"
|
#include "song.h"
|
||||||
|
|
||||||
|
void DivSong::clearSongData() {
|
||||||
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
|
pat[i].wipePatterns();
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(orders.ord,0,DIV_MAX_CHANS*256);
|
||||||
|
ordersLen=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivSong::clearInstruments() {
|
||||||
|
for (DivInstrument* i: ins) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
ins.clear();
|
||||||
|
insLen=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivSong::clearWavetables() {
|
||||||
|
for (DivWavetable* i: wave) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
wave.clear();
|
||||||
|
waveLen=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivSong::clearSamples() {
|
||||||
|
for (DivSample* i: sample) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
sample.clear();
|
||||||
|
sampleLen=0;
|
||||||
|
}
|
||||||
|
|
||||||
void DivSong::unload() {
|
void DivSong::unload() {
|
||||||
for (DivInstrument* i: ins) {
|
for (DivInstrument* i: ins) {
|
||||||
delete i;
|
delete i;
|
||||||
}
|
}
|
||||||
ins.clear();
|
ins.clear();
|
||||||
|
insLen=0;
|
||||||
|
|
||||||
for (DivWavetable* i: wave) {
|
for (DivWavetable* i: wave) {
|
||||||
delete i;
|
delete i;
|
||||||
}
|
}
|
||||||
wave.clear();
|
wave.clear();
|
||||||
|
waveLen=0;
|
||||||
|
|
||||||
for (DivSample* i: sample) {
|
for (DivSample* i: sample) {
|
||||||
delete i;
|
delete i;
|
||||||
}
|
}
|
||||||
sample.clear();
|
sample.clear();
|
||||||
|
sampleLen=0;
|
||||||
|
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
pat[i].wipePatterns();
|
pat[i].wipePatterns();
|
||||||
|
|
|
@ -325,6 +325,8 @@ struct DivSong {
|
||||||
bool ignoreDACModeOutsideIntendedChannel;
|
bool ignoreDACModeOutsideIntendedChannel;
|
||||||
bool e1e2AlsoTakePriority;
|
bool e1e2AlsoTakePriority;
|
||||||
bool newSegaPCM;
|
bool newSegaPCM;
|
||||||
|
bool fbPortaPause;
|
||||||
|
bool snDutyReset;
|
||||||
|
|
||||||
DivOrders orders;
|
DivOrders orders;
|
||||||
std::vector<DivInstrument*> ins;
|
std::vector<DivInstrument*> ins;
|
||||||
|
@ -339,6 +341,27 @@ struct DivSong {
|
||||||
DivWavetable nullWave;
|
DivWavetable nullWave;
|
||||||
DivSample nullSample;
|
DivSample nullSample;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clear orders and patterns.
|
||||||
|
*/
|
||||||
|
void clearSongData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clear instruments.
|
||||||
|
*/
|
||||||
|
void clearInstruments();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clear wavetables.
|
||||||
|
*/
|
||||||
|
void clearWavetables();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clear samples.
|
||||||
|
*/
|
||||||
|
void clearSamples();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* unloads the song, freeing all memory associated with it.
|
* unloads the song, freeing all memory associated with it.
|
||||||
* use before destroying the object.
|
* use before destroying the object.
|
||||||
|
@ -407,7 +430,9 @@ struct DivSong {
|
||||||
sharedExtStat(true),
|
sharedExtStat(true),
|
||||||
ignoreDACModeOutsideIntendedChannel(false),
|
ignoreDACModeOutsideIntendedChannel(false),
|
||||||
e1e2AlsoTakePriority(false),
|
e1e2AlsoTakePriority(false),
|
||||||
newSegaPCM(true) {
|
newSegaPCM(true),
|
||||||
|
fbPortaPause(false),
|
||||||
|
snDutyReset(false) {
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<32; i++) {
|
||||||
system[i]=DIV_SYSTEM_NULL;
|
system[i]=DIV_SYSTEM_NULL;
|
||||||
systemVol[i]=64;
|
systemVol[i]=64;
|
||||||
|
|
|
@ -101,6 +101,10 @@ void FurnaceGUI::drawCompatFlags() {
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip("simulates a bug in where portamento does not work after sliding.");
|
ImGui::SetTooltip("simulates a bug in where portamento does not work after sliding.");
|
||||||
}
|
}
|
||||||
|
ImGui::Checkbox("FM pitch slide octave boundary odd behavior",&e->song.fbPortaPause);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("if this is on, a pitch slide that crosses the octave boundary will stop for one tick and then continue from the nearest octave boundary.\nfor .dmf compatibility.");
|
||||||
|
}
|
||||||
ImGui::Checkbox("Apply Game Boy envelope on note-less instrument change",&e->song.gbInsAffectsEnvelope);
|
ImGui::Checkbox("Apply Game Boy envelope on note-less instrument change",&e->song.gbInsAffectsEnvelope);
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip("if this is on, an instrument change will also affect the envelope.");
|
ImGui::SetTooltip("if this is on, an instrument change will also affect the envelope.");
|
||||||
|
@ -113,6 +117,10 @@ void FurnaceGUI::drawCompatFlags() {
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip("does this make any sense by now?");
|
ImGui::SetTooltip("does this make any sense by now?");
|
||||||
}
|
}
|
||||||
|
ImGui::Checkbox("SN76489 duty macro always resets phase",&e->song.snDutyReset);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("when enabled, duty macro will always reset phase, even if its value hasn't changed.");
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Text("Loop modality:");
|
ImGui::Text("Loop modality:");
|
||||||
if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {
|
if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {
|
||||||
|
|
|
@ -554,6 +554,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
||||||
}
|
}
|
||||||
if (settings.cursorPastePos) {
|
if (settings.cursorPastePos) {
|
||||||
cursor.y=j;
|
cursor.y=j;
|
||||||
|
if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1;
|
||||||
updateScroll(cursor.y);
|
updateScroll(cursor.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
#include "../../extern/pfd-fixed/portable-file-dialogs.h"
|
#include "../../extern/pfd-fixed/portable-file-dialogs.h"
|
||||||
|
|
||||||
bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale) {
|
bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale, FileDialogSelectCallback clickCallback) {
|
||||||
if (opened) return false;
|
if (opened) return false;
|
||||||
saving=false;
|
saving=false;
|
||||||
curPath=path;
|
curPath=path;
|
||||||
|
@ -13,7 +13,7 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, c
|
||||||
dialogO=new pfd::open_file(header,path,filter);
|
dialogO=new pfd::open_file(header,path,filter);
|
||||||
} else {
|
} else {
|
||||||
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
|
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
|
||||||
ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path);
|
ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,1,nullptr,0,clickCallback);
|
||||||
}
|
}
|
||||||
opened=true;
|
opened=true;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "../ta-utils.h"
|
#include "../ta-utils.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace pfd {
|
namespace pfd {
|
||||||
|
@ -7,6 +8,8 @@ namespace pfd {
|
||||||
class save_file;
|
class save_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef std::function<void(const char*)> FileDialogSelectCallback;
|
||||||
|
|
||||||
class FurnaceGUIFileDialog {
|
class FurnaceGUIFileDialog {
|
||||||
bool sysDialog;
|
bool sysDialog;
|
||||||
bool opened;
|
bool opened;
|
||||||
|
@ -16,7 +19,7 @@ class FurnaceGUIFileDialog {
|
||||||
pfd::open_file* dialogO;
|
pfd::open_file* dialogO;
|
||||||
pfd::save_file* dialogS;
|
pfd::save_file* dialogS;
|
||||||
public:
|
public:
|
||||||
bool openLoad(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale);
|
bool openLoad(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale, FileDialogSelectCallback clickCallback=NULL);
|
||||||
bool openSave(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale);
|
bool openSave(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale);
|
||||||
bool accepted();
|
bool accepted();
|
||||||
void close();
|
void close();
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <SDL_keycode.h>
|
|
||||||
#define _USE_MATH_DEFINES
|
#define _USE_MATH_DEFINES
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
@ -1290,11 +1289,19 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
||||||
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
|
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
|
||||||
hasOpened=fileDialog->openLoad(
|
hasOpened=fileDialog->openLoad(
|
||||||
"Load Instrument",
|
"Load Instrument",
|
||||||
{"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.bnk *.ff",
|
{"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.opli *.opni *.y12 *.bnk *.ff *.opm",
|
||||||
"all files", ".*"},
|
"all files", ".*"},
|
||||||
"compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.bnk,.ff},.*",
|
"compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.opli,.opni,.y12,.bnk,.ff,.opm},.*",
|
||||||
workingDirIns,
|
workingDirIns,
|
||||||
dpiScale
|
dpiScale,
|
||||||
|
[this](const char* path) {
|
||||||
|
std::vector<DivInstrument*> instruments=e->instrumentFromFile(path);
|
||||||
|
if (!instruments.empty()) {
|
||||||
|
e->loadTempIns(instruments[0]);
|
||||||
|
curIns=-2;
|
||||||
|
}
|
||||||
|
for (DivInstrument* i: instruments) delete i;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case GUI_FILE_INS_SAVE:
|
case GUI_FILE_INS_SAVE:
|
||||||
|
@ -2420,7 +2427,7 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wantCaptureKeyboard=ImGui::GetIO().WantCaptureKeyboard;
|
wantCaptureKeyboard=ImGui::GetIO().WantTextInput;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
midiLock.lock();
|
midiLock.lock();
|
||||||
|
@ -2712,8 +2719,10 @@ bool FurnaceGUI::loop() {
|
||||||
if (ImGui::MenuItem("redo",BIND_FOR(GUI_ACTION_REDO))) doRedo();
|
if (ImGui::MenuItem("redo",BIND_FOR(GUI_ACTION_REDO))) doRedo();
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
editOptions(true);
|
editOptions(true);
|
||||||
/*ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::MenuItem("clear...");*/
|
if (ImGui::MenuItem("clear...")) {
|
||||||
|
showWarning("Are you sure you want to clear...",GUI_WARN_CLEAR);
|
||||||
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
if (ImGui::BeginMenu("settings")) {
|
if (ImGui::BeginMenu("settings")) {
|
||||||
|
@ -3371,6 +3380,69 @@ bool FurnaceGUI::loop() {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case GUI_WARN_CLEAR:
|
||||||
|
if (ImGui::Button("Song (orders and patterns)")) {
|
||||||
|
stop();
|
||||||
|
e->lockEngine([this]() {
|
||||||
|
e->song.clearSongData();
|
||||||
|
});
|
||||||
|
e->setOrder(0);
|
||||||
|
curOrder=0;
|
||||||
|
oldOrder=0;
|
||||||
|
oldOrder1=0;
|
||||||
|
MARK_MODIFIED;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Pattern")) {
|
||||||
|
stop();
|
||||||
|
e->lockEngine([this]() {
|
||||||
|
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||||
|
DivPattern* pat=e->song.pat[i].getPattern(e->song.orders.ord[i][curOrder],true);
|
||||||
|
memset(pat->data,-1,256*32*sizeof(short));
|
||||||
|
for (int j=0; j<256; j++) {
|
||||||
|
pat->data[j][0]=0;
|
||||||
|
pat->data[j][1]=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
MARK_MODIFIED;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Instruments")) {
|
||||||
|
stop();
|
||||||
|
e->lockEngine([this]() {
|
||||||
|
e->song.clearInstruments();
|
||||||
|
});
|
||||||
|
curIns=-1;
|
||||||
|
MARK_MODIFIED;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Wavetables")) {
|
||||||
|
stop();
|
||||||
|
e->lockEngine([this]() {
|
||||||
|
e->song.clearWavetables();
|
||||||
|
});
|
||||||
|
curWave=0;
|
||||||
|
MARK_MODIFIED;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Samples")) {
|
||||||
|
stop();
|
||||||
|
e->lockEngine([this]() {
|
||||||
|
e->song.clearSamples();
|
||||||
|
});
|
||||||
|
curSample=0;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Wait! What am I doing? Cancel!")) {
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case GUI_WARN_GENERIC:
|
case GUI_WARN_GENERIC:
|
||||||
if (ImGui::Button("OK")) {
|
if (ImGui::Button("OK")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
|
|
|
@ -265,6 +265,7 @@ enum FurnaceGUIWarnings {
|
||||||
GUI_WARN_RESET_COLORS,
|
GUI_WARN_RESET_COLORS,
|
||||||
GUI_WARN_RESET_KEYBINDS,
|
GUI_WARN_RESET_KEYBINDS,
|
||||||
GUI_WARN_CLOSE_SETTINGS,
|
GUI_WARN_CLOSE_SETTINGS,
|
||||||
|
GUI_WARN_CLEAR,
|
||||||
GUI_WARN_GENERIC
|
GUI_WARN_GENERIC
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -842,6 +843,8 @@ class FurnaceGUI {
|
||||||
int notePreviewBehavior;
|
int notePreviewBehavior;
|
||||||
int powerSave;
|
int powerSave;
|
||||||
int absorbInsInput;
|
int absorbInsInput;
|
||||||
|
int eventDelay;
|
||||||
|
int moveWindowTitle;
|
||||||
unsigned int maxUndoSteps;
|
unsigned int maxUndoSteps;
|
||||||
String mainFontPath;
|
String mainFontPath;
|
||||||
String patFontPath;
|
String patFontPath;
|
||||||
|
@ -917,6 +920,8 @@ class FurnaceGUI {
|
||||||
notePreviewBehavior(1),
|
notePreviewBehavior(1),
|
||||||
powerSave(1),
|
powerSave(1),
|
||||||
absorbInsInput(0),
|
absorbInsInput(0),
|
||||||
|
eventDelay(0),
|
||||||
|
moveWindowTitle(0),
|
||||||
maxUndoSteps(100),
|
maxUndoSteps(100),
|
||||||
mainFontPath(""),
|
mainFontPath(""),
|
||||||
patFontPath(""),
|
patFontPath(""),
|
||||||
|
|
|
@ -2762,8 +2762,8 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
if (ins->c64.filterIsAbs) {
|
if (ins->c64.filterIsAbs) {
|
||||||
volMax=2047;
|
volMax=2047;
|
||||||
} else {
|
} else {
|
||||||
volMin=-18;
|
volMin=-64;
|
||||||
volMax=18;
|
volMax=64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2795,13 +2795,15 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
bool arpMode=ins->std.arpMacro.mode;
|
bool arpMode=ins->std.arpMacro.mode;
|
||||||
|
|
||||||
const char* dutyLabel="Duty/Noise";
|
const char* dutyLabel="Duty/Noise";
|
||||||
|
int dutyMin=0;
|
||||||
int dutyMax=3;
|
int dutyMax=3;
|
||||||
if (ins->type==DIV_INS_C64) {
|
if (ins->type==DIV_INS_C64) {
|
||||||
dutyLabel="Duty";
|
dutyLabel="Duty";
|
||||||
if (ins->c64.dutyIsAbs) {
|
if (ins->c64.dutyIsAbs) {
|
||||||
dutyMax=4095;
|
dutyMax=4095;
|
||||||
} else {
|
} else {
|
||||||
dutyMax=24;
|
dutyMin=-96;
|
||||||
|
dutyMax=96;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ins->type==DIV_INS_FM) {
|
if (ins->type==DIV_INS_FM) {
|
||||||
|
@ -2926,16 +2928,16 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
if (volMax>0) {
|
if (volMax>0) {
|
||||||
NORMAL_MACRO(ins->std.volMacro,volMin,volMax,"vol",volumeLabel,160,ins->std.volMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_VOLUME],mmlString[0],volMin,volMax,NULL,false);
|
NORMAL_MACRO(ins->std.volMacro,volMin,volMax,"vol",volumeLabel,160,ins->std.volMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_VOLUME],mmlString[0],volMin,volMax,NULL,false);
|
||||||
}
|
}
|
||||||
NORMAL_MACRO(ins->std.arpMacro,arpMacroScroll,arpMacroScroll+24,"arp","Arpeggio",160,ins->std.arpMacro.open,false,NULL,true,&arpMacroScroll,(arpMode?-60:-80),70,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[1],-92,94,(ins->std.arpMacro.mode?(¯oHoverNote):NULL),true);
|
NORMAL_MACRO(ins->std.arpMacro,arpMacroScroll,arpMacroScroll+24,"arp","Arpeggio",160,ins->std.arpMacro.open,false,NULL,true,&arpMacroScroll,(arpMode?-60:-120),120,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[1],-120,120,(ins->std.arpMacro.mode?(¯oHoverNote):NULL),true);
|
||||||
if (dutyMax>0) {
|
if (dutyMax>0) {
|
||||||
if (ins->type==DIV_INS_MIKEY) {
|
if (ins->type==DIV_INS_MIKEY) {
|
||||||
NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false);
|
NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false);
|
||||||
} else if (ins->type==DIV_INS_C64) {
|
} else if (ins->type==DIV_INS_C64) {
|
||||||
NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false);
|
NORMAL_MACRO(ins->std.dutyMacro,dutyMin,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],dutyMin,dutyMax,NULL,false);
|
||||||
} else if (ins->type==DIV_INS_ES5506) {
|
} else if (ins->type==DIV_INS_ES5506) {
|
||||||
NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,¯oHoverES5506FilterMode,false);
|
NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,¯oHoverES5506FilterMode,false);
|
||||||
} else {
|
} else {
|
||||||
NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false);
|
NORMAL_MACRO(ins->std.dutyMacro,dutyMin,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],dutyMin,dutyMax,NULL,false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (waveMax>0) {
|
if (waveMax>0) {
|
||||||
|
|
|
@ -74,15 +74,6 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("Rate (Hz)");
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
|
||||||
if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED
|
|
||||||
if (sample->rate<100) sample->rate=100;
|
|
||||||
if (sample->rate>96000) sample->rate=96000;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("C-4 (Hz)");
|
ImGui::Text("C-4 (Hz)");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
@ -92,6 +83,15 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
if (sample->centerRate>65535) sample->centerRate=65535;
|
if (sample->centerRate>65535) sample->centerRate=65535;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::Text("Compat Rate");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED
|
||||||
|
if (sample->rate<100) sample->rate=100;
|
||||||
|
if (sample->rate>96000) sample->rate=96000;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Loop Mode");
|
ImGui::Text("Loop Mode");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
@ -619,16 +619,6 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("Rate (Hz)");
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
|
||||||
if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED
|
|
||||||
if (sample->rate<100) sample->rate=100;
|
|
||||||
if (sample->rate>96000) sample->rate=96000;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("C-4 (Hz)");
|
ImGui::Text("C-4 (Hz)");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
@ -638,6 +628,16 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
if (sample->centerRate>65535) sample->centerRate=65535;
|
if (sample->centerRate>65535) sample->centerRate=65535;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::Text("Compat Rate");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED
|
||||||
|
if (sample->rate<100) sample->rate=100;
|
||||||
|
if (sample->rate>96000) sample->rate=96000;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Loop Mode");
|
ImGui::Text("Loop Mode");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
|
@ -313,6 +313,21 @@ void FurnaceGUI::drawSettings() {
|
||||||
settings.sysFileDialog=sysFileDialogB;
|
settings.sysFileDialog=sysFileDialogB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool moveWindowTitleB=settings.moveWindowTitle;
|
||||||
|
if (ImGui::Checkbox("Only allow window movement when clicking on title bar",&moveWindowTitleB)) {
|
||||||
|
settings.moveWindowTitle=moveWindowTitleB;
|
||||||
|
applyUISettings(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool eventDelayB=settings.eventDelay;
|
||||||
|
if (ImGui::Checkbox("Enable event delay",&eventDelayB)) {
|
||||||
|
settings.eventDelay=eventDelayB;
|
||||||
|
applyUISettings(false);
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("may cause issues with high-polling-rate mice when previewing notes.");
|
||||||
|
}
|
||||||
|
|
||||||
bool powerSaveB=settings.powerSave;
|
bool powerSaveB=settings.powerSave;
|
||||||
if (ImGui::Checkbox("Power-saving mode",&powerSaveB)) {
|
if (ImGui::Checkbox("Power-saving mode",&powerSaveB)) {
|
||||||
settings.powerSave=powerSaveB;
|
settings.powerSave=powerSaveB;
|
||||||
|
@ -1651,6 +1666,8 @@ void FurnaceGUI::syncSettings() {
|
||||||
settings.notePreviewBehavior=e->getConfInt("notePreviewBehavior",1);
|
settings.notePreviewBehavior=e->getConfInt("notePreviewBehavior",1);
|
||||||
settings.powerSave=e->getConfInt("powerSave",POWER_SAVE_DEFAULT);
|
settings.powerSave=e->getConfInt("powerSave",POWER_SAVE_DEFAULT);
|
||||||
settings.absorbInsInput=e->getConfInt("absorbInsInput",0);
|
settings.absorbInsInput=e->getConfInt("absorbInsInput",0);
|
||||||
|
settings.eventDelay=e->getConfInt("eventDelay",0);
|
||||||
|
settings.moveWindowTitle=e->getConfInt("moveWindowTitle",0);
|
||||||
|
|
||||||
clampSetting(settings.mainFontSize,2,96);
|
clampSetting(settings.mainFontSize,2,96);
|
||||||
clampSetting(settings.patFontSize,2,96);
|
clampSetting(settings.patFontSize,2,96);
|
||||||
|
@ -1715,6 +1732,8 @@ void FurnaceGUI::syncSettings() {
|
||||||
clampSetting(settings.notePreviewBehavior,0,3);
|
clampSetting(settings.notePreviewBehavior,0,3);
|
||||||
clampSetting(settings.powerSave,0,1);
|
clampSetting(settings.powerSave,0,1);
|
||||||
clampSetting(settings.absorbInsInput,0,1);
|
clampSetting(settings.absorbInsInput,0,1);
|
||||||
|
clampSetting(settings.eventDelay,0,1);
|
||||||
|
clampSetting(settings.moveWindowTitle,0,1);
|
||||||
|
|
||||||
// keybinds
|
// keybinds
|
||||||
for (int i=0; i<GUI_ACTION_MAX; i++) {
|
for (int i=0; i<GUI_ACTION_MAX; i++) {
|
||||||
|
@ -1806,6 +1825,8 @@ void FurnaceGUI::commitSettings() {
|
||||||
e->setConf("notePreviewBehavior",settings.notePreviewBehavior);
|
e->setConf("notePreviewBehavior",settings.notePreviewBehavior);
|
||||||
e->setConf("powerSave",settings.powerSave);
|
e->setConf("powerSave",settings.powerSave);
|
||||||
e->setConf("absorbInsInput",settings.absorbInsInput);
|
e->setConf("absorbInsInput",settings.absorbInsInput);
|
||||||
|
e->setConf("eventDelay",settings.eventDelay);
|
||||||
|
e->setConf("moveWindowTitle",settings.moveWindowTitle);
|
||||||
|
|
||||||
// colors
|
// colors
|
||||||
for (int i=0; i<GUI_COLOR_MAX; i++) {
|
for (int i=0; i<GUI_COLOR_MAX; i++) {
|
||||||
|
@ -2317,6 +2338,9 @@ void FurnaceGUI::applyUISettings(bool updateFonts) {
|
||||||
|
|
||||||
ImGui::GetStyle()=sty;
|
ImGui::GetStyle()=sty;
|
||||||
|
|
||||||
|
ImGui::GetIO().ConfigInputTrickleEventQueue=settings.eventDelay;
|
||||||
|
ImGui::GetIO().ConfigWindowsMoveFromTitleBarOnly=settings.moveWindowTitle;
|
||||||
|
|
||||||
for (int i=0; i<256; i++) {
|
for (int i=0; i<256; i++) {
|
||||||
ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH];
|
ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH];
|
||||||
pitchGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w));
|
pitchGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w));
|
||||||
|
@ -2485,10 +2509,14 @@ void FurnaceGUI::applyUISettings(bool updateFonts) {
|
||||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||||
|
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".opli",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||||
|
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".opni",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||||
|
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".y12",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bnk",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bnk",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ff",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ff",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||||
|
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".opm",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||||
|
|
||||||
if (updateFonts) {
|
if (updateFonts) {
|
||||||
if (fileDialog!=NULL) delete fileDialog;
|
if (fileDialog!=NULL) delete fileDialog;
|
||||||
|
|
67
src/main.cpp
67
src/main.cpp
|
@ -60,7 +60,7 @@ bool displayEngineFailError=false;
|
||||||
|
|
||||||
std::vector<TAParam> params;
|
std::vector<TAParam> params;
|
||||||
|
|
||||||
bool pHelp(String) {
|
TAParamResult pHelp(String) {
|
||||||
printf("usage: furnace [params] [filename]\n"
|
printf("usage: furnace [params] [filename]\n"
|
||||||
"you may specify the following parameters:\n");
|
"you may specify the following parameters:\n");
|
||||||
for (auto& i: params) {
|
for (auto& i: params) {
|
||||||
|
@ -70,13 +70,13 @@ bool pHelp(String) {
|
||||||
printf(" -%s: %s\n",i.name.c_str(),i.desc.c_str());
|
printf(" -%s: %s\n",i.name.c_str(),i.desc.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return TA_PARAM_QUIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pAudio(String val) {
|
TAParamResult pAudio(String val) {
|
||||||
if (outName!="") {
|
if (outName!="") {
|
||||||
logE("can't use -audio and -output at the same time.");
|
logE("can't use -audio and -output at the same time.");
|
||||||
return false;
|
return TA_PARAM_ERROR;
|
||||||
}
|
}
|
||||||
if (val=="jack") {
|
if (val=="jack") {
|
||||||
e.setAudio(DIV_AUDIO_JACK);
|
e.setAudio(DIV_AUDIO_JACK);
|
||||||
|
@ -84,12 +84,12 @@ bool pAudio(String val) {
|
||||||
e.setAudio(DIV_AUDIO_SDL);
|
e.setAudio(DIV_AUDIO_SDL);
|
||||||
} else {
|
} else {
|
||||||
logE("invalid value for audio engine! valid values are: jack, sdl.");
|
logE("invalid value for audio engine! valid values are: jack, sdl.");
|
||||||
return false;
|
return TA_PARAM_ERROR;
|
||||||
}
|
}
|
||||||
return true;
|
return TA_PARAM_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pView(String val) {
|
TAParamResult pView(String val) {
|
||||||
if (val=="pattern") {
|
if (val=="pattern") {
|
||||||
e.setView(DIV_STATUS_PATTERN);
|
e.setView(DIV_STATUS_PATTERN);
|
||||||
} else if (val=="commands") {
|
} else if (val=="commands") {
|
||||||
|
@ -98,17 +98,17 @@ bool pView(String val) {
|
||||||
e.setView(DIV_STATUS_NOTHING);
|
e.setView(DIV_STATUS_NOTHING);
|
||||||
} else {
|
} else {
|
||||||
logE("invalid value for view type! valid values are: pattern, commands, nothing.");
|
logE("invalid value for view type! valid values are: pattern, commands, nothing.");
|
||||||
return false;
|
return TA_PARAM_ERROR;
|
||||||
}
|
}
|
||||||
return true;
|
return TA_PARAM_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pConsole(String val) {
|
TAParamResult pConsole(String val) {
|
||||||
consoleMode=true;
|
consoleMode=true;
|
||||||
return true;
|
return TA_PARAM_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pLogLevel(String val) {
|
TAParamResult pLogLevel(String val) {
|
||||||
if (val=="trace") {
|
if (val=="trace") {
|
||||||
logLevel=LOGLEVEL_TRACE;
|
logLevel=LOGLEVEL_TRACE;
|
||||||
} else if (val=="debug") {
|
} else if (val=="debug") {
|
||||||
|
@ -121,12 +121,12 @@ bool pLogLevel(String val) {
|
||||||
logLevel=LOGLEVEL_ERROR;
|
logLevel=LOGLEVEL_ERROR;
|
||||||
} else {
|
} else {
|
||||||
logE("invalid value for loglevel! valid values are: trace, debug, info, warning, error.");
|
logE("invalid value for loglevel! valid values are: trace, debug, info, warning, error.");
|
||||||
return false;
|
return TA_PARAM_ERROR;
|
||||||
}
|
}
|
||||||
return true;
|
return TA_PARAM_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pVersion(String) {
|
TAParamResult pVersion(String) {
|
||||||
printf("Furnace version " DIV_VERSION ".\n\n");
|
printf("Furnace version " DIV_VERSION ".\n\n");
|
||||||
printf("copyright (C) 2021-2022 tildearrow and contributors.\n");
|
printf("copyright (C) 2021-2022 tildearrow and contributors.\n");
|
||||||
printf("licensed under the GNU General Public License version 2 or later\n");
|
printf("licensed under the GNU General Public License version 2 or later\n");
|
||||||
|
@ -152,10 +152,10 @@ bool pVersion(String) {
|
||||||
printf("- puNES by FHorse (GPLv2)\n");
|
printf("- puNES by FHorse (GPLv2)\n");
|
||||||
printf("- reSID by Dag Lem (GPLv2)\n");
|
printf("- reSID by Dag Lem (GPLv2)\n");
|
||||||
printf("- Stella by Stella Team (GPLv2)\n");
|
printf("- Stella by Stella Team (GPLv2)\n");
|
||||||
return false;
|
return TA_PARAM_QUIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pWarranty(String) {
|
TAParamResult pWarranty(String) {
|
||||||
printf("This program is free software; you can redistribute it and/or\n"
|
printf("This program is free software; you can redistribute it and/or\n"
|
||||||
"modify it under the terms of the GNU General Public License\n"
|
"modify it under the terms of the GNU General Public License\n"
|
||||||
"as published by the Free Software Foundation; either version 2\n"
|
"as published by the Free Software Foundation; either version 2\n"
|
||||||
|
@ -169,10 +169,10 @@ bool pWarranty(String) {
|
||||||
"You should have received a copy of the GNU General Public License\n"
|
"You should have received a copy of the GNU General Public License\n"
|
||||||
"along with this program; if not, write to the Free Software\n"
|
"along with this program; if not, write to the Free Software\n"
|
||||||
"Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n");
|
"Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n");
|
||||||
return false;
|
return TA_PARAM_QUIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pLoops(String val) {
|
TAParamResult pLoops(String val) {
|
||||||
try {
|
try {
|
||||||
int count=std::stoi(val);
|
int count=std::stoi(val);
|
||||||
if (count<0) {
|
if (count<0) {
|
||||||
|
@ -182,12 +182,12 @@ bool pLoops(String val) {
|
||||||
}
|
}
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
logE("loop count shall be a number.");
|
logE("loop count shall be a number.");
|
||||||
return false;
|
return TA_PARAM_ERROR;
|
||||||
}
|
}
|
||||||
return true;
|
return TA_PARAM_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pOutMode(String val) {
|
TAParamResult pOutMode(String val) {
|
||||||
if (val=="one") {
|
if (val=="one") {
|
||||||
outMode=DIV_EXPORT_MODE_ONE;
|
outMode=DIV_EXPORT_MODE_ONE;
|
||||||
} else if (val=="persys") {
|
} else if (val=="persys") {
|
||||||
|
@ -196,21 +196,21 @@ bool pOutMode(String val) {
|
||||||
outMode=DIV_EXPORT_MODE_MANY_CHAN;
|
outMode=DIV_EXPORT_MODE_MANY_CHAN;
|
||||||
} else {
|
} else {
|
||||||
logE("invalid value for outmode! valid values are: one, persys and perchan.");
|
logE("invalid value for outmode! valid values are: one, persys and perchan.");
|
||||||
return false;
|
return TA_PARAM_ERROR;
|
||||||
}
|
}
|
||||||
return true;
|
return TA_PARAM_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pOutput(String val) {
|
TAParamResult pOutput(String val) {
|
||||||
outName=val;
|
outName=val;
|
||||||
e.setAudio(DIV_AUDIO_DUMMY);
|
e.setAudio(DIV_AUDIO_DUMMY);
|
||||||
return true;
|
return TA_PARAM_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pVGMOut(String val) {
|
TAParamResult pVGMOut(String val) {
|
||||||
vgmOutName=val;
|
vgmOutName=val;
|
||||||
e.setAudio(DIV_AUDIO_DUMMY);
|
e.setAudio(DIV_AUDIO_DUMMY);
|
||||||
return true;
|
return TA_PARAM_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needsValue(String param) {
|
bool needsValue(String param) {
|
||||||
|
@ -283,7 +283,16 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
for (size_t j=0; j<params.size(); j++) {
|
for (size_t j=0; j<params.size(); j++) {
|
||||||
if (params[j].name==arg || params[j].shortName==arg) {
|
if (params[j].name==arg || params[j].shortName==arg) {
|
||||||
if (!params[j].func(val)) return 1;
|
switch (params[j].func(val)) {
|
||||||
|
case TA_PARAM_ERROR:
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
case TA_PARAM_SUCCESS:
|
||||||
|
break;
|
||||||
|
case TA_PARAM_QUIT:
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,14 +43,20 @@ typedef std::string String;
|
||||||
|
|
||||||
typedef std::wstring WString;
|
typedef std::wstring WString;
|
||||||
|
|
||||||
|
enum TAParamResult {
|
||||||
|
TA_PARAM_ERROR=0,
|
||||||
|
TA_PARAM_SUCCESS,
|
||||||
|
TA_PARAM_QUIT
|
||||||
|
};
|
||||||
|
|
||||||
struct TAParam {
|
struct TAParam {
|
||||||
String shortName;
|
String shortName;
|
||||||
String name;
|
String name;
|
||||||
String valName;
|
String valName;
|
||||||
String desc;
|
String desc;
|
||||||
bool value;
|
bool value;
|
||||||
bool (*func)(String);
|
TAParamResult (*func)(String);
|
||||||
TAParam(const String& sn, const String& n, bool v, bool (*f)(String), const String& vn, const String& d):
|
TAParam(const String& sn, const String& n, bool v, TAParamResult (*f)(String), const String& vn, const String& d):
|
||||||
shortName(sn),
|
shortName(sn),
|
||||||
name(n),
|
name(n),
|
||||||
valName(vn),
|
valName(vn),
|
||||||
|
|
Loading…
Reference in a new issue