mirror of
https://github.com/tildearrow/furnace.git
synced 2024-12-04 18:27:25 +00:00
Merge branch 'master' of https://github.com/tildearrow/furnace into x1_010_bank
This commit is contained in:
commit
c69318b222
86 changed files with 1642 additions and 375 deletions
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -24,7 +24,7 @@ jobs:
|
||||||
#- { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 }
|
#- { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 }
|
||||||
- { name: 'macOS x86_64', os: macos-latest, arch: x86_64 }
|
- { name: 'macOS x86_64', os: macos-latest, arch: x86_64 }
|
||||||
- { name: 'macOS ARM', os: macos-latest, arch: arm64 }
|
- { name: 'macOS ARM', os: macos-latest, arch: arm64 }
|
||||||
- { name: 'Linux x86_64', os: ubuntu-18.04, arch: x86_64 }
|
- { name: 'Linux x86_64', os: ubuntu-20.04, arch: x86_64 }
|
||||||
#- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf }
|
#- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf }
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
|
|
16
TODO.md
Normal file
16
TODO.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# to-do for 0.6pre5
|
||||||
|
|
||||||
|
- tutorial
|
||||||
|
- ease-of-use improvements... ideas:
|
||||||
|
- preset compat flags
|
||||||
|
- setting to toggle the Choose a System screen on new project
|
||||||
|
- maybe reduced set of presets for the sake of simplicity
|
||||||
|
- a more preferable highlight/drag system
|
||||||
|
- some speed/intuitive workflow improvements that go a long way
|
||||||
|
- Had a hard time finding the docs on github and in Furnace's folder.
|
||||||
|
- make .pdf manual out of papers/doc/
|
||||||
|
- you're going too fast; please slow down
|
||||||
|
- break compatibility if it relieves complexity
|
||||||
|
- ins/wave/sample organization (folders and all)
|
||||||
|
- multi-key binds
|
||||||
|
- bug fixes
|
BIN
demos/arcade/Maximum_Overdrive_NamcoWSG.fur
Normal file
BIN
demos/arcade/Maximum_Overdrive_NamcoWSG.fur
Normal file
Binary file not shown.
BIN
demos/ay8910/Second_Addition.fur
Normal file
BIN
demos/ay8910/Second_Addition.fur
Normal file
Binary file not shown.
BIN
demos/c64/yeah!.fur
Normal file
BIN
demos/c64/yeah!.fur
Normal file
Binary file not shown.
BIN
demos/misc/BlueBolt_VIC20.fur
Normal file
BIN
demos/misc/BlueBolt_VIC20.fur
Normal file
Binary file not shown.
BIN
demos/misc/GreenIdeas_PET.fur
Normal file
BIN
demos/misc/GreenIdeas_PET.fur
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
demos/multichip/MegaMari-Cirno.fur
Normal file
BIN
demos/multichip/MegaMari-Cirno.fur
Normal file
Binary file not shown.
Binary file not shown.
BIN
demos/multichip/collab.fur
Normal file
BIN
demos/multichip/collab.fur
Normal file
Binary file not shown.
BIN
demos/multichip/one.fur
Normal file
BIN
demos/multichip/one.fur
Normal file
Binary file not shown.
74
extern/imgui_patched/imgui.cpp
vendored
74
extern/imgui_patched/imgui.cpp
vendored
|
@ -837,6 +837,8 @@ CODE
|
||||||
#include <stdint.h> // intptr_t
|
#include <stdint.h> // intptr_t
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "../../src/fileutils.h"
|
||||||
|
|
||||||
// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
|
// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
|
||||||
#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
|
#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
|
||||||
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
|
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
|
||||||
|
@ -12457,14 +12459,47 @@ void ImGui::ClearIniSettings()
|
||||||
g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
|
g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
|
bool ImGui::LoadIniSettingsFromDisk(const char* ini_filename, bool redundancy)
|
||||||
{
|
{
|
||||||
size_t file_data_size = 0;
|
size_t file_data_size = 0;
|
||||||
char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
|
char* file_data = NULL;
|
||||||
|
|
||||||
|
if (redundancy) {
|
||||||
|
char fileName[4096];
|
||||||
|
|
||||||
|
for (int i=0; i<5; i++) {
|
||||||
|
bool viable=false;
|
||||||
|
if (i>0) {
|
||||||
|
snprintf(fileName,4095,"%s.%d",ini_filename,i);
|
||||||
|
} else {
|
||||||
|
strncpy(fileName,ini_filename,4095);
|
||||||
|
}
|
||||||
|
file_data=(char*)ImFileLoadToMemory(fileName, "rb", &file_data_size);
|
||||||
|
if (!file_data) continue;
|
||||||
|
|
||||||
|
for (size_t j=0; j<file_data_size; j++) {
|
||||||
|
if (file_data[j]!='\r' && file_data[j]!='\n' && file_data[j]!=' ') {
|
||||||
|
viable=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!viable) {
|
||||||
|
IM_FREE(file_data);
|
||||||
|
file_data=NULL;
|
||||||
|
file_data_size=0;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
file_data=(char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
|
||||||
|
}
|
||||||
if (!file_data)
|
if (!file_data)
|
||||||
return;
|
return false;
|
||||||
LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
|
LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
|
||||||
IM_FREE(file_data);
|
IM_FREE(file_data);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero-tolerance, no error reporting, cheap .ini parsing
|
// Zero-tolerance, no error reporting, cheap .ini parsing
|
||||||
|
@ -12538,20 +12573,45 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
|
||||||
g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
|
g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
|
bool ImGui::SaveIniSettingsToDisk(const char* ini_filename, bool redundancy)
|
||||||
{
|
{
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *GImGui;
|
||||||
g.SettingsDirtyTimer = 0.0f;
|
g.SettingsDirtyTimer = 0.0f;
|
||||||
if (!ini_filename)
|
if (!ini_filename)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
|
if (redundancy) {
|
||||||
|
char oldPath[4096];
|
||||||
|
char newPath[4096];
|
||||||
|
|
||||||
|
if (fileExists(ini_filename)==1) {
|
||||||
|
for (int i=4; i>=0; i--) {
|
||||||
|
if (i>0) {
|
||||||
|
snprintf(oldPath,4095,"%s.%d",ini_filename,i);
|
||||||
|
} else {
|
||||||
|
strncpy(oldPath,ini_filename,4095);
|
||||||
|
}
|
||||||
|
snprintf(newPath,4095,"%s.%d",ini_filename,i+1);
|
||||||
|
|
||||||
|
if (i>=4) {
|
||||||
|
deleteFile(oldPath);
|
||||||
|
} else {
|
||||||
|
moveFiles(oldPath,newPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size_t ini_data_size = 0;
|
size_t ini_data_size = 0;
|
||||||
const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
|
const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
|
||||||
ImFileHandle f = ImFileOpen(ini_filename, "wt");
|
ImFileHandle f = ImFileOpen(ini_filename, "wt");
|
||||||
if (!f)
|
if (!f)
|
||||||
return;
|
return false;
|
||||||
ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
|
bool areEqual=ImFileWrite(ini_data, sizeof(char), ini_data_size, f)==ini_data_size;
|
||||||
|
IM_ASSERT_USER_ERROR(areEqual, "ImFileWrite failed to write file!");
|
||||||
ImFileClose(f);
|
ImFileClose(f);
|
||||||
|
if (!areEqual && redundancy) deleteFile(ini_filename);
|
||||||
|
return areEqual;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
|
// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
|
||||||
|
|
4
extern/imgui_patched/imgui.h
vendored
4
extern/imgui_patched/imgui.h
vendored
|
@ -950,9 +950,9 @@ namespace ImGui
|
||||||
// - The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini").
|
// - The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini").
|
||||||
// - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually.
|
// - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually.
|
||||||
// - Important: default value "imgui.ini" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables).
|
// - Important: default value "imgui.ini" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables).
|
||||||
IMGUI_API void LoadIniSettingsFromDisk(const char* ini_filename); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename).
|
IMGUI_API bool LoadIniSettingsFromDisk(const char* ini_filename, bool redundancy=false); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename).
|
||||||
IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source.
|
IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source.
|
||||||
IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext).
|
IMGUI_API bool SaveIniSettingsToDisk(const char* ini_filename, bool redundancy=false); // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext).
|
||||||
IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings.
|
IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings.
|
||||||
|
|
||||||
// Debug Utilities
|
// Debug Utilities
|
||||||
|
|
4
extern/nfd-modified/src/nfd_win.cpp
vendored
4
extern/nfd-modified/src/nfd_win.cpp
vendored
|
@ -204,7 +204,9 @@ static void CopyNFDCharToWChar( const nfdchar_t *inStr, wchar_t **outStr )
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
int inStrCharacterCount = static_cast<int>(NFDi_UTF8_Strlen(inStr));
|
int inStrCharacterCount = static_cast<int>(NFDi_UTF8_Strlen(inStr));
|
||||||
assert( ret == inStrCharacterCount );
|
if (ret!=inStrCharacterCount) {
|
||||||
|
logW("length does not match! %d != %d",ret,inStrCharacterCount);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
_NFD_UNUSED(ret);
|
_NFD_UNUSED(ret);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
# wavetable editor
|
# wavetable editor
|
||||||
|
|
||||||
Wavetable synthesizers, in context of Furnace, are sound sources that operate on extremely short n-bit PCM streams. By extremely short, no more than 256 bytes. This amount of space is nowhere near enough to store an actual sampled sound, it allows certain amount of freedom to define a waveform shape. As of Furnace 0.5.8, wavetable editor affects PC Engine, WonderSwan and channel 3 of Game Boy.
|
Wavetable synthesizers, in context of Furnace, are sound sources that operate on extremely short n-bit PCM streams. By extremely short, no more than 256 bytes. This amount of space is nowhere near enough to store an actual sampled sound, it allows certain amount of freedom to define a waveform shape. As of Furnace 0.6pre4, wavetable editor affects PC Engine, WonderSwan, Namco WSGs, Virtual Boy, Game.com, SCC, FDS, Seta X1-010, Konami Bubble System WSG, SNES, Amiga and channel 3 of Game Boy.
|
||||||
|
|
||||||
Furnace's wavetable editor is rather simple, you can draw the waveform using mouse or by pasting an MML bit stream in the input field. Maximum wave width (length) is 256 bytes, and maximum wave height (depth) is 256. NOTE: Game Boy, PCE, WonderSwan and Bubble System can handle max 32 byte waveforms, X1-010 can handle max 128 byte waveforms as of now, with 16-level height for GB, X1-010 Envelope, WS, Bubble System and N163, and 32-level height for PCE. If a larger wave is defined for these chips, it will be squashed to fit within the constraints of the chips.
|
Furnace's wavetable editor is rather simple, you can draw the waveform using mouse or by pasting an MML bit stream in the input field. Maximum wave width (length) is 256 bytes, and maximum wave height (depth) is 256. NOTE: Game Boy, PCE, WonderSwan, Namco WSG, N163, Game.com, Virtual Boy and Bubble System can handle max 32 byte waveforms, X1-010 can handle max 128 byte waveforms as of now, with 16-level height for GB, X1-010 Envelope, WS, Bubble System, SNES, Namco WSG and N163, 32-level height for PCE and 64-level height for Virtual Boy. If a larger wave is defined for these chips, it will be squashed to fit within the constraints of the chips.
|
||||||
|
|
||||||
|
Furnace's wavetable editor features multiple ways of creating desired waveform shape:
|
||||||
|
|
||||||
|
- Shape tab allows you to select a few predefined basic shapes and indirectly edit it via "Duty", "Exponent" and "XOR Point" sliders TODO: what the last two are doing? What is amplitude/phase for?)
|
||||||
|
- FM is for creating the waveform with frequency modulation synthesis principles: One can set carrier/modulation levels, frquency multiplier, connection between operators and FM waveforms of these operators.
|
||||||
|
- WaveTools allows user to fine-tune the waveform: scale said waveform in both X and Y axes, smoothen, amplify, normalize, convert to signed/unisgned, invert or even randomize the wavetable.
|
||||||
|
|
||||||
## wavetable synthesizer
|
## wavetable synthesizer
|
||||||
|
|
||||||
|
|
|
@ -69,10 +69,13 @@ hex | description
|
||||||
.. | ...
|
.. | ...
|
||||||
ef | preset delay 15
|
ef | preset delay 15
|
||||||
----|------------------------------------
|
----|------------------------------------
|
||||||
|
f4 | call symbol (16-bit index follows; only used internally)
|
||||||
|
f5 | jump to sub-block (address follows)
|
||||||
|
f6 | go to sub-block (32-bit offset follows)
|
||||||
f7 | full command (command and data follows)
|
f7 | full command (command and data follows)
|
||||||
f8 | go to sub-block (offset follows)
|
f8 | go to sub-block (16-bit offset follows)
|
||||||
f9 | return from sub-block
|
f9 | return from sub-block
|
||||||
fa | jump (offset follows)
|
fa | jump (address follows)
|
||||||
fb | set tick rate (4 bytes)
|
fb | set tick rate (4 bytes)
|
||||||
fc | wait (16-bit)
|
fc | wait (16-bit)
|
||||||
fd | wait (8-bit)
|
fd | wait (8-bit)
|
||||||
|
|
|
@ -32,6 +32,15 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
|
||||||
|
|
||||||
the format versions are:
|
the format versions are:
|
||||||
|
|
||||||
|
- 152: Furnace dev152
|
||||||
|
- 151: Furnace dev151
|
||||||
|
- 150: Furnace dev150
|
||||||
|
- 149: Furnace dev149
|
||||||
|
- 148: Furnace dev148
|
||||||
|
- 147: Furnace dev147
|
||||||
|
- 146: Furnace Pro (joke version)
|
||||||
|
- 145: Furnace dev145
|
||||||
|
- 144: Furnace dev144
|
||||||
- 143: Furnace 0.6pre4
|
- 143: Furnace 0.6pre4
|
||||||
- 142: Furnace dev142
|
- 142: Furnace dev142
|
||||||
- 141: Furnace Tournament Edition (for intro tune contest)
|
- 141: Furnace Tournament Edition (for intro tune contest)
|
||||||
|
|
|
@ -389,7 +389,7 @@ the sample map format:
|
||||||
```
|
```
|
||||||
size | description
|
size | description
|
||||||
-----|------------------------------------
|
-----|------------------------------------
|
||||||
2 | note to play
|
2 | note to play (>=152) or reserved
|
||||||
2 | sample to play
|
2 | sample to play
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,24 @@
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
#include "cmdStream.h"
|
#include "cmdStream.h"
|
||||||
|
#include "dispatch.h"
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
#include "../ta-log.h"
|
#include "../ta-log.h"
|
||||||
|
|
||||||
|
bool DivCSChannelState::doCall(unsigned int addr) {
|
||||||
|
if (callStackPos>=8) {
|
||||||
|
readPos=0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
callStack[callStackPos++]=readPos;
|
||||||
|
readPos=addr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void DivCSPlayer::cleanup() {
|
void DivCSPlayer::cleanup() {
|
||||||
delete b;
|
delete b;
|
||||||
}
|
}
|
||||||
|
@ -29,18 +43,25 @@ bool DivCSPlayer::tick() {
|
||||||
bool ticked=false;
|
bool ticked=false;
|
||||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||||
bool sendVolume=false;
|
bool sendVolume=false;
|
||||||
|
bool sendPitch=false;
|
||||||
if (chan[i].readPos==0) continue;
|
if (chan[i].readPos==0) continue;
|
||||||
|
|
||||||
ticked=true;
|
ticked=true;
|
||||||
|
|
||||||
chan[i].waitTicks--;
|
chan[i].waitTicks--;
|
||||||
while (chan[i].waitTicks<=0) {
|
while (chan[i].waitTicks<=0) {
|
||||||
stream.seek(chan[i].readPos,SEEK_SET);
|
if (!stream.seek(chan[i].readPos,SEEK_SET)) {
|
||||||
|
logE("%d: access violation! $%x",i,chan[i].readPos);
|
||||||
|
chan[i].readPos=0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
unsigned char next=stream.readC();
|
unsigned char next=stream.readC();
|
||||||
unsigned char command=0;
|
unsigned char command=0;
|
||||||
|
|
||||||
if (next<0xb3) { // note
|
if (next<0xb3) { // note
|
||||||
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,next-60));
|
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,(int)next-60));
|
||||||
|
chan[i].note=(int)next-60;
|
||||||
|
chan[i].vibratoPos=0;
|
||||||
} else if (next>=0xd0 && next<=0xdf) {
|
} else if (next>=0xd0 && next<=0xdf) {
|
||||||
command=fastCmds[next&15];
|
command=fastCmds[next&15];
|
||||||
} else if (next>=0xe0 && next<=0xef) { // preset delay
|
} else if (next>=0xe0 && next<=0xef) { // preset delay
|
||||||
|
@ -48,6 +69,7 @@ bool DivCSPlayer::tick() {
|
||||||
} else switch (next) {
|
} else switch (next) {
|
||||||
case 0xb4: // note on null
|
case 0xb4: // note on null
|
||||||
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL));
|
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL));
|
||||||
|
chan[i].vibratoPos=0;
|
||||||
break;
|
break;
|
||||||
case 0xb5: // note off
|
case 0xb5: // note off
|
||||||
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,i));
|
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,i));
|
||||||
|
@ -66,17 +88,46 @@ bool DivCSPlayer::tick() {
|
||||||
case 0xf7:
|
case 0xf7:
|
||||||
command=stream.readC();
|
command=stream.readC();
|
||||||
break;
|
break;
|
||||||
case 0xf8:
|
case 0xf8: {
|
||||||
logE("TODO: CALL");
|
unsigned int callAddr=chan[i].readPos+2+stream.readS();
|
||||||
|
if (!chan[i].doCall(callAddr)) {
|
||||||
|
logE("%d: (callb16) stack error!",i);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
case 0xf6: {
|
||||||
|
unsigned int callAddr=chan[i].readPos+4+stream.readI();
|
||||||
|
if (!chan[i].doCall(callAddr)) {
|
||||||
|
logE("%d: (callb32) stack error!",i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xf5: {
|
||||||
|
unsigned int callAddr=stream.readI();
|
||||||
|
if (!chan[i].doCall(callAddr)) {
|
||||||
|
logE("%d: (call) stack error!",i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xf4: {
|
||||||
|
logE("%d: (callsym) not supported here!",i);
|
||||||
|
chan[i].readPos=0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0xf9:
|
case 0xf9:
|
||||||
logE("TODO: RET");
|
if (!chan[i].callStackPos) {
|
||||||
|
logE("%d: (ret) stack error!",i);
|
||||||
|
chan[i].readPos=0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chan[i].readPos=chan[i].callStack[--chan[i].callStackPos];
|
||||||
break;
|
break;
|
||||||
case 0xfa:
|
case 0xfa:
|
||||||
logE("TODO: JMP");
|
chan[i].readPos=stream.readI();
|
||||||
break;
|
break;
|
||||||
case 0xfb:
|
case 0xfb:
|
||||||
logE("TODO: RATE");
|
logE("TODO: RATE");
|
||||||
|
stream.readI();
|
||||||
break;
|
break;
|
||||||
case 0xfc:
|
case 0xfc:
|
||||||
chan[i].waitTicks=(unsigned short)stream.readS();
|
chan[i].waitTicks=(unsigned short)stream.readS();
|
||||||
|
@ -88,6 +139,11 @@ bool DivCSPlayer::tick() {
|
||||||
chan[i].waitTicks=1;
|
chan[i].waitTicks=1;
|
||||||
break;
|
break;
|
||||||
case 0xff:
|
case 0xff:
|
||||||
|
chan[i].readPos=0;
|
||||||
|
logI("%d: stop",i);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logE("%d: illegal instruction $%.2x! $%.x",i,next,chan[i].readPos);
|
||||||
chan[i].readPos=0;
|
chan[i].readPos=0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -101,15 +157,18 @@ bool DivCSPlayer::tick() {
|
||||||
case DIV_CMD_INSTRUMENT:
|
case DIV_CMD_INSTRUMENT:
|
||||||
case DIV_CMD_HINT_VIBRATO_RANGE:
|
case DIV_CMD_HINT_VIBRATO_RANGE:
|
||||||
case DIV_CMD_HINT_VIBRATO_SHAPE:
|
case DIV_CMD_HINT_VIBRATO_SHAPE:
|
||||||
case DIV_CMD_HINT_PITCH:
|
|
||||||
case DIV_CMD_HINT_VOLUME:
|
case DIV_CMD_HINT_VOLUME:
|
||||||
|
case DIV_CMD_HINT_ARP_TIME:
|
||||||
arg0=(unsigned char)stream.readC();
|
arg0=(unsigned char)stream.readC();
|
||||||
break;
|
break;
|
||||||
|
case DIV_CMD_HINT_PITCH:
|
||||||
|
arg0=(signed char)stream.readC();
|
||||||
|
break;
|
||||||
case DIV_CMD_PANNING:
|
case DIV_CMD_PANNING:
|
||||||
case DIV_CMD_HINT_VIBRATO:
|
case DIV_CMD_HINT_VIBRATO:
|
||||||
case DIV_CMD_HINT_ARPEGGIO:
|
case DIV_CMD_HINT_ARPEGGIO:
|
||||||
case DIV_CMD_HINT_PORTA:
|
case DIV_CMD_HINT_PORTA:
|
||||||
arg0=(unsigned char)stream.readC();
|
arg0=(signed char)stream.readC();
|
||||||
arg1=(unsigned char)stream.readC();
|
arg1=(unsigned char)stream.readC();
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_PRE_PORTA:
|
case DIV_CMD_PRE_PORTA:
|
||||||
|
@ -120,6 +179,14 @@ bool DivCSPlayer::tick() {
|
||||||
case DIV_CMD_HINT_VOL_SLIDE:
|
case DIV_CMD_HINT_VOL_SLIDE:
|
||||||
arg0=(short)stream.readS();
|
arg0=(short)stream.readS();
|
||||||
break;
|
break;
|
||||||
|
case DIV_CMD_HINT_LEGATO:
|
||||||
|
arg0=(unsigned char)stream.readC();
|
||||||
|
if (arg0==0xff) {
|
||||||
|
arg0=DIV_NOTE_NULL;
|
||||||
|
} else {
|
||||||
|
arg0-=60;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case DIV_CMD_SAMPLE_MODE:
|
case DIV_CMD_SAMPLE_MODE:
|
||||||
case DIV_CMD_SAMPLE_FREQ:
|
case DIV_CMD_SAMPLE_FREQ:
|
||||||
case DIV_CMD_SAMPLE_BANK:
|
case DIV_CMD_SAMPLE_BANK:
|
||||||
|
@ -218,6 +285,29 @@ bool DivCSPlayer::tick() {
|
||||||
case DIV_CMD_HINT_VOL_SLIDE:
|
case DIV_CMD_HINT_VOL_SLIDE:
|
||||||
chan[i].volSpeed=arg0;
|
chan[i].volSpeed=arg0;
|
||||||
break;
|
break;
|
||||||
|
case DIV_CMD_HINT_PITCH:
|
||||||
|
chan[i].pitch=arg0;
|
||||||
|
sendPitch=true;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_HINT_VIBRATO:
|
||||||
|
chan[i].vibratoDepth=arg0;
|
||||||
|
chan[i].vibratoRate=arg1;
|
||||||
|
sendPitch=true;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_HINT_PORTA:
|
||||||
|
chan[i].portaTarget=arg0;
|
||||||
|
chan[i].portaSpeed=arg1;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_HINT_LEGATO:
|
||||||
|
chan[i].note=arg0;
|
||||||
|
e->dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
|
||||||
|
break;
|
||||||
|
case DIV_CMD_HINT_ARPEGGIO:
|
||||||
|
chan[i].arp=(((unsigned char)arg0)<<4)|(arg1&15);
|
||||||
|
break;
|
||||||
|
case DIV_CMD_HINT_ARP_TIME:
|
||||||
|
arpSpeed=arg0;
|
||||||
|
break;
|
||||||
default: // dispatch it
|
default: // dispatch it
|
||||||
e->dispatchCmd(DivCommand((DivDispatchCmds)command,i,arg0,arg1));
|
e->dispatchCmd(DivCommand((DivDispatchCmds)command,i,arg0,arg1));
|
||||||
break;
|
break;
|
||||||
|
@ -238,6 +328,37 @@ bool DivCSPlayer::tick() {
|
||||||
|
|
||||||
e->dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
|
e->dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sendPitch || chan[i].vibratoDepth!=0) {
|
||||||
|
if (chan[i].vibratoDepth>0) {
|
||||||
|
chan[i].vibratoPos+=chan[i].vibratoRate;
|
||||||
|
if (chan[i].vibratoPos>=64) chan[i].vibratoPos-=64;
|
||||||
|
}
|
||||||
|
e->dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(vibTable[chan[i].vibratoPos&63]*chan[i].vibratoDepth)/15));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chan[i].portaSpeed) {
|
||||||
|
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(e->song.linearPitch==2?e->song.pitchSlideSpeed:1),chan[i].portaTarget));
|
||||||
|
}
|
||||||
|
if (chan[i].arp && !chan[i].portaSpeed) {
|
||||||
|
if (chan[i].arpTicks==0) {
|
||||||
|
switch (chan[i].arpStage) {
|
||||||
|
case 0:
|
||||||
|
e->dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
e->dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp>>4)));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
e->dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp&15)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chan[i].arpStage++;
|
||||||
|
if (chan[i].arpStage>=3) chan[i].arpStage=0;
|
||||||
|
chan[i].arpTicks=arpSpeed;
|
||||||
|
}
|
||||||
|
chan[i].arpTicks--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ticked;
|
return ticked;
|
||||||
|
@ -273,6 +394,12 @@ bool DivCSPlayer::init() {
|
||||||
chan[i].volume=chan[i].volMax;
|
chan[i].volume=chan[i].volMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<64; i++) {
|
||||||
|
vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI));
|
||||||
|
}
|
||||||
|
|
||||||
|
arpSpeed=1;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,14 +29,41 @@ struct DivCSChannelState {
|
||||||
unsigned int readPos;
|
unsigned int readPos;
|
||||||
int waitTicks;
|
int waitTicks;
|
||||||
|
|
||||||
|
int note, pitch;
|
||||||
int volume, volMax, volSpeed;
|
int volume, volMax, volSpeed;
|
||||||
|
int vibratoDepth, vibratoRate, vibratoPos;
|
||||||
|
int portaTarget, portaSpeed;
|
||||||
|
unsigned char arp, arpStage, arpTicks;
|
||||||
|
|
||||||
|
unsigned int callStack[8];
|
||||||
|
unsigned char callStackPos;
|
||||||
|
|
||||||
|
struct TraceEntry {
|
||||||
|
unsigned int addr;
|
||||||
|
unsigned char length;
|
||||||
|
unsigned char data[11];
|
||||||
|
} trace[32];
|
||||||
|
unsigned char tracePos;
|
||||||
|
|
||||||
|
bool doCall(unsigned int addr);
|
||||||
|
|
||||||
DivCSChannelState():
|
DivCSChannelState():
|
||||||
readPos(0),
|
readPos(0),
|
||||||
waitTicks(0),
|
waitTicks(0),
|
||||||
|
note(-1),
|
||||||
|
pitch(0),
|
||||||
volume(0x7f00),
|
volume(0x7f00),
|
||||||
volMax(0),
|
volMax(0),
|
||||||
volSpeed(0) {}
|
volSpeed(0),
|
||||||
|
vibratoDepth(0),
|
||||||
|
vibratoRate(0),
|
||||||
|
vibratoPos(0),
|
||||||
|
portaTarget(0),
|
||||||
|
portaSpeed(0),
|
||||||
|
arp(0),
|
||||||
|
arpStage(0),
|
||||||
|
arpTicks(0),
|
||||||
|
callStackPos(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class DivCSPlayer {
|
class DivCSPlayer {
|
||||||
|
@ -46,6 +73,9 @@ class DivCSPlayer {
|
||||||
DivCSChannelState chan[DIV_MAX_CHANS];
|
DivCSChannelState chan[DIV_MAX_CHANS];
|
||||||
unsigned char fastDelays[16];
|
unsigned char fastDelays[16];
|
||||||
unsigned char fastCmds[16];
|
unsigned char fastCmds[16];
|
||||||
|
unsigned char arpSpeed;
|
||||||
|
|
||||||
|
short vibTable[64];
|
||||||
public:
|
public:
|
||||||
void cleanup();
|
void cleanup();
|
||||||
bool tick();
|
bool tick();
|
||||||
|
|
|
@ -23,21 +23,54 @@
|
||||||
#include "../fileutils.h"
|
#include "../fileutils.h"
|
||||||
#include <fmt/printf.h>
|
#include <fmt/printf.h>
|
||||||
|
|
||||||
bool DivConfig::save(const char* path) {
|
#define REDUNDANCY_NUM_ATTEMPTS 5
|
||||||
|
#define CHECK_BUF_SIZE 8192
|
||||||
|
|
||||||
|
bool DivConfig::save(const char* path, bool redundancy) {
|
||||||
|
if (redundancy) {
|
||||||
|
char oldPath[4096];
|
||||||
|
char newPath[4096];
|
||||||
|
|
||||||
|
if (fileExists(path)==1) {
|
||||||
|
logD("rotating config files...");
|
||||||
|
for (int i=4; i>=0; i--) {
|
||||||
|
if (i>0) {
|
||||||
|
snprintf(oldPath,4095,"%s.%d",path,i);
|
||||||
|
} else {
|
||||||
|
strncpy(oldPath,path,4095);
|
||||||
|
}
|
||||||
|
snprintf(newPath,4095,"%s.%d",path,i+1);
|
||||||
|
|
||||||
|
if (i>=4) {
|
||||||
|
logV("remove %s",oldPath);
|
||||||
|
deleteFile(oldPath);
|
||||||
|
} else {
|
||||||
|
logV("move %s to %s",oldPath,newPath);
|
||||||
|
moveFiles(oldPath,newPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logD("opening config for write: %s",path);
|
||||||
FILE* f=ps_fopen(path,"wb");
|
FILE* f=ps_fopen(path,"wb");
|
||||||
if (f==NULL) {
|
if (f==NULL) {
|
||||||
logW("could not write config file! %s",strerror(errno));
|
logW("could not write config file! %s",strerror(errno));
|
||||||
|
reportError(fmt::sprintf("could not write config file! %s",strerror(errno)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (auto& i: conf) {
|
for (auto& i: conf) {
|
||||||
String toWrite=fmt::sprintf("%s=%s\n",i.first,i.second);
|
String toWrite=fmt::sprintf("%s=%s\n",i.first,i.second);
|
||||||
if (fwrite(toWrite.c_str(),1,toWrite.size(),f)!=toWrite.size()) {
|
if (fwrite(toWrite.c_str(),1,toWrite.size(),f)!=toWrite.size()) {
|
||||||
logW("could not write config file! %s",strerror(errno));
|
logW("could not write config file! %s",strerror(errno));
|
||||||
|
reportError(fmt::sprintf("could not write config file! %s",strerror(errno)));
|
||||||
|
logV("removing config file");
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
deleteFile(path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
logD("config file written successfully.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +96,7 @@ void DivConfig::parseLine(const char* line) {
|
||||||
String value="";
|
String value="";
|
||||||
bool keyOrValue=false;
|
bool keyOrValue=false;
|
||||||
for (const char* i=line; *i; i++) {
|
for (const char* i=line; *i; i++) {
|
||||||
|
if (*i=='\r') continue;
|
||||||
if (*i=='\n') continue;
|
if (*i=='\n') continue;
|
||||||
if (keyOrValue) {
|
if (keyOrValue) {
|
||||||
value+=*i;
|
value+=*i;
|
||||||
|
@ -79,17 +113,94 @@ void DivConfig::parseLine(const char* line) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DivConfig::loadFromFile(const char* path, bool createOnFail) {
|
bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundancy) {
|
||||||
char line[4096];
|
char line[4096];
|
||||||
FILE* f=ps_fopen(path,"rb");
|
logD("opening config for read: %s",path);
|
||||||
|
|
||||||
|
FILE* f=NULL;
|
||||||
|
|
||||||
|
if (redundancy) {
|
||||||
|
unsigned char* readBuf=new unsigned char[CHECK_BUF_SIZE];
|
||||||
|
size_t readBufLen=0;
|
||||||
|
for (int i=0; i<REDUNDANCY_NUM_ATTEMPTS; i++) {
|
||||||
|
bool viable=false;
|
||||||
|
if (i>0) {
|
||||||
|
snprintf(line,4095,"%s.%d",path,i);
|
||||||
|
} else {
|
||||||
|
strncpy(line,path,4095);
|
||||||
|
}
|
||||||
|
logV("trying: %s",line);
|
||||||
|
|
||||||
|
// try to open config
|
||||||
|
f=ps_fopen(line,"rb");
|
||||||
|
// check whether we could open it
|
||||||
if (f==NULL) {
|
if (f==NULL) {
|
||||||
|
logV("fopen(): %s",strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether there's something
|
||||||
|
while (!feof(f)) {
|
||||||
|
readBufLen=fread(readBuf,1,CHECK_BUF_SIZE,f);
|
||||||
|
if (ferror(f)) {
|
||||||
|
logV("fread(): %s",strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t j=0; j<readBufLen; j++) {
|
||||||
|
if (readBuf[j]!='\r' && readBuf[j]!='\n' && readBuf[j]!=' ') {
|
||||||
|
viable=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viable) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's something
|
||||||
|
if (viable) {
|
||||||
|
if (fseek(f,0,SEEK_SET)==-1) {
|
||||||
|
logV("fseek(): %s",strerror(errno));
|
||||||
|
viable=false;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// close it (because there's nothing)
|
||||||
|
fclose(f);
|
||||||
|
f=NULL;
|
||||||
|
}
|
||||||
|
delete[] readBuf;
|
||||||
|
|
||||||
|
// we couldn't read at all
|
||||||
|
if (f==NULL) {
|
||||||
|
logD("config does not exist");
|
||||||
if (createOnFail) {
|
if (createOnFail) {
|
||||||
logI("creating default config.");
|
logI("creating default config.");
|
||||||
return save(path);
|
reportError(fmt::sprintf("Creating default config: %s",strerror(errno)));
|
||||||
|
return save(path,redundancy);
|
||||||
} else {
|
} else {
|
||||||
|
reportError(fmt::sprintf("COULD NOT LOAD CONFIG %s",strerror(errno)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
f=ps_fopen(path,"rb");
|
||||||
|
if (f==NULL) {
|
||||||
|
logD("config does not exist");
|
||||||
|
if (createOnFail) {
|
||||||
|
logI("creating default config.");
|
||||||
|
reportError(fmt::sprintf("Creating default config: %s",strerror(errno)));
|
||||||
|
return save(path);
|
||||||
|
} else {
|
||||||
|
reportError(fmt::sprintf("COULD NOT LOAD CONFIG %s",strerror(errno)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
logI("loading config.");
|
logI("loading config.");
|
||||||
while (!feof(f)) {
|
while (!feof(f)) {
|
||||||
if (fgets(line,4095,f)==NULL) {
|
if (fgets(line,4095,f)==NULL) {
|
||||||
|
@ -97,6 +208,7 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail) {
|
||||||
}
|
}
|
||||||
parseLine(line);
|
parseLine(line);
|
||||||
}
|
}
|
||||||
|
logD("end of file (%s)",strerror(errno));
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -175,6 +287,33 @@ String DivConfig::getString(String key, String fallback) const {
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<int> DivConfig::getIntList(String key, std::initializer_list<int> fallback) const {
|
||||||
|
String next;
|
||||||
|
std::vector<int> ret;
|
||||||
|
try {
|
||||||
|
String val=conf.at(key);
|
||||||
|
|
||||||
|
for (char i: val) {
|
||||||
|
if (i==',') {
|
||||||
|
int num=std::stoi(next);
|
||||||
|
ret.push_back(num);
|
||||||
|
next="";
|
||||||
|
} else {
|
||||||
|
next+=i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!next.empty()) {
|
||||||
|
int num=std::stoi(next);
|
||||||
|
ret.push_back(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
} catch (std::out_of_range& e) {
|
||||||
|
} catch (std::invalid_argument& e) {
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
bool DivConfig::has(String key) const {
|
bool DivConfig::has(String key) const {
|
||||||
try {
|
try {
|
||||||
String test=conf.at(key);
|
String test=conf.at(key);
|
||||||
|
@ -212,6 +351,17 @@ void DivConfig::set(String key, String value) {
|
||||||
conf[key]=value;
|
conf[key]=value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DivConfig::set(String key, const std::vector<int>& value) {
|
||||||
|
String val;
|
||||||
|
bool comma=false;
|
||||||
|
for (int i: value) {
|
||||||
|
if (comma) val+=',';
|
||||||
|
val+=fmt::sprintf("%d",i);
|
||||||
|
comma=true;
|
||||||
|
}
|
||||||
|
conf[key]=val;
|
||||||
|
}
|
||||||
|
|
||||||
bool DivConfig::remove(String key) {
|
bool DivConfig::remove(String key) {
|
||||||
return conf.erase(key);
|
return conf.erase(key);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
|
|
||||||
#include "../ta-utils.h"
|
#include "../ta-utils.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
class DivConfig {
|
class DivConfig {
|
||||||
std::map<String,String> conf;
|
std::map<String,String> conf;
|
||||||
|
@ -30,10 +32,10 @@ class DivConfig {
|
||||||
// config loading/saving
|
// config loading/saving
|
||||||
bool loadFromMemory(const char* buf);
|
bool loadFromMemory(const char* buf);
|
||||||
bool loadFromBase64(const char* buf);
|
bool loadFromBase64(const char* buf);
|
||||||
bool loadFromFile(const char* path, bool createOnFail=true);
|
bool loadFromFile(const char* path, bool createOnFail=true, bool redundancy=false);
|
||||||
String toString();
|
String toString();
|
||||||
String toBase64();
|
String toBase64();
|
||||||
bool save(const char* path);
|
bool save(const char* path, bool redundancy=false);
|
||||||
|
|
||||||
// get the map
|
// get the map
|
||||||
const std::map<String,String>& configMap();
|
const std::map<String,String>& configMap();
|
||||||
|
@ -44,6 +46,7 @@ class DivConfig {
|
||||||
float getFloat(String key, float fallback) const;
|
float getFloat(String key, float fallback) const;
|
||||||
double getDouble(String key, double fallback) const;
|
double getDouble(String key, double fallback) const;
|
||||||
String getString(String key, String fallback) const;
|
String getString(String key, String fallback) const;
|
||||||
|
std::vector<int> getIntList(String key, std::initializer_list<int> fallback) const;
|
||||||
|
|
||||||
// check for existence
|
// check for existence
|
||||||
bool has(String key) const;
|
bool has(String key) const;
|
||||||
|
@ -55,6 +58,7 @@ class DivConfig {
|
||||||
void set(String key, double value);
|
void set(String key, double value);
|
||||||
void set(String key, const char* value);
|
void set(String key, const char* value);
|
||||||
void set(String key, String value);
|
void set(String key, String value);
|
||||||
|
void set(String key, const std::vector<int>& value);
|
||||||
|
|
||||||
// remove a config value
|
// remove a config value
|
||||||
bool remove(String key);
|
bool remove(String key);
|
||||||
|
|
|
@ -110,12 +110,12 @@ void DivEngine::initConfDir() {
|
||||||
|
|
||||||
bool DivEngine::saveConf() {
|
bool DivEngine::saveConf() {
|
||||||
configFile=configPath+String(CONFIG_FILE);
|
configFile=configPath+String(CONFIG_FILE);
|
||||||
return conf.save(configFile.c_str());
|
return conf.save(configFile.c_str(),true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DivEngine::loadConf() {
|
bool DivEngine::loadConf() {
|
||||||
configFile=configPath+String(CONFIG_FILE);
|
configFile=configPath+String(CONFIG_FILE);
|
||||||
return conf.loadFromFile(configFile.c_str());
|
return conf.loadFromFile(configFile.c_str(),true,true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DivEngine::getConfBool(String key, bool fallback) {
|
bool DivEngine::getConfBool(String key, bool fallback) {
|
||||||
|
|
|
@ -229,6 +229,8 @@ enum DivDispatchCmds {
|
||||||
DIV_CMD_ES5506_ENVELOPE_K2RAMP, // (ramp, slowdown)
|
DIV_CMD_ES5506_ENVELOPE_K2RAMP, // (ramp, slowdown)
|
||||||
DIV_CMD_ES5506_PAUSE, // (value)
|
DIV_CMD_ES5506_PAUSE, // (value)
|
||||||
|
|
||||||
|
DIV_CMD_HINT_ARP_TIME, // (value)
|
||||||
|
|
||||||
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
|
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
|
||||||
|
|
||||||
DIV_CMD_MAX
|
DIV_CMD_MAX
|
||||||
|
|
|
@ -418,6 +418,7 @@ void writePackedCommandValues(SafeWriter* w, const DivCommand& c) {
|
||||||
case DIV_CMD_AMIGA_PM:
|
case DIV_CMD_AMIGA_PM:
|
||||||
case DIV_CMD_MACRO_OFF:
|
case DIV_CMD_MACRO_OFF:
|
||||||
case DIV_CMD_MACRO_ON:
|
case DIV_CMD_MACRO_ON:
|
||||||
|
case DIV_CMD_HINT_ARP_TIME:
|
||||||
w->writeC(1); // length
|
w->writeC(1); // length
|
||||||
w->writeC(c.value);
|
w->writeC(c.value);
|
||||||
break;
|
break;
|
||||||
|
@ -2440,6 +2441,16 @@ void DivEngine::stop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset all chan oscs
|
||||||
|
for (int i=0; i<chans; i++) {
|
||||||
|
DivDispatchOscBuffer* buf=disCont[dispatchOfChan[i]].dispatch->getOscBuffer(dispatchChanOfChan[i]);
|
||||||
|
if (buf!=NULL) {
|
||||||
|
memset(buf->data,0,65536*sizeof(short));
|
||||||
|
buf->needle=0;
|
||||||
|
buf->readNeedle=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2657,10 +2668,17 @@ void DivEngine::previewSampleNoLock(int sample, int note, int pStart, int pEnd)
|
||||||
if (rate<=0) rate=song.sample[sample]->centerRate;
|
if (rate<=0) rate=song.sample[sample]->centerRate;
|
||||||
}
|
}
|
||||||
if (rate<100) rate=100;
|
if (rate<100) rate=100;
|
||||||
|
double rateOrig=rate;
|
||||||
|
sPreview.rateMul=1;
|
||||||
|
while (sPreview.rateMul<0x40000000 && rate<got.rate) {
|
||||||
|
sPreview.rateMul<<=1;
|
||||||
|
rate*=2.0;
|
||||||
|
}
|
||||||
blip_set_rates(samp_bb,rate,got.rate);
|
blip_set_rates(samp_bb,rate,got.rate);
|
||||||
samp_prevSample=0;
|
samp_prevSample=0;
|
||||||
sPreview.rate=rate;
|
sPreview.rate=rateOrig;
|
||||||
sPreview.pos=(sPreview.pBegin>=0)?sPreview.pBegin:0;
|
sPreview.pos=(sPreview.pBegin>=0)?sPreview.pBegin:0;
|
||||||
|
sPreview.posSub=0;
|
||||||
sPreview.sample=sample;
|
sPreview.sample=sample;
|
||||||
sPreview.wave=-1;
|
sPreview.wave=-1;
|
||||||
sPreview.dir=false;
|
sPreview.dir=false;
|
||||||
|
@ -2685,10 +2703,17 @@ void DivEngine::previewWaveNoLock(int wave, int note) {
|
||||||
blip_clear(samp_bb);
|
blip_clear(samp_bb);
|
||||||
double rate=song.wave[wave]->len*((song.tuning*0.0625)*pow(2.0,(double)(note+3)/12.0));
|
double rate=song.wave[wave]->len*((song.tuning*0.0625)*pow(2.0,(double)(note+3)/12.0));
|
||||||
if (rate<100) rate=100;
|
if (rate<100) rate=100;
|
||||||
|
double rateOrig=rate;
|
||||||
|
sPreview.rateMul=1;
|
||||||
|
while (sPreview.rateMul<0x40000000 && rate<got.rate) {
|
||||||
|
sPreview.rateMul<<=1;
|
||||||
|
rate*=2.0;
|
||||||
|
}
|
||||||
blip_set_rates(samp_bb,rate,got.rate);
|
blip_set_rates(samp_bb,rate,got.rate);
|
||||||
samp_prevSample=0;
|
samp_prevSample=0;
|
||||||
sPreview.rate=rate;
|
sPreview.rate=rateOrig;
|
||||||
sPreview.pos=0;
|
sPreview.pos=0;
|
||||||
|
sPreview.posSub=0;
|
||||||
sPreview.sample=-1;
|
sPreview.sample=-1;
|
||||||
sPreview.wave=wave;
|
sPreview.wave=wave;
|
||||||
sPreview.dir=false;
|
sPreview.dir=false;
|
||||||
|
|
|
@ -53,8 +53,8 @@
|
||||||
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
|
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
|
||||||
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
|
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
|
||||||
|
|
||||||
#define DIV_VERSION "dev145"
|
#define DIV_VERSION "dev153"
|
||||||
#define DIV_ENGINE_VERSION 145
|
#define DIV_ENGINE_VERSION 153
|
||||||
// for imports
|
// for imports
|
||||||
#define DIV_VERSION_MOD 0xff01
|
#define DIV_VERSION_MOD 0xff01
|
||||||
#define DIV_VERSION_FC 0xff02
|
#define DIV_VERSION_FC 0xff02
|
||||||
|
@ -414,6 +414,7 @@ class DivEngine {
|
||||||
int wave;
|
int wave;
|
||||||
int pos;
|
int pos;
|
||||||
int pBegin, pEnd;
|
int pBegin, pEnd;
|
||||||
|
int rateMul, posSub;
|
||||||
bool dir;
|
bool dir;
|
||||||
SamplePreview():
|
SamplePreview():
|
||||||
rate(0.0),
|
rate(0.0),
|
||||||
|
@ -422,6 +423,8 @@ class DivEngine {
|
||||||
pos(0),
|
pos(0),
|
||||||
pBegin(-1),
|
pBegin(-1),
|
||||||
pEnd(-1),
|
pEnd(-1),
|
||||||
|
rateMul(1),
|
||||||
|
posSub(0),
|
||||||
dir(false) {}
|
dir(false) {}
|
||||||
} sPreview;
|
} sPreview;
|
||||||
|
|
||||||
|
|
|
@ -696,6 +696,13 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
ds.wave.push_back(wave);
|
ds.wave.push_back(wave);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sometimes there's a single length 0 wavetable in the file. I don't know why.
|
||||||
|
if (ds.waveLen==1) {
|
||||||
|
if (ds.wave[0]->len==0) {
|
||||||
|
ds.clearWavetables();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logV("%x",reader.tell());
|
logV("%x",reader.tell());
|
||||||
|
@ -850,6 +857,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
sample->rate=22050;
|
sample->rate=22050;
|
||||||
if (ds.version>=0x0b) {
|
if (ds.version>=0x0b) {
|
||||||
sample->rate=fileToDivRate(reader.readC());
|
sample->rate=fileToDivRate(reader.readC());
|
||||||
|
sample->centerRate=sample->rate;
|
||||||
pitch=reader.readC();
|
pitch=reader.readC();
|
||||||
vol=reader.readC();
|
vol=reader.readC();
|
||||||
}
|
}
|
||||||
|
@ -874,24 +882,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
// what the hell man...
|
// what the hell man...
|
||||||
cutStart=reader.readI();
|
cutStart=reader.readI();
|
||||||
cutEnd=reader.readI();
|
cutEnd=reader.readI();
|
||||||
if (cutStart<0 || cutStart>length) {
|
logV("cutStart: %d cutEnd: %d",cutStart,cutEnd);
|
||||||
logE("cutStart is out of range! (%d)",cutStart);
|
|
||||||
lastError="file is corrupt or unreadable at samples";
|
|
||||||
delete[] file;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cutEnd<0 || cutEnd>length) {
|
|
||||||
logE("cutEnd is out of range! (%d)",cutEnd);
|
|
||||||
lastError="file is corrupt or unreadable at samples";
|
|
||||||
delete[] file;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cutEnd<cutStart) {
|
|
||||||
logE("cutEnd %d is before cutStart %d. what's going on?",cutEnd,cutStart);
|
|
||||||
lastError="file is corrupt or unreadable at samples";
|
|
||||||
delete[] file;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (length>0) {
|
if (length>0) {
|
||||||
if (ds.version>0x08) {
|
if (ds.version>0x08) {
|
||||||
|
@ -904,19 +895,6 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
reader.read(data,length*2);
|
reader.read(data,length*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ds.version>0x1b) {
|
|
||||||
if (cutStart!=0 || cutEnd!=length) {
|
|
||||||
// cut data
|
|
||||||
short* newData=new short[cutEnd-cutStart];
|
|
||||||
memcpy(newData,&data[cutStart],(cutEnd-cutStart)*sizeof(short));
|
|
||||||
delete[] data;
|
|
||||||
data=newData;
|
|
||||||
length=cutEnd-cutStart;
|
|
||||||
cutStart=0;
|
|
||||||
cutEnd=length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef TA_BIG_ENDIAN
|
#ifdef TA_BIG_ENDIAN
|
||||||
// convert to big-endian
|
// convert to big-endian
|
||||||
for (int pos=0; pos<length; pos++) {
|
for (int pos=0; pos<length; pos++) {
|
||||||
|
@ -924,27 +902,76 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (pitch!=5) {
|
int scaledLen=ceil((double)length/samplePitches[pitch]);
|
||||||
|
|
||||||
|
if (scaledLen>0) {
|
||||||
|
// resample
|
||||||
logD("%d: scaling from %d...",i,pitch);
|
logD("%d: scaling from %d...",i,pitch);
|
||||||
}
|
|
||||||
|
|
||||||
// render data
|
short* newData=new short[scaledLen];
|
||||||
if (!sample->init((double)length/samplePitches[pitch])) {
|
memset(newData,0,scaledLen*sizeof(short));
|
||||||
logE("%d: error while initializing sample!",i);
|
int k=0;
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int k=0;
|
|
||||||
float mult=(float)(vol)/50.0f;
|
float mult=(float)(vol)/50.0f;
|
||||||
for (double j=0; j<length; j+=samplePitches[pitch]) {
|
for (double j=0; j<length; j+=samplePitches[pitch]) {
|
||||||
if (k>=sample->samples) {
|
if (k>=scaledLen) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
|
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
|
||||||
float next=(float)(data[(unsigned int)j]-0x80)*mult;
|
float next=(float)(data[(unsigned int)j]-0x80)*mult;
|
||||||
sample->data8[k++]=fmin(fmax(next,-128),127);
|
newData[k++]=fmin(fmax(next,-128),127);
|
||||||
} else {
|
} else {
|
||||||
float next=(float)data[(unsigned int)j]*mult;
|
float next=(float)data[(unsigned int)j]*mult;
|
||||||
sample->data16[k++]=fmin(fmax(next,-32768),32767);
|
newData[k++]=fmin(fmax(next,-32768),32767);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] data;
|
||||||
|
data=newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
logV("length: %d. scaledLen: %d.",length,scaledLen);
|
||||||
|
|
||||||
|
if (ds.version>=0x1b) {
|
||||||
|
if (cutStart<0 || cutStart>scaledLen) {
|
||||||
|
logE("cutStart is out of range! (%d, scaledLen: %d)",cutStart,scaledLen);
|
||||||
|
lastError="file is corrupt or unreadable at samples";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cutEnd<0 || cutEnd>scaledLen) {
|
||||||
|
logE("cutEnd is out of range! (%d, scaledLen: %d)",cutEnd,scaledLen);
|
||||||
|
lastError="file is corrupt or unreadable at samples";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cutEnd<cutStart) {
|
||||||
|
logE("cutEnd %d is before cutStart %d. what's going on?",cutEnd,cutStart);
|
||||||
|
lastError="file is corrupt or unreadable at samples";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cutStart!=0 || cutEnd!=scaledLen) {
|
||||||
|
// cut data
|
||||||
|
short* newData=new short[cutEnd-cutStart];
|
||||||
|
memcpy(newData,&data[cutStart],(cutEnd-cutStart)*sizeof(short));
|
||||||
|
delete[] data;
|
||||||
|
data=newData;
|
||||||
|
scaledLen=cutEnd-cutStart;
|
||||||
|
cutStart=0;
|
||||||
|
cutEnd=scaledLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy data
|
||||||
|
if (!sample->init(scaledLen)) {
|
||||||
|
logE("%d: error while initializing sample!",i);
|
||||||
|
} else {
|
||||||
|
for (int i=0; i<scaledLen; i++) {
|
||||||
|
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
|
||||||
|
sample->data8[i]=data[i];
|
||||||
|
} else {
|
||||||
|
sample->data16[i]=data[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2688,6 +2715,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SrgaPCM slide compat
|
||||||
|
if (ds.version<153) {
|
||||||
|
for (int i=0; i<ds.systemLen; i++) {
|
||||||
|
if (ds.system[i]==DIV_SYSTEM_SEGAPCM || ds.system[i]==DIV_SYSTEM_SEGAPCM_COMPAT) {
|
||||||
|
ds.systemFlags[i].set("oldSlides",true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (active) quitDispatch();
|
if (active) quitDispatch();
|
||||||
BUSY_BEGIN_SOFT;
|
BUSY_BEGIN_SOFT;
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
|
@ -5183,7 +5219,6 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
for (int i=0; i<song.insLen; i++) {
|
for (int i=0; i<song.insLen; i++) {
|
||||||
DivInstrument* ins=song.ins[i];
|
DivInstrument* ins=song.ins[i];
|
||||||
insPtr.push_back(w->tell());
|
insPtr.push_back(w->tell());
|
||||||
logV("writing instrument %d...",i);
|
|
||||||
ins->putInsData2(w,false);
|
ins->putInsData2(w,false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -214,7 +214,6 @@ bool DivInstrumentSNES::operator==(const DivInstrumentSNES& other) {
|
||||||
#undef _C
|
#undef _C
|
||||||
|
|
||||||
#define FEATURE_BEGIN(x) \
|
#define FEATURE_BEGIN(x) \
|
||||||
logV("- %s",x); \
|
|
||||||
w->write(x,2); \
|
w->write(x,2); \
|
||||||
size_t featStartSeek=w->tell(); \
|
size_t featStartSeek=w->tell(); \
|
||||||
w->writeS(0);
|
w->writeS(0);
|
||||||
|
@ -2099,6 +2098,12 @@ void DivInstrument::readFeatureSM(SafeReader& reader, short version) {
|
||||||
amiga.noteMap[note].freq=reader.readS();
|
amiga.noteMap[note].freq=reader.readS();
|
||||||
amiga.noteMap[note].map=reader.readS();
|
amiga.noteMap[note].map=reader.readS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version<152) {
|
||||||
|
for (int note=0; note<120; note++) {
|
||||||
|
amiga.noteMap[note].freq=note;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
READ_FEAT_END;
|
READ_FEAT_END;
|
||||||
|
@ -2966,6 +2971,12 @@ DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
|
||||||
for (int note=0; note<120; note++) {
|
for (int note=0; note<120; note++) {
|
||||||
amiga.noteMap[note].map=reader.readS();
|
amiga.noteMap[note].map=reader.readS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version<152) {
|
||||||
|
for (int note=0; note<120; note++) {
|
||||||
|
amiga.noteMap[note].freq=note;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -414,7 +414,7 @@ struct DivInstrumentAmiga {
|
||||||
if (note>119) note=119;
|
if (note>119) note=119;
|
||||||
return noteMap[note].freq;
|
return noteMap[note].freq;
|
||||||
}
|
}
|
||||||
return -1;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
DivInstrumentAmiga():
|
DivInstrumentAmiga():
|
||||||
|
@ -423,8 +423,9 @@ struct DivInstrumentAmiga {
|
||||||
useSample(false),
|
useSample(false),
|
||||||
useWave(false),
|
useWave(false),
|
||||||
waveLen(31) {
|
waveLen(31) {
|
||||||
for (SampleMap& elem: noteMap) {
|
for (int i=0; i<120; i++) {
|
||||||
elem=SampleMap();
|
noteMap[i].map=-1;
|
||||||
|
noteMap[i].freq=i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -560,7 +560,10 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
chan[c.chan].useWave=false;
|
chan[c.chan].useWave=false;
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
|
|
@ -292,8 +292,6 @@ void DivPlatformAY8910::tick(bool sysTick) {
|
||||||
if (chan[i].std.phaseReset.val==1) {
|
if (chan[i].std.phaseReset.val==1) {
|
||||||
if (chan[i].nextPSGMode.dac) {
|
if (chan[i].nextPSGMode.dac) {
|
||||||
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
||||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AY);
|
|
||||||
chan[i].dac.sample=ins->amiga.getSample(chan[i].note);
|
|
||||||
if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) {
|
if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) {
|
||||||
if (dumpWrites) {
|
if (dumpWrites) {
|
||||||
rWrite(0x08+i,0);
|
rWrite(0x08+i,0);
|
||||||
|
@ -405,7 +403,10 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
||||||
if (chan[c.chan].nextPSGMode.dac) {
|
if (chan[c.chan].nextPSGMode.dac) {
|
||||||
if (skipRegisterWrites) break;
|
if (skipRegisterWrites) break;
|
||||||
if (!parent->song.disableSampleMacro && (ins->type==DIV_INS_AMIGA || ins->amiga.useSample)) {
|
if (!parent->song.disableSampleMacro && (ins->type==DIV_INS_AMIGA || ins->amiga.useSample)) {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
|
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].dac.sample=-1;
|
chan[c.chan].dac.sample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||||
|
|
|
@ -280,8 +280,6 @@ void DivPlatformAY8930::tick(bool sysTick) {
|
||||||
if (chan[i].std.phaseReset.val==1) {
|
if (chan[i].std.phaseReset.val==1) {
|
||||||
if (chan[i].nextPSGMode.dac) {
|
if (chan[i].nextPSGMode.dac) {
|
||||||
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
||||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AY8930);
|
|
||||||
chan[i].dac.sample=ins->amiga.getSample(chan[i].note);
|
|
||||||
if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) {
|
if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) {
|
||||||
if (dumpWrites) {
|
if (dumpWrites) {
|
||||||
rWrite(0x08+i,0);
|
rWrite(0x08+i,0);
|
||||||
|
@ -406,7 +404,10 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
|
||||||
if (chan[c.chan].nextPSGMode.dac) {
|
if (chan[c.chan].nextPSGMode.dac) {
|
||||||
if (skipRegisterWrites) break;
|
if (skipRegisterWrites) break;
|
||||||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
|
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].dac.sample=-1;
|
chan[c.chan].dac.sample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||||
|
|
|
@ -410,7 +410,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
if (chan[i].pcmChanged.changed) {
|
if (chan[i].pcmChanged.changed) {
|
||||||
if (chan[i].pcmChanged.index) {
|
if (chan[i].pcmChanged.index) {
|
||||||
const int next=chan[i].pcm.next;
|
const int next=chan[i].pcm.next;
|
||||||
bool sampleVaild=false;
|
bool sampleValid=false;
|
||||||
if (((ins->amiga.useNoteMap) && (next>=0 && next<120)) ||
|
if (((ins->amiga.useNoteMap) && (next>=0 && next<120)) ||
|
||||||
((!ins->amiga.useNoteMap) && (next>=0 && next<parent->song.sampleLen))) {
|
((!ins->amiga.useNoteMap) && (next>=0 && next<parent->song.sampleLen))) {
|
||||||
DivInstrumentAmiga::SampleMap& noteMapind=ins->amiga.noteMap[next];
|
DivInstrumentAmiga::SampleMap& noteMapind=ins->amiga.noteMap[next];
|
||||||
|
@ -420,7 +420,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||||
const unsigned int offES5506=sampleOffES5506[sample];
|
const unsigned int offES5506=sampleOffES5506[sample];
|
||||||
sampleVaild=true;
|
sampleValid=true;
|
||||||
chan[i].pcm.index=sample;
|
chan[i].pcm.index=sample;
|
||||||
chan[i].pcm.isNoteMap=ins->amiga.useNoteMap;
|
chan[i].pcm.isNoteMap=ins->amiga.useNoteMap;
|
||||||
DivSample* s=parent->getSample(sample);
|
DivSample* s=parent->getSample(sample);
|
||||||
|
@ -433,7 +433,6 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
off=(double)center/8363.0;
|
off=(double)center/8363.0;
|
||||||
}
|
}
|
||||||
if (ins->amiga.useNoteMap) {
|
if (ins->amiga.useNoteMap) {
|
||||||
off*=(double)noteMapind.freq/((double)MAX(1,center)*pow(2.0,((double)next-48.0)/12.0));
|
|
||||||
chan[i].pcm.note=next;
|
chan[i].pcm.note=next;
|
||||||
}
|
}
|
||||||
// get loop mode
|
// get loop mode
|
||||||
|
@ -459,7 +458,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sampleVaild) {
|
if (sampleValid) {
|
||||||
if (!chan[i].keyOn) {
|
if (!chan[i].keyOn) {
|
||||||
pageWrite(0x20|i,0x03,(chan[i].pcm.direction)?chan[i].pcm.end:chan[i].pcm.start);
|
pageWrite(0x20|i,0x03,(chan[i].pcm.direction)?chan[i].pcm.end:chan[i].pcm.start);
|
||||||
}
|
}
|
||||||
|
@ -618,10 +617,6 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
} else {
|
} else {
|
||||||
off=(double)center/8363.0;
|
off=(double)center/8363.0;
|
||||||
}
|
}
|
||||||
if (ins->amiga.useNoteMap) {
|
|
||||||
DivInstrumentAmiga::SampleMap& noteMapind=ins->amiga.noteMap[chan[i].pcm.note];
|
|
||||||
off*=(double)noteMapind.freq/((double)MAX(1,center)*pow(2.0,((double)chan[i].pcm.note-48.0)/12.0));
|
|
||||||
}
|
|
||||||
chan[i].pcm.loopStart=(chan[i].pcm.start+(s->loopStart<<11))&0xfffff800;
|
chan[i].pcm.loopStart=(chan[i].pcm.start+(s->loopStart<<11))&0xfffff800;
|
||||||
chan[i].pcm.loopEnd=(chan[i].pcm.start+((s->loopEnd-1)<<11))&0xffffff80;
|
chan[i].pcm.loopEnd=(chan[i].pcm.start+((s->loopEnd-1)<<11))&0xffffff80;
|
||||||
chan[i].pcm.freqOffs=PITCH_OFFSET*off;
|
chan[i].pcm.freqOffs=PITCH_OFFSET*off;
|
||||||
|
@ -750,24 +745,22 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
||||||
switch (c.cmd) {
|
switch (c.cmd) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_ES5506);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_ES5506);
|
||||||
bool sampleVaild=false;
|
bool sampleValid=false;
|
||||||
if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) ||
|
if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) ||
|
||||||
((!ins->amiga.useNoteMap) && (ins->amiga.initSample>=0 && ins->amiga.initSample<parent->song.sampleLen))) {
|
((!ins->amiga.useNoteMap) && (ins->amiga.initSample>=0 && ins->amiga.initSample<parent->song.sampleLen))) {
|
||||||
DivInstrumentAmiga::SampleMap& noteMapind=ins->amiga.noteMap[c.value];
|
int sample=ins->amiga.getSample(c.value);
|
||||||
int sample=ins->amiga.initSample;
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
if (ins->amiga.useNoteMap) {
|
|
||||||
sample=noteMapind.map;
|
|
||||||
}
|
|
||||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||||
sampleVaild=true;
|
sampleValid=true;
|
||||||
chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xfff;
|
chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xfff;
|
||||||
chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xfff;
|
chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xfff;
|
||||||
chan[c.chan].pcm.next=sample;
|
chan[c.chan].pcm.note=c.value;
|
||||||
|
chan[c.chan].pcm.next=ins->amiga.useNoteMap?c.value:sample;
|
||||||
chan[c.chan].filter=ins->es5506.filter;
|
chan[c.chan].filter=ins->es5506.filter;
|
||||||
chan[c.chan].envelope=ins->es5506.envelope;
|
chan[c.chan].envelope=ins->es5506.envelope;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!sampleVaild) {
|
if (!sampleValid) {
|
||||||
chan[c.chan].pcm.index=chan[c.chan].pcm.next=-1;
|
chan[c.chan].pcm.index=chan[c.chan].pcm.next=-1;
|
||||||
chan[c.chan].filter=DivInstrumentES5506::Filter();
|
chan[c.chan].filter=DivInstrumentES5506::Filter();
|
||||||
chan[c.chan].envelope=DivInstrumentES5506::Envelope();
|
chan[c.chan].envelope=DivInstrumentES5506::Envelope();
|
||||||
|
|
|
@ -200,7 +200,10 @@ int DivPlatformGA20::dispatch(DivCommand c) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -681,7 +681,10 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
if (c.chan>=5 && chan[c.chan].dacMode) {
|
if (c.chan>=5 && chan[c.chan].dacMode) {
|
||||||
//if (skipRegisterWrites) break;
|
//if (skipRegisterWrites) break;
|
||||||
if (ins->type==DIV_INS_AMIGA) { // Furnace mode
|
if (ins->type==DIV_INS_AMIGA) { // Furnace mode
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].dacSample=-1;
|
chan[c.chan].dacSample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002,0);
|
if (dumpWrites) addWrite(0xffff0002,0);
|
||||||
|
|
|
@ -433,13 +433,8 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) {
|
||||||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||||
if (isOpMuted[ch-2] || !op.enable) {
|
if (isOpMuted[ch-2] || !op.enable) {
|
||||||
rWrite(baseAddr+0x40,127);
|
rWrite(baseAddr+0x40,127);
|
||||||
immWrite(baseAddr+0x40,127);
|
|
||||||
} else if (KVS(2,ordch)) {
|
|
||||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
|
||||||
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
|
||||||
} else {
|
} else {
|
||||||
rWrite(baseAddr+0x40,op.tl);
|
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||||
immWrite(baseAddr+0x40,op.tl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
||||||
|
|
|
@ -34,7 +34,7 @@ const char* regCheatSheetK007232[]={
|
||||||
"CHX_StartM", "X*6+3",
|
"CHX_StartM", "X*6+3",
|
||||||
"CHX_StartH", "X*6+4",
|
"CHX_StartH", "X*6+4",
|
||||||
"CHX_Keyon", "X*6+5",
|
"CHX_Keyon", "X*6+5",
|
||||||
"SLEV", "C", // external IO
|
"SLEV", "C", // external IO (Volume for Mono speaker)
|
||||||
"Loop", "D",
|
"Loop", "D",
|
||||||
// off-chip
|
// off-chip
|
||||||
"CHX_Volume", "X*2+10",
|
"CHX_Volume", "X*2+10",
|
||||||
|
@ -157,8 +157,7 @@ void DivPlatformK007232::tick(bool sysTick) {
|
||||||
rWrite(0x10+i,(chan[i].lvol&0xf)|((chan[i].rvol&0xf)<<4));
|
rWrite(0x10+i,(chan[i].lvol&0xf)|((chan[i].rvol&0xf)<<4));
|
||||||
chan[i].prevPan=newPan;
|
chan[i].prevPan=newPan;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
const unsigned char prevVolume=lastVolume;
|
const unsigned char prevVolume=lastVolume;
|
||||||
lastVolume=(lastVolume&~(0xf<<(i<<2)))|((chan[i].resVol&0xf)<<(i<<2));
|
lastVolume=(lastVolume&~(0xf<<(i<<2)))|((chan[i].resVol&0xf)<<(i<<2));
|
||||||
if (prevVolume!=lastVolume) {
|
if (prevVolume!=lastVolume) {
|
||||||
|
@ -274,7 +273,10 @@ int DivPlatformK007232::dispatch(DivCommand c) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:15;
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:15;
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||||
}
|
}
|
||||||
|
@ -477,6 +479,7 @@ void DivPlatformK007232::setFlags(const DivConfig& flags) {
|
||||||
rate=chipClock/4;
|
rate=chipClock/4;
|
||||||
stereo=flags.getBool("stereo",false);
|
stereo=flags.getBool("stereo",false);
|
||||||
for (int i=0; i<2; i++) {
|
for (int i=0; i<2; i++) {
|
||||||
|
chan[i].volumeChanged=true;
|
||||||
oscBuf[i]->rate=rate;
|
oscBuf[i]->rate=rate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,13 +261,14 @@ int DivPlatformLynx::dispatch(DivCommand c) {
|
||||||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
|
||||||
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
|
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
|
||||||
if (chan[c.chan].pcm) {
|
if (chan[c.chan].pcm) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
chan[c.chan].sampleBaseFreq=NOTE_FREQUENCY(c.value);
|
chan[c.chan].sampleBaseFreq=NOTE_FREQUENCY(c.value);
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
|
||||||
chan[c.chan].sampleAccum=0;
|
chan[c.chan].sampleAccum=0;
|
||||||
chan[c.chan].samplePos=0;
|
chan[c.chan].samplePos=0;
|
||||||
}
|
}
|
||||||
|
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
chan[c.chan].actualNote=c.value;
|
chan[c.chan].actualNote=c.value;
|
||||||
|
|
|
@ -176,7 +176,10 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
|
||||||
if (c.chan==2) { // PCM
|
if (c.chan==2) { // PCM
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
|
||||||
if (ins->type==DIV_INS_AMIGA) {
|
if (ins->type==DIV_INS_AMIGA) {
|
||||||
if (c.value!=DIV_NOTE_NULL) dacSample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
dacSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002,0);
|
if (dumpWrites) addWrite(0xffff0002,0);
|
||||||
|
|
|
@ -576,7 +576,7 @@ void DivPlatformNamcoWSG::setFlags(const DivConfig& flags) {
|
||||||
oscBuf[i]->rate=rate;
|
oscBuf[i]->rate=rate;
|
||||||
}
|
}
|
||||||
newNoise=flags.getBool("newNoise",true);
|
newNoise=flags.getBool("newNoise",true);
|
||||||
romMode=flags.getBool("romMode",true);
|
romMode=flags.getBool("romMode",false);
|
||||||
if (devType==30) romMode=false;
|
if (devType==30) romMode=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -361,7 +361,10 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
||||||
if (c.chan==4) { // PCM
|
if (c.chan==4) { // PCM
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
|
||||||
if (ins->type==DIV_INS_AMIGA) {
|
if (ins->type==DIV_INS_AMIGA) {
|
||||||
if (c.value!=DIV_NOTE_NULL) dacSample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
dacSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0);
|
if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0);
|
||||||
|
|
|
@ -856,7 +856,10 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
immWrite(18,chan[c.chan].outVol);
|
immWrite(18,chan[c.chan].outVol);
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
immWrite(8,0);
|
immWrite(8,0);
|
||||||
|
@ -1555,7 +1558,7 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
|
DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
|
||||||
if (oplType==759) {
|
if (oplType==759 || chipType==8950) {
|
||||||
if (ch>=totalChans+1) return NULL;
|
if (ch>=totalChans+1) return NULL;
|
||||||
} else {
|
} else {
|
||||||
if (ch>=totalChans) return NULL;
|
if (ch>=totalChans) return NULL;
|
||||||
|
|
|
@ -282,7 +282,10 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
||||||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||||
chan[c.chan].furnaceDac=true;
|
chan[c.chan].furnaceDac=true;
|
||||||
if (skipRegisterWrites) break;
|
if (skipRegisterWrites) break;
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].dacSample=-1;
|
chan[c.chan].dacSample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||||
|
|
|
@ -271,7 +271,10 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[0].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[0].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
chan[0].useWave=false;
|
chan[0].useWave=false;
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
|
|
@ -450,7 +450,10 @@ int DivPlatformQSound::dispatch(DivCommand c) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
chan[c.chan].isNewQSound=(ins->type==DIV_INS_QSOUND);
|
chan[c.chan].isNewQSound=(ins->type==DIV_INS_QSOUND);
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value);
|
chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,8 +151,8 @@ void DivPlatformRF5C68::tick(bool sysTick) {
|
||||||
if (s->isLoopable()) {
|
if (s->isLoopable()) {
|
||||||
loop=start+s->loopStart;
|
loop=start+s->loopStart;
|
||||||
}
|
}
|
||||||
start=MIN(start,getSampleMemCapacity()-31);
|
start=MIN(start,getSampleMemCapacity()-32);
|
||||||
loop=MIN(loop,getSampleMemCapacity()-31);
|
loop=MIN(loop,getSampleMemCapacity()-32);
|
||||||
rWrite(8,keyoff); // force keyoff first
|
rWrite(8,keyoff); // force keyoff first
|
||||||
chWrite(i,6,start>>8);
|
chWrite(i,6,start>>8);
|
||||||
chWrite(i,4,loop&0xff);
|
chWrite(i,4,loop&0xff);
|
||||||
|
@ -182,7 +182,10 @@ int DivPlatformRF5C68::dispatch(DivCommand c) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||||
}
|
}
|
||||||
|
@ -422,7 +425,7 @@ void DivPlatformRF5C68::renderSamples(int sysID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int length=s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT);
|
int length=s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT);
|
||||||
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-31,length);
|
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-32,length);
|
||||||
if (actualLength>0) {
|
if (actualLength>0) {
|
||||||
sampleOffRFC[i]=memPos;
|
sampleOffRFC[i]=memPos;
|
||||||
for (int j=0; j<actualLength; j++) {
|
for (int j=0; j<actualLength; j++) {
|
||||||
|
@ -432,8 +435,8 @@ void DivPlatformRF5C68::renderSamples(int sysID) {
|
||||||
sampleMem[memPos++]=(val>0)?(val|0x80):(0-val);
|
sampleMem[memPos++]=(val>0)?(val|0x80):(0-val);
|
||||||
}
|
}
|
||||||
// write end of sample marker
|
// write end of sample marker
|
||||||
memset(&sampleMem[memPos],0xff,31);
|
memset(&sampleMem[memPos],0xff,32);
|
||||||
memPos+=31;
|
memPos+=32;
|
||||||
}
|
}
|
||||||
if (actualLength<length) {
|
if (actualLength<length) {
|
||||||
logW("out of RF5C68 PCM memory for sample %d!",i);
|
logW("out of RF5C68 PCM memory for sample %d!",i);
|
||||||
|
|
|
@ -72,7 +72,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
|
||||||
chan[i].handleArp();
|
chan[i].handleArp();
|
||||||
} else if (chan[i].std.arp.had) {
|
} else if (chan[i].std.arp.had) {
|
||||||
if (!chan[i].inPorta) {
|
if (!chan[i].inPorta) {
|
||||||
chan[i].baseFreq=(parent->calcArp(chan[i].note,chan[i].std.arp.val)<<6);
|
chan[i].baseFreq=(parent->calcArp(chan[i].note,chan[i].std.arp.val)<<7);
|
||||||
}
|
}
|
||||||
chan[i].freqChanged=true;
|
chan[i].freqChanged=true;
|
||||||
}
|
}
|
||||||
|
@ -106,21 +106,22 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||||
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64;
|
chan[i].freq=chan[i].baseFreq+(chan[i].pitch)-128+(oldSlides?0:chan[i].pitch2);
|
||||||
if (!parent->song.oldArpStrategy) {
|
if (!parent->song.oldArpStrategy) {
|
||||||
if (chan[i].fixedArp) {
|
if (chan[i].fixedArp) {
|
||||||
chan[i].freq=(chan[i].baseNoteOverride<<6)+(chan[i].pitch>>1)-64+chan[i].pitch2;
|
chan[i].freq=(chan[i].baseNoteOverride<<7)+chan[i].pitch-128+(chan[i].pitch2<<(oldSlides?1:0));
|
||||||
} else {
|
} else {
|
||||||
chan[i].freq+=chan[i].arpOff<<6;
|
chan[i].freq+=chan[i].arpOff<<7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (oldSlides) chan[i].freq&=~1;
|
||||||
if (chan[i].furnacePCM) {
|
if (chan[i].furnacePCM) {
|
||||||
double off=1.0;
|
double off=1.0;
|
||||||
if (chan[i].pcm.sample>=0 && chan[i].pcm.sample<parent->song.sampleLen) {
|
if (chan[i].pcm.sample>=0 && chan[i].pcm.sample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[i].pcm.sample);
|
DivSample* s=parent->getSample(chan[i].pcm.sample);
|
||||||
off=(double)s->centerRate/8363.0;
|
off=(double)s->centerRate/8363.0;
|
||||||
}
|
}
|
||||||
chan[i].pcm.freq=MIN(255,(15625+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250)+chan[i].pitch2;
|
chan[i].pcm.freq=MIN(255,(15625+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+512)/(128.0*12.0)))*255)/31250)+(oldSlides?chan[i].pitch2:0);
|
||||||
rWrite(7+(i<<3),chan[i].pcm.freq);
|
rWrite(7+(i<<3),chan[i].pcm.freq);
|
||||||
}
|
}
|
||||||
chan[i].freqChanged=false;
|
chan[i].freqChanged=false;
|
||||||
|
@ -186,7 +187,10 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
||||||
if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SEGAPCM) {
|
if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SEGAPCM) {
|
||||||
chan[c.chan].macroVolMul=(ins->type==DIV_INS_AMIGA)?64:127;
|
chan[c.chan].macroVolMul=(ins->type==DIV_INS_AMIGA)?64:127;
|
||||||
chan[c.chan].isNewSegaPCM=(ins->type==DIV_INS_SEGAPCM);
|
chan[c.chan].isNewSegaPCM=(ins->type==DIV_INS_SEGAPCM);
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].pcm.sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].pcm.sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
|
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].pcm.sample=-1;
|
chan[c.chan].pcm.sample=-1;
|
||||||
rWrite(0x86+(c.chan<<3),3);
|
rWrite(0x86+(c.chan<<3),3);
|
||||||
|
@ -198,7 +202,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
chan[c.chan].baseFreq=(c.value<<6);
|
chan[c.chan].baseFreq=(c.value<<7);
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
}
|
}
|
||||||
chan[c.chan].furnacePCM=true;
|
chan[c.chan].furnacePCM=true;
|
||||||
|
@ -286,17 +290,18 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_NOTE_PORTA: {
|
case DIV_CMD_NOTE_PORTA: {
|
||||||
int destFreq=(c.value2<<6);
|
int destFreq=(c.value2<<7);
|
||||||
int newFreq;
|
int newFreq;
|
||||||
|
int mul=(oldSlides || parent->song.linearPitch!=2)?8:1;
|
||||||
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*4;
|
newFreq=chan[c.chan].baseFreq+c.value*mul;
|
||||||
if (newFreq>=destFreq) {
|
if (newFreq>=destFreq) {
|
||||||
newFreq=destFreq;
|
newFreq=destFreq;
|
||||||
return2=true;
|
return2=true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newFreq=chan[c.chan].baseFreq-c.value*4;
|
newFreq=chan[c.chan].baseFreq-c.value*mul;
|
||||||
if (newFreq<=destFreq) {
|
if (newFreq<=destFreq) {
|
||||||
newFreq=destFreq;
|
newFreq=destFreq;
|
||||||
return2=true;
|
return2=true;
|
||||||
|
@ -311,7 +316,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_LEGATO: {
|
case DIV_CMD_LEGATO: {
|
||||||
chan[c.chan].baseFreq=(c.value<<6);
|
chan[c.chan].baseFreq=(c.value<<7);
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -334,7 +339,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
||||||
return 127;
|
return 127;
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_PRE_PORTA:
|
case DIV_CMD_PRE_PORTA:
|
||||||
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=(chan[c.chan].note<<6);
|
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=(chan[c.chan].note<<7);
|
||||||
chan[c.chan].inPorta=c.value;
|
chan[c.chan].inPorta=c.value;
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_PRE_NOTE:
|
case DIV_CMD_PRE_NOTE:
|
||||||
|
@ -480,7 +485,11 @@ void DivPlatformSegaPCM::renderSamples(int sysID) {
|
||||||
if (memPos>=16777216) break;
|
if (memPos>=16777216) break;
|
||||||
sampleOffSegaPCM[i]=memPos;
|
sampleOffSegaPCM[i]=memPos;
|
||||||
for (unsigned int j=0; j<alignedSize; j++) {
|
for (unsigned int j=0; j<alignedSize; j++) {
|
||||||
|
if (j>=sample->samples) {
|
||||||
|
sampleMem[memPos++]=0;
|
||||||
|
} else {
|
||||||
sampleMem[memPos++]=((unsigned char)sample->data8[j]+0x80);
|
sampleMem[memPos++]=((unsigned char)sample->data8[j]+0x80);
|
||||||
|
}
|
||||||
sampleEndSegaPCM[i]=((memPos+0xff)>>8)-1;
|
sampleEndSegaPCM[i]=((memPos+0xff)>>8)-1;
|
||||||
if (memPos>=16777216) break;
|
if (memPos>=16777216) break;
|
||||||
}
|
}
|
||||||
|
@ -497,6 +506,8 @@ void DivPlatformSegaPCM::setFlags(const DivConfig& flags) {
|
||||||
for (int i=0; i<16; i++) {
|
for (int i=0; i<16; i++) {
|
||||||
oscBuf[i]->rate=rate;
|
oscBuf[i]->rate=rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldSlides=flags.getBool("oldSlides",false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformSegaPCM::getOutputCount() {
|
int DivPlatformSegaPCM::getOutputCount() {
|
||||||
|
|
|
@ -65,6 +65,7 @@ class DivPlatformSegaPCM: public DivDispatch {
|
||||||
segapcm_device pcm;
|
segapcm_device pcm;
|
||||||
int delay;
|
int delay;
|
||||||
int pcmL, pcmR, pcmCycles;
|
int pcmL, pcmR, pcmCycles;
|
||||||
|
bool oldSlides;
|
||||||
unsigned char sampleBank;
|
unsigned char sampleBank;
|
||||||
unsigned char lastBusy;
|
unsigned char lastBusy;
|
||||||
|
|
||||||
|
|
|
@ -336,7 +336,10 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
chan[c.chan].ws.init(ins,chan[c.chan].wtLen,15,chan[c.chan].insChanged);
|
chan[c.chan].ws.init(ins,chan[c.chan].wtLen,15,chan[c.chan].insChanged);
|
||||||
} else {
|
} else {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
chan[c.chan].useWave=false;
|
chan[c.chan].useWave=false;
|
||||||
}
|
}
|
||||||
if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
||||||
|
|
|
@ -138,9 +138,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
|
||||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
||||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,chan[i].switchRoles,2,chan[i].pitch2,chipClock,chan[i].switchRoles?CHIP_DIVIDER:CHIP_FREQBASE);
|
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,chan[i].switchRoles,2,chan[i].pitch2,chipClock,chan[i].switchRoles?CHIP_DIVIDER:CHIP_FREQBASE);
|
||||||
if (chan[i].pcm) {
|
if (chan[i].pcm) {
|
||||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
DivSample* sample=parent->getSample(chan[i].sample);
|
||||||
// TODO: sample map?
|
|
||||||
DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note));
|
|
||||||
if (sample!=NULL) {
|
if (sample!=NULL) {
|
||||||
double off=0.25;
|
double off=0.25;
|
||||||
if (sample->centerRate<1) {
|
if (sample->centerRate<1) {
|
||||||
|
@ -209,6 +207,12 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
|
||||||
writeControlUpper(c.chan);
|
writeControlUpper(c.chan);
|
||||||
}
|
}
|
||||||
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
|
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
|
||||||
|
if (chan[c.chan].pcm) {
|
||||||
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value);
|
chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value);
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
class DivPlatformSoundUnit: public DivDispatch {
|
class DivPlatformSoundUnit: public DivDispatch {
|
||||||
struct Channel: public SharedChannel<signed char> {
|
struct Channel: public SharedChannel<signed char> {
|
||||||
int cutoff, baseCutoff, res, control, hasOffset;
|
int cutoff, baseCutoff, res, control, hasOffset, sample;
|
||||||
signed char pan;
|
signed char pan;
|
||||||
unsigned char duty;
|
unsigned char duty;
|
||||||
bool noise, pcm, phaseReset, filterPhaseReset, switchRoles;
|
bool noise, pcm, phaseReset, filterPhaseReset, switchRoles;
|
||||||
|
@ -43,6 +43,7 @@ class DivPlatformSoundUnit: public DivDispatch {
|
||||||
res(0),
|
res(0),
|
||||||
control(0),
|
control(0),
|
||||||
hasOffset(0),
|
hasOffset(0),
|
||||||
|
sample(-1),
|
||||||
pan(0),
|
pan(0),
|
||||||
duty(63),
|
duty(63),
|
||||||
noise(false),
|
noise(false),
|
||||||
|
|
|
@ -261,7 +261,10 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
||||||
dacPos=0;
|
dacPos=0;
|
||||||
dacPeriod=0;
|
dacPeriod=0;
|
||||||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||||
if (c.value!=DIV_NOTE_NULL) dacSample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
dacSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
if (dumpWrites) postWrite(0xffff0002,0);
|
if (dumpWrites) postWrite(0xffff0002,0);
|
||||||
|
|
|
@ -235,7 +235,11 @@ int DivPlatformVERA::dispatch(DivCommand c) {
|
||||||
if (c.chan<16) {
|
if (c.chan<16) {
|
||||||
rWriteLo(c.chan,2,chan[c.chan].vol);
|
rWriteLo(c.chan,2,chan[c.chan].vol);
|
||||||
} else {
|
} else {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[16].pcm.sample=parent->getIns(chan[16].ins,DIV_INS_VERA)->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
DivInstrument* ins=parent->getIns(chan[16].ins,DIV_INS_VERA);
|
||||||
|
chan[16].pcm.sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) {
|
if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) {
|
||||||
chan[16].pcm.sample=-1;
|
chan[16].pcm.sample=-1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,8 +179,6 @@ void DivPlatformVRC6::tick(bool sysTick) {
|
||||||
if (chan[i].std.phaseReset.val && chan[i].active) {
|
if (chan[i].std.phaseReset.val && chan[i].active) {
|
||||||
if ((i!=2) && (!chan[i].pcm)) {
|
if ((i!=2) && (!chan[i].pcm)) {
|
||||||
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
||||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_VRC6);
|
|
||||||
chan[i].dacSample=ins->amiga.getSample(chan[i].note);
|
|
||||||
if (chan[i].dacSample<0 || chan[i].dacSample>=parent->song.sampleLen) {
|
if (chan[i].dacSample<0 || chan[i].dacSample>=parent->song.sampleLen) {
|
||||||
if (dumpWrites) {
|
if (dumpWrites) {
|
||||||
chWrite(i,2,0x80);
|
chWrite(i,2,0x80);
|
||||||
|
@ -242,7 +240,10 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
|
||||||
if (chan[c.chan].pcm) {
|
if (chan[c.chan].pcm) {
|
||||||
if (skipRegisterWrites) break;
|
if (skipRegisterWrites) break;
|
||||||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].dacSample=-1;
|
chan[c.chan].dacSample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||||
|
|
|
@ -222,7 +222,8 @@ void DivPlatformX1_010::acquire(short** buf, size_t len) {
|
||||||
if (stereo) buf[1][h]=tempR;
|
if (stereo) buf[1][h]=tempR;
|
||||||
|
|
||||||
for (int i=0; i<16; i++) {
|
for (int i=0; i<16; i++) {
|
||||||
oscBuf[i]->data[oscBuf[i]->needle++]=(x1_010.voice_out(i,0)+x1_010.voice_out(i,1))>>1;
|
int vo=(x1_010.voice_out(i,0)+x1_010.voice_out(i,1))<<3;
|
||||||
|
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(vo,-32768,32767);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -540,7 +541,10 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
|
||||||
if (chan[c.chan].furnacePCM) {
|
if (chan[c.chan].furnacePCM) {
|
||||||
chan[c.chan].pcm=true;
|
chan[c.chan].pcm=true;
|
||||||
chan[c.chan].macroInit(ins);
|
chan[c.chan].macroInit(ins);
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
if (isBanked) {
|
if (isBanked) {
|
||||||
|
|
|
@ -542,13 +542,8 @@ void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
|
||||||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||||
if (isOpMuted[ch-2] || !op.enable) {
|
if (isOpMuted[ch-2] || !op.enable) {
|
||||||
rWrite(baseAddr+0x40,127);
|
rWrite(baseAddr+0x40,127);
|
||||||
immWrite(baseAddr+0x40,127);
|
|
||||||
} else if (KVS(2,ordch)) {
|
|
||||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
|
||||||
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
|
||||||
} else {
|
} else {
|
||||||
rWrite(baseAddr+0x40,op.tl);
|
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||||
immWrite(baseAddr+0x40,op.tl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -896,7 +896,10 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
immWrite(0x10b,chan[c.chan].outVol);
|
immWrite(0x10b,chan[c.chan].outVol);
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
immWrite(0x100,0x01); // reset
|
immWrite(0x100,0x01); // reset
|
||||||
|
|
|
@ -564,13 +564,8 @@ void DivPlatformYM2608Ext::muteChannel(int ch, bool mute) {
|
||||||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||||
if (isOpMuted[ch-2] || !op.enable) {
|
if (isOpMuted[ch-2] || !op.enable) {
|
||||||
rWrite(baseAddr+0x40,127);
|
rWrite(baseAddr+0x40,127);
|
||||||
immWrite(baseAddr+0x40,127);
|
|
||||||
} else if (KVS(2,ordch)) {
|
|
||||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
|
||||||
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
|
||||||
} else {
|
} else {
|
||||||
rWrite(baseAddr+0x40,op.tl);
|
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||||
immWrite(baseAddr+0x40,op.tl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
||||||
|
|
|
@ -827,7 +827,10 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
immWrite(0x1b,chan[c.chan].outVol);
|
immWrite(0x1b,chan[c.chan].outVol);
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
immWrite(0x12,(sampleOffB[chan[c.chan].sample]>>8)&0xff);
|
immWrite(0x12,(sampleOffB[chan[c.chan].sample]>>8)&0xff);
|
||||||
|
|
|
@ -894,7 +894,10 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
immWrite(0x1b,chan[c.chan].outVol);
|
immWrite(0x1b,chan[c.chan].outVol);
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
immWrite(0x12,(sampleOffB[chan[c.chan].sample]>>8)&0xff);
|
immWrite(0x12,(sampleOffB[chan[c.chan].sample]>>8)&0xff);
|
||||||
|
|
|
@ -560,13 +560,8 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
|
||||||
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
|
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
|
||||||
if (isOpMuted[ch-extChanOffs] || !op.enable) {
|
if (isOpMuted[ch-extChanOffs] || !op.enable) {
|
||||||
rWrite(baseAddr+0x40,127);
|
rWrite(baseAddr+0x40,127);
|
||||||
immWrite(baseAddr+0x40,127);
|
|
||||||
} else if (KVS(2,ordch)) {
|
|
||||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
|
||||||
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
|
||||||
} else {
|
} else {
|
||||||
rWrite(baseAddr+0x40,op.tl);
|
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
||||||
immWrite(baseAddr+0x40,op.tl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||||
|
|
|
@ -560,13 +560,8 @@ void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) {
|
||||||
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
|
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
|
||||||
if (isOpMuted[ch-extChanOffs] || !op.enable) {
|
if (isOpMuted[ch-extChanOffs] || !op.enable) {
|
||||||
rWrite(baseAddr+0x40,127);
|
rWrite(baseAddr+0x40,127);
|
||||||
immWrite(baseAddr+0x40,127);
|
|
||||||
} else if (KVS(2,ordch)) {
|
|
||||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
|
||||||
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
|
||||||
} else {
|
} else {
|
||||||
rWrite(baseAddr+0x40,op.tl);
|
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
||||||
immWrite(baseAddr+0x40,op.tl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||||
|
|
|
@ -212,7 +212,10 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
chan[c.chan].isNewYMZ=ins->type==DIV_INS_YMZ280B;
|
chan[c.chan].isNewYMZ=ins->type==DIV_INS_YMZ280B;
|
||||||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
||||||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,11 @@ const char** DivPlatformZXBeeperQuadTone::getRegisterSheet() {
|
||||||
void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
|
void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
|
||||||
bool o=false;
|
bool o=false;
|
||||||
for (size_t h=0; h<len; h++) {
|
for (size_t h=0; h<len; h++) {
|
||||||
if (curSample>=0 && curSample<parent->song.sampleLen) {
|
if (curSample>=0 && curSample<parent->song.sampleLen && !isMuted[4]) {
|
||||||
while (curSamplePeriod>=chan[4].freq) {
|
while (curSamplePeriod>=chan[4].freq) {
|
||||||
DivSample* s=parent->getSample(curSample);
|
DivSample* s=parent->getSample(curSample);
|
||||||
if (s->samples>0) {
|
if (s->samples>0) {
|
||||||
o=(!isMuted[4]&&s->data8[curSamplePos++]>0);
|
if (!isMuted[4]) o=(s->data8[curSamplePos++]>0);
|
||||||
if (curSamplePos>=s->samples) curSample=-1;
|
if (curSamplePos>=s->samples) curSample=-1;
|
||||||
// (theoretical) 32KiB limit
|
// (theoretical) 32KiB limit
|
||||||
if (curSamplePos>=32768*8) curSample=-1;
|
if (curSamplePos>=32768*8) curSample=-1;
|
||||||
|
@ -59,6 +59,7 @@ void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
|
||||||
if ((outputClock&1)==0) {
|
if ((outputClock&1)==0) {
|
||||||
chan[ch].sPosition+=(regPool[1+b]<<8)|regPool[0+b];
|
chan[ch].sPosition+=(regPool[1+b]<<8)|regPool[0+b];
|
||||||
chan[ch].out=regPool[3+b]+((((chan[ch].sPosition>>8)&0xff)<regPool[2+b])?1:0);
|
chan[ch].out=regPool[3+b]+((((chan[ch].sPosition>>8)&0xff)<regPool[2+b])?1:0);
|
||||||
|
if (isMuted[ch]) chan[ch].out=0;
|
||||||
}
|
}
|
||||||
if ((outputClock&3)==0) {
|
if ((outputClock&3)==0) {
|
||||||
oscBuf[4]->data[oscBuf[4]->needle++]=0;
|
oscBuf[4]->data[oscBuf[4]->needle++]=0;
|
||||||
|
@ -66,6 +67,22 @@ void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
|
||||||
o=chan[ch].out&0x10;
|
o=chan[ch].out&0x10;
|
||||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=o?32767:0;
|
oscBuf[ch]->data[oscBuf[ch]->needle++]=o?32767:0;
|
||||||
chan[ch].out<<=1;
|
chan[ch].out<<=1;
|
||||||
|
|
||||||
|
// if muted, ztill run sample
|
||||||
|
if (curSample>=0 && curSample<parent->song.sampleLen && isMuted[4]) {
|
||||||
|
while (curSamplePeriod>=chan[4].freq) {
|
||||||
|
DivSample* s=parent->getSample(curSample);
|
||||||
|
if (s->samples>0) {
|
||||||
|
if (curSamplePos>=s->samples) curSample=-1;
|
||||||
|
// (theoretical) 32KiB limit
|
||||||
|
if (curSamplePos>=32768*8) curSample=-1;
|
||||||
|
} else {
|
||||||
|
curSample=-1;
|
||||||
|
}
|
||||||
|
curSamplePeriod-=chan[4].freq;
|
||||||
|
}
|
||||||
|
curSamplePeriod+=40;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
outputClock=(outputClock+1)&7;
|
outputClock=(outputClock+1)&7;
|
||||||
buf[0][h]=o?32767:0;
|
buf[0][h]=o?32767:0;
|
||||||
|
@ -169,10 +186,11 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) {
|
||||||
} else {
|
} else {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
curSample=ins->amiga.getSample(c.value);
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
curSample=ins->amiga.getSample(c.value);
|
|
||||||
// TODO support offset commands
|
// TODO support offset commands
|
||||||
curSamplePos=0;
|
curSamplePos=0;
|
||||||
curSamplePeriod=0;
|
curSamplePeriod=0;
|
||||||
|
@ -282,7 +300,7 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) {
|
||||||
void DivPlatformZXBeeperQuadTone::writeOutVol(int ch) {
|
void DivPlatformZXBeeperQuadTone::writeOutVol(int ch) {
|
||||||
if (ch>=4) return;
|
if (ch>=4) return;
|
||||||
unsigned char val=(chan[ch].outVol>=1)?((chan[ch].outVol>=2)?31:7):0;
|
unsigned char val=(chan[ch].outVol>=1)?((chan[ch].outVol>=2)?31:7):0;
|
||||||
rWrite(3+ch*4,(!isMuted[ch]&&chan[ch].active)?val:0);
|
rWrite(3+ch*4,(chan[ch].active)?val:0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformZXBeeperQuadTone::muteChannel(int ch, bool mute) {
|
void DivPlatformZXBeeperQuadTone::muteChannel(int ch, bool mute) {
|
||||||
|
|
|
@ -229,6 +229,8 @@ const char* cmdName[]={
|
||||||
"ES5506_ENVELOPE_K2RAMP",
|
"ES5506_ENVELOPE_K2RAMP",
|
||||||
"ES5506_PAUSE",
|
"ES5506_PAUSE",
|
||||||
|
|
||||||
|
"HINT_ARP_TIME",
|
||||||
|
|
||||||
"ALWAYS_SET_VOLUME"
|
"ALWAYS_SET_VOLUME"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -510,13 +512,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
if (chan[i].stopOnOff) {
|
if (chan[i].stopOnOff) {
|
||||||
chan[i].portaNote=-1;
|
chan[i].portaNote=-1;
|
||||||
chan[i].portaSpeed=-1;
|
chan[i].portaSpeed=-1;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].stopOnOff=false;
|
chan[i].stopOnOff=false;
|
||||||
}
|
}
|
||||||
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
|
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
|
||||||
chan[i].portaNote=-1;
|
chan[i].portaNote=-1;
|
||||||
chan[i].portaSpeed=-1;
|
chan[i].portaSpeed=-1;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
|
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
|
||||||
chan[i+1].portaNote=-1;
|
chan[i+1].portaNote=-1;
|
||||||
chan[i+1].portaSpeed=-1;
|
chan[i+1].portaSpeed=-1;
|
||||||
|
@ -533,13 +535,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
if (chan[i].stopOnOff) {
|
if (chan[i].stopOnOff) {
|
||||||
chan[i].portaNote=-1;
|
chan[i].portaNote=-1;
|
||||||
chan[i].portaSpeed=-1;
|
chan[i].portaSpeed=-1;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].stopOnOff=false;
|
chan[i].stopOnOff=false;
|
||||||
}
|
}
|
||||||
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
|
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
|
||||||
chan[i].portaNote=-1;
|
chan[i].portaNote=-1;
|
||||||
chan[i].portaSpeed=-1;
|
chan[i].portaSpeed=-1;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
|
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
|
||||||
chan[i+1].portaNote=-1;
|
chan[i+1].portaNote=-1;
|
||||||
chan[i+1].portaSpeed=-1;
|
chan[i+1].portaSpeed=-1;
|
||||||
|
@ -632,13 +634,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
if (effectVal==0) {
|
if (effectVal==0) {
|
||||||
chan[i].portaNote=-1;
|
chan[i].portaNote=-1;
|
||||||
chan[i].portaSpeed=-1;
|
chan[i].portaSpeed=-1;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].inPorta=false;
|
chan[i].inPorta=false;
|
||||||
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
|
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
|
||||||
} else {
|
} else {
|
||||||
chan[i].portaNote=song.limitSlides?0x60:255;
|
chan[i].portaNote=song.limitSlides?0x60:255;
|
||||||
chan[i].portaSpeed=effectVal;
|
chan[i].portaSpeed=effectVal;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].portaStop=true;
|
chan[i].portaStop=true;
|
||||||
chan[i].nowYouCanStop=false;
|
chan[i].nowYouCanStop=false;
|
||||||
chan[i].stopOnOff=false;
|
chan[i].stopOnOff=false;
|
||||||
|
@ -654,13 +656,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
if (effectVal==0) {
|
if (effectVal==0) {
|
||||||
chan[i].portaNote=-1;
|
chan[i].portaNote=-1;
|
||||||
chan[i].portaSpeed=-1;
|
chan[i].portaSpeed=-1;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].inPorta=false;
|
chan[i].inPorta=false;
|
||||||
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
|
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
|
||||||
} else {
|
} else {
|
||||||
chan[i].portaNote=song.limitSlides?disCont[dispatchOfChan[i]].dispatch->getPortaFloor(dispatchChanOfChan[i]):-60;
|
chan[i].portaNote=song.limitSlides?disCont[dispatchOfChan[i]].dispatch->getPortaFloor(dispatchChanOfChan[i]):-60;
|
||||||
chan[i].portaSpeed=effectVal;
|
chan[i].portaSpeed=effectVal;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].portaStop=true;
|
chan[i].portaStop=true;
|
||||||
chan[i].nowYouCanStop=false;
|
chan[i].nowYouCanStop=false;
|
||||||
chan[i].stopOnOff=false;
|
chan[i].stopOnOff=false;
|
||||||
|
@ -674,7 +676,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
if (effectVal==0) {
|
if (effectVal==0) {
|
||||||
chan[i].portaNote=-1;
|
chan[i].portaNote=-1;
|
||||||
chan[i].portaSpeed=-1;
|
chan[i].portaSpeed=-1;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].inPorta=false;
|
chan[i].inPorta=false;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
|
dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
|
||||||
} else {
|
} else {
|
||||||
|
@ -688,7 +690,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
chan[i].inPorta=true;
|
chan[i].inPorta=true;
|
||||||
chan[i].wasShorthandPorta=false;
|
chan[i].wasShorthandPorta=false;
|
||||||
}
|
}
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].portaStop=true;
|
chan[i].portaStop=true;
|
||||||
if (chan[i].keyOn) chan[i].doNote=false;
|
if (chan[i].keyOn) chan[i].doNote=false;
|
||||||
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
|
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
|
||||||
|
@ -765,12 +767,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
case 0xe0: // arp speed
|
case 0xe0: // arp speed
|
||||||
if (effectVal>0) {
|
if (effectVal>0) {
|
||||||
curSubSong->arpLen=effectVal;
|
curSubSong->arpLen=effectVal;
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_HINT_ARP_TIME,i,curSubSong->arpLen));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0xe1: // portamento up
|
case 0xe1: // portamento up
|
||||||
chan[i].portaNote=chan[i].note+(effectVal&15);
|
chan[i].portaNote=chan[i].note+(effectVal&15);
|
||||||
chan[i].portaSpeed=(effectVal>>4)*4;
|
chan[i].portaSpeed=(effectVal>>4)*4;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].portaStop=true;
|
chan[i].portaStop=true;
|
||||||
chan[i].nowYouCanStop=false;
|
chan[i].nowYouCanStop=false;
|
||||||
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
|
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
|
||||||
|
@ -789,7 +792,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
case 0xe2: // portamento down
|
case 0xe2: // portamento down
|
||||||
chan[i].portaNote=chan[i].note-(effectVal&15);
|
chan[i].portaNote=chan[i].note-(effectVal&15);
|
||||||
chan[i].portaSpeed=(effectVal>>4)*4;
|
chan[i].portaSpeed=(effectVal>>4)*4;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].portaStop=true;
|
chan[i].portaStop=true;
|
||||||
chan[i].nowYouCanStop=false;
|
chan[i].nowYouCanStop=false;
|
||||||
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
|
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
|
||||||
|
@ -947,13 +950,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
if (chan[i].inPorta && chan[i].keyOn && !chan[i].shorthandPorta) {
|
if (chan[i].inPorta && chan[i].keyOn && !chan[i].shorthandPorta) {
|
||||||
if (song.e1e2StopOnSameNote && chan[i].wasShorthandPorta) {
|
if (song.e1e2StopOnSameNote && chan[i].wasShorthandPorta) {
|
||||||
chan[i].portaSpeed=-1;
|
chan[i].portaSpeed=-1;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
|
if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
|
||||||
chan[i].wasShorthandPorta=false;
|
chan[i].wasShorthandPorta=false;
|
||||||
chan[i].inPorta=false;
|
chan[i].inPorta=false;
|
||||||
} else {
|
} else {
|
||||||
chan[i].portaNote=chan[i].note;
|
chan[i].portaNote=chan[i].note;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
}
|
}
|
||||||
} else if (!chan[i].noteOnInhibit) {
|
} else if (!chan[i].noteOnInhibit) {
|
||||||
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note,chan[i].volume>>8));
|
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note,chan[i].volume>>8));
|
||||||
|
@ -966,7 +969,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
if (!chan[i].keyOn && chan[i].scheduledSlideReset) {
|
if (!chan[i].keyOn && chan[i].scheduledSlideReset) {
|
||||||
chan[i].portaNote=-1;
|
chan[i].portaNote=-1;
|
||||||
chan[i].portaSpeed=-1;
|
chan[i].portaSpeed=-1;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].scheduledSlideReset=false;
|
chan[i].scheduledSlideReset=false;
|
||||||
chan[i].inPorta=false;
|
chan[i].inPorta=false;
|
||||||
}
|
}
|
||||||
|
@ -1122,11 +1125,23 @@ void DivEngine::nextRow() {
|
||||||
bool wantPreNote=false;
|
bool wantPreNote=false;
|
||||||
if (disCont[dispatchOfChan[i]].dispatch!=NULL) {
|
if (disCont[dispatchOfChan[i]].dispatch!=NULL) {
|
||||||
wantPreNote=disCont[dispatchOfChan[i]].dispatch->getWantPreNote();
|
wantPreNote=disCont[dispatchOfChan[i]].dispatch->getWantPreNote();
|
||||||
if (wantPreNote) dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks));
|
if (wantPreNote) {
|
||||||
|
int addition=0;
|
||||||
|
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||||
|
if (pat->data[curRow][4+(j<<1)]==0xed) {
|
||||||
|
if (pat->data[curRow][5+(j<<1)]>0) {
|
||||||
|
addition=pat->data[curRow][5+(j<<1)];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks+addition));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (song.oneTickCut) {
|
if (song.oneTickCut) {
|
||||||
bool doPrepareCut=true;
|
bool doPrepareCut=true;
|
||||||
|
int addition=0;
|
||||||
|
|
||||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||||
if (pat->data[curRow][4+(j<<1)]==0x03) {
|
if (pat->data[curRow][4+(j<<1)]==0x03) {
|
||||||
|
@ -1139,8 +1154,14 @@ void DivEngine::nextRow() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (pat->data[curRow][4+(j<<1)]==0xed) {
|
||||||
|
if (pat->data[curRow][5+(j<<1)]>0) {
|
||||||
|
addition=pat->data[curRow][5+(j<<1)];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (doPrepareCut && !wantPreNote && chan[i].cut<=0) chan[i].cut=ticks;
|
}
|
||||||
|
}
|
||||||
|
if (doPrepareCut && !wantPreNote && chan[i].cut<=0) chan[i].cut=ticks+addition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1311,7 +1332,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
||||||
if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) {
|
if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) {
|
||||||
if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.linearPitch==2?song.pitchSlideSpeed:1),chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) {
|
if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.linearPitch==2?song.pitchSlideSpeed:1),chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) {
|
||||||
chan[i].portaSpeed=0;
|
chan[i].portaSpeed=0;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].oldNote=chan[i].note;
|
chan[i].oldNote=chan[i].note;
|
||||||
chan[i].note=chan[i].portaNote;
|
chan[i].note=chan[i].portaNote;
|
||||||
chan[i].inPorta=false;
|
chan[i].inPorta=false;
|
||||||
|
@ -1330,13 +1351,13 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
||||||
if (chan[i].stopOnOff) {
|
if (chan[i].stopOnOff) {
|
||||||
chan[i].portaNote=-1;
|
chan[i].portaNote=-1;
|
||||||
chan[i].portaSpeed=-1;
|
chan[i].portaSpeed=-1;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
chan[i].stopOnOff=false;
|
chan[i].stopOnOff=false;
|
||||||
}
|
}
|
||||||
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
|
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
|
||||||
chan[i].portaNote=-1;
|
chan[i].portaNote=-1;
|
||||||
chan[i].portaSpeed=-1;
|
chan[i].portaSpeed=-1;
|
||||||
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
|
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
|
||||||
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
|
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
|
||||||
chan[i+1].portaNote=-1;
|
chan[i+1].portaNote=-1;
|
||||||
chan[i+1].portaSpeed=-1;
|
chan[i+1].portaSpeed=-1;
|
||||||
|
@ -1409,6 +1430,15 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
||||||
ret=true;
|
ret=true;
|
||||||
shallStop=false;
|
shallStop=false;
|
||||||
shallStopSched=false;
|
shallStopSched=false;
|
||||||
|
// reset all chan oscs
|
||||||
|
for (int i=0; i<chans; i++) {
|
||||||
|
DivDispatchOscBuffer* buf=disCont[dispatchOfChan[i]].dispatch->getOscBuffer(dispatchChanOfChan[i]);
|
||||||
|
if (buf!=NULL) {
|
||||||
|
memset(buf->data,0,65536*sizeof(short));
|
||||||
|
buf->needle=0;
|
||||||
|
buf->readNeedle=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1529,12 +1559,15 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
||||||
samp_temp=0;
|
samp_temp=0;
|
||||||
} else {
|
} else {
|
||||||
samp_temp=s->data16[sPreview.pos];
|
samp_temp=s->data16[sPreview.pos];
|
||||||
|
if (--sPreview.posSub<=0) {
|
||||||
|
sPreview.posSub=sPreview.rateMul;
|
||||||
if (sPreview.dir) {
|
if (sPreview.dir) {
|
||||||
sPreview.pos--;
|
sPreview.pos--;
|
||||||
} else {
|
} else {
|
||||||
sPreview.pos++;
|
sPreview.pos++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
blip_add_delta(samp_bb,i,samp_temp-samp_prevSample);
|
blip_add_delta(samp_bb,i,samp_temp-samp_prevSample);
|
||||||
samp_prevSample=samp_temp;
|
samp_prevSample=samp_temp;
|
||||||
|
|
||||||
|
@ -1637,9 +1670,12 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
||||||
} else {
|
} else {
|
||||||
samp_temp=((MIN(wave->data[sPreview.pos],wave->max)<<14)/wave->max)-8192;
|
samp_temp=((MIN(wave->data[sPreview.pos],wave->max)<<14)/wave->max)-8192;
|
||||||
}
|
}
|
||||||
|
if (--sPreview.posSub<=0) {
|
||||||
|
sPreview.posSub=sPreview.rateMul;
|
||||||
if (++sPreview.pos>=wave->len) {
|
if (++sPreview.pos>=wave->len) {
|
||||||
sPreview.pos=0;
|
sPreview.pos=0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
blip_add_delta(samp_bb,i,samp_temp-samp_prevSample);
|
blip_add_delta(samp_bb,i,samp_temp-samp_prevSample);
|
||||||
samp_prevSample=samp_temp;
|
samp_prevSample=samp_temp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,7 +212,6 @@ void DivWaveSynth::setWidth(int val) {
|
||||||
|
|
||||||
void DivWaveSynth::changeWave1(int num) {
|
void DivWaveSynth::changeWave1(int num) {
|
||||||
DivWavetable* w1=e->getWave(num);
|
DivWavetable* w1=e->getWave(num);
|
||||||
logV("changeWave1 (%d)",width);
|
|
||||||
if (width<1) return;
|
if (width<1) return;
|
||||||
for (int i=0; i<width; i++) {
|
for (int i=0; i<width; i++) {
|
||||||
if (w1->max<1 || w1->len<1) {
|
if (w1->max<1 || w1->len<1) {
|
||||||
|
|
|
@ -20,6 +20,13 @@
|
||||||
#include "fileutils.h"
|
#include "fileutils.h"
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "utfutils.h"
|
#include "utfutils.h"
|
||||||
|
#include <windows.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
#include <shlwapi.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FILE* ps_fopen(const char* path, const char* mode) {
|
FILE* ps_fopen(const char* path, const char* mode) {
|
||||||
|
@ -29,3 +36,67 @@ FILE* ps_fopen(const char* path, const char* mode) {
|
||||||
return fopen(path,mode);
|
return fopen(path,mode);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: copy in case of failure
|
||||||
|
bool moveFiles(const char* src, const char* dest) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return MoveFileW(utf8To16(src).c_str(),utf8To16(dest).c_str());
|
||||||
|
#else
|
||||||
|
if (rename(src,dest)==-1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deleteFile(const char* path) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return DeleteFileW(utf8To16(path).c_str());
|
||||||
|
#else
|
||||||
|
return (unlink(path)==0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileExists(const char* path) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (PathFileExistsW(utf8To16(path).c_str())) return 1;
|
||||||
|
// which errors could PathFileExists possibly throw?
|
||||||
|
switch (GetLastError()) {
|
||||||
|
case ERROR_FILE_EXISTS:
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
case ERROR_FILE_NOT_FOUND:
|
||||||
|
case ERROR_PATH_NOT_FOUND:
|
||||||
|
case ERROR_INVALID_DRIVE:
|
||||||
|
case ERROR_DEV_NOT_EXIST:
|
||||||
|
case ERROR_NETNAME_DELETED:
|
||||||
|
case ERROR_BAD_NET_NAME:
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
if (access(path,F_OK)==0) return 1;
|
||||||
|
if (errno==ENOENT) return 0;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dirExists(const char* what) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
WString ws=utf8To16(what);
|
||||||
|
return (PathIsDirectoryW(ws.c_str())!=FALSE);
|
||||||
|
#else
|
||||||
|
struct stat st;
|
||||||
|
if (stat(what,&st)<0) return false;
|
||||||
|
return (st.st_mode&S_IFDIR);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool makeDir(const char* path) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return (SHCreateDirectory(NULL,utf8To16(path).c_str())==ERROR_SUCCESS);
|
||||||
|
#else
|
||||||
|
return (mkdir(path,0755)==0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
@ -22,5 +22,11 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
FILE* ps_fopen(const char* path, const char* mode);
|
FILE* ps_fopen(const char* path, const char* mode);
|
||||||
|
bool moveFiles(const char* src, const char* dest);
|
||||||
|
bool deleteFile(const char* path);
|
||||||
|
// returns 1 if file exists, 0 if it doesn't and -1 on error.
|
||||||
|
int fileExists(const char* path);
|
||||||
|
bool dirExists(const char* what);
|
||||||
|
bool makeDir(const char* path);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -540,9 +540,23 @@ void FurnaceGUI::drawDebug() {
|
||||||
}
|
}
|
||||||
if (ImGui::TreeNode("Performance")) {
|
if (ImGui::TreeNode("Performance")) {
|
||||||
double perfFreq=SDL_GetPerformanceFrequency()/1000000.0;
|
double perfFreq=SDL_GetPerformanceFrequency()/1000000.0;
|
||||||
|
int lastProcTime=(int)e->processTime/1000;
|
||||||
|
TAAudioDesc& audioGot=e->getAudioDescGot();
|
||||||
|
|
||||||
|
ImGui::Text("video frame: %.0fµs",ImGui::GetIO().DeltaTime*1000000.0);
|
||||||
|
ImGui::Text("audio frame: %.0fµs",1000000.0*(double)audioGot.bufsize/(double)audioGot.rate);
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
ImGui::Text("audio: %dµs",lastProcTime);
|
||||||
ImGui::Text("render: %.0fµs",(double)renderTimeDelta/perfFreq);
|
ImGui::Text("render: %.0fµs",(double)renderTimeDelta/perfFreq);
|
||||||
ImGui::Text("layout: %.0fµs",(double)layoutTimeDelta/perfFreq);
|
ImGui::Text("layout: %.0fµs",(double)layoutTimeDelta/perfFreq);
|
||||||
ImGui::Text("event: %.0fµs",(double)eventTimeDelta/perfFreq);
|
ImGui::Text("event: %.0fµs",(double)eventTimeDelta/perfFreq);
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
ImGui::Text("details:");
|
||||||
|
for (int i=0; i<perfMetricsLastLen; i++) {
|
||||||
|
ImGui::Text("%s: %.0fµs",perfMetricsLast[i].name,(double)perfMetricsLast[i].elapsed/perfFreq);
|
||||||
|
}
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
if (ImGui::TreeNode("Settings")) {
|
if (ImGui::TreeNode("Settings")) {
|
||||||
|
|
|
@ -32,6 +32,13 @@ const unsigned char avRequest[15]={
|
||||||
|
|
||||||
void FurnaceGUI::doAction(int what) {
|
void FurnaceGUI::doAction(int what) {
|
||||||
switch (what) {
|
switch (what) {
|
||||||
|
case GUI_ACTION_NEW:
|
||||||
|
if (modified) {
|
||||||
|
showWarning("Unsaved changes! Save changes before creating a new song?",GUI_WARN_NEW);
|
||||||
|
} else {
|
||||||
|
displayNew=true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case GUI_ACTION_OPEN:
|
case GUI_ACTION_OPEN:
|
||||||
if (modified) {
|
if (modified) {
|
||||||
showWarning("Unsaved changes! Save changes before opening another file?",GUI_WARN_OPEN);
|
showWarning("Unsaved changes! Save changes before opening another file?",GUI_WARN_OPEN);
|
||||||
|
@ -43,9 +50,7 @@ void FurnaceGUI::doAction(int what) {
|
||||||
if (modified) {
|
if (modified) {
|
||||||
showWarning("Unsaved changes! Save changes before opening backup?",GUI_WARN_OPEN_BACKUP);
|
showWarning("Unsaved changes! Save changes before opening backup?",GUI_WARN_OPEN_BACKUP);
|
||||||
} else {
|
} else {
|
||||||
if (load(backupPath)>0) {
|
openFileDialog(GUI_FILE_OPEN_BACKUP);
|
||||||
showError("No backup available! (or unable to open it)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GUI_ACTION_SAVE:
|
case GUI_ACTION_SAVE:
|
||||||
|
|
|
@ -524,6 +524,11 @@ void FurnaceGUI::drawMobileControls() {
|
||||||
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
|
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Restore Backup")) {
|
||||||
|
mobileMenuOpen=false;
|
||||||
|
doAction(GUI_ACTION_OPEN_BACKUP);
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
if (ImGui::BeginTabBar("MobileSong")) {
|
if (ImGui::BeginTabBar("MobileSong")) {
|
||||||
|
|
494
src/gui/gui.cpp
494
src/gui/gui.cpp
|
@ -50,13 +50,15 @@
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
#include "../utfutils.h"
|
#include "../utfutils.h"
|
||||||
#define LAYOUT_INI "\\layout.ini"
|
#define LAYOUT_INI "\\layout.ini"
|
||||||
#define BACKUP_FUR "\\backup.fur"
|
#define BACKUPS_DIR "\\backups"
|
||||||
#else
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#define LAYOUT_INI "/layout.ini"
|
#define LAYOUT_INI "/layout.ini"
|
||||||
#define BACKUP_FUR "/backup.fur"
|
#define BACKUPS_DIR "/backups"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef IS_MOBILE
|
#ifdef IS_MOBILE
|
||||||
|
@ -528,18 +530,22 @@ void FurnaceGUI::setFileName(String name) {
|
||||||
if (index>=4095) break;
|
if (index>=4095) break;
|
||||||
}
|
}
|
||||||
ret[index]=0;
|
ret[index]=0;
|
||||||
|
backupLock.lock();
|
||||||
if (GetFullPathNameW(ws.c_str(),4095,ret,NULL)==0) {
|
if (GetFullPathNameW(ws.c_str(),4095,ret,NULL)==0) {
|
||||||
curFileName=name;
|
curFileName=name;
|
||||||
} else {
|
} else {
|
||||||
curFileName=utf16To8(ret);
|
curFileName=utf16To8(ret);
|
||||||
}
|
}
|
||||||
|
backupLock.unlock();
|
||||||
#else
|
#else
|
||||||
char ret[4096];
|
char ret[4096];
|
||||||
|
backupLock.lock();
|
||||||
if (realpath(name.c_str(),ret)==NULL) {
|
if (realpath(name.c_str(),ret)==NULL) {
|
||||||
curFileName=name;
|
curFileName=name;
|
||||||
} else {
|
} else {
|
||||||
curFileName=ret;
|
curFileName=ret;
|
||||||
}
|
}
|
||||||
|
backupLock.unlock();
|
||||||
#endif
|
#endif
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
pushRecentFile(curFileName);
|
pushRecentFile(curFileName);
|
||||||
|
@ -1076,10 +1082,6 @@ void FurnaceGUI::stop() {
|
||||||
e->stop();
|
e->stop();
|
||||||
curNibble=false;
|
curNibble=false;
|
||||||
orderNibble=false;
|
orderNibble=false;
|
||||||
activeNotes.clear();
|
|
||||||
memset(chanOscVol,0,DIV_MAX_CHANS*sizeof(float));
|
|
||||||
memset(chanOscPitch,0,DIV_MAX_CHANS*sizeof(float));
|
|
||||||
memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) {
|
void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) {
|
||||||
|
@ -1325,6 +1327,41 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sampleMapWaitingInput) {
|
||||||
|
if (sampleMapColumn==1) {
|
||||||
|
// TODO: map?
|
||||||
|
if (ev.key.keysym.scancode==SDL_SCANCODE_DELETE) {
|
||||||
|
alterSampleMap(true,-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int key=noteKeys.at(ev.key.keysym.scancode);
|
||||||
|
int num=12*curOctave+key;
|
||||||
|
|
||||||
|
if (num<-60) num=-60; // C-(-5)
|
||||||
|
if (num>119) num=119; // B-9
|
||||||
|
|
||||||
|
alterSampleMap(true,num);
|
||||||
|
return;
|
||||||
|
} catch (std::out_of_range& e) {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: map?
|
||||||
|
if (ev.key.keysym.scancode==SDL_SCANCODE_DELETE) {
|
||||||
|
alterSampleMap(false,-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int num=valueKeys.at(ev.key.keysym.sym);
|
||||||
|
if (num<10) {
|
||||||
|
alterSampleMap(false,num);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (std::out_of_range& e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PER-WINDOW KEYS
|
// PER-WINDOW KEYS
|
||||||
switch (curWindow) {
|
switch (curWindow) {
|
||||||
case GUI_WINDOW_PATTERN:
|
case GUI_WINDOW_PATTERN:
|
||||||
|
@ -1458,15 +1495,8 @@ void FurnaceGUI::keyUp(SDL_Event& ev) {
|
||||||
// nothing for now
|
// nothing for now
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dirExists(String what) {
|
bool dirExists(String s) {
|
||||||
#ifdef _WIN32
|
return dirExists(s.c_str());
|
||||||
WString ws=utf8To16(what.c_str());
|
|
||||||
return (PathIsDirectoryW(ws.c_str())!=FALSE);
|
|
||||||
#else
|
|
||||||
struct stat st;
|
|
||||||
if (stat(what.c_str(),&st)<0) return false;
|
|
||||||
return (st.st_mode&S_IFDIR);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
||||||
|
@ -1483,6 +1513,19 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
||||||
dpiScale
|
dpiScale
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case GUI_FILE_OPEN_BACKUP:
|
||||||
|
if (!dirExists(backupPath)) {
|
||||||
|
showError("no backups made yet!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hasOpened=fileDialog->openLoad(
|
||||||
|
"Restore Backup",
|
||||||
|
{"Furnace song", "*.fur"},
|
||||||
|
"Furnace song{.fur}",
|
||||||
|
backupPath+String(DIR_SEPARATOR_STR),
|
||||||
|
dpiScale
|
||||||
|
);
|
||||||
|
break;
|
||||||
case GUI_FILE_SAVE:
|
case GUI_FILE_SAVE:
|
||||||
if (!dirExists(workingDirSong)) workingDirSong=getHomeDir();
|
if (!dirExists(workingDirSong)) workingDirSong=getHomeDir();
|
||||||
hasOpened=fileDialog->openSave(
|
hasOpened=fileDialog->openSave(
|
||||||
|
@ -2009,7 +2052,9 @@ int FurnaceGUI::save(String path, int dmfVersion) {
|
||||||
#endif
|
#endif
|
||||||
fclose(outFile);
|
fclose(outFile);
|
||||||
w->finish();
|
w->finish();
|
||||||
|
backupLock.lock();
|
||||||
curFileName=path;
|
curFileName=path;
|
||||||
|
backupLock.unlock();
|
||||||
modified=false;
|
modified=false;
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
if (!e->getWarnings().empty()) {
|
if (!e->getWarnings().empty()) {
|
||||||
|
@ -2074,7 +2119,9 @@ int FurnaceGUI::load(String path) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
backupLock.lock();
|
||||||
curFileName=path;
|
curFileName=path;
|
||||||
|
backupLock.unlock();
|
||||||
modified=false;
|
modified=false;
|
||||||
curNibble=false;
|
curNibble=false;
|
||||||
orderNibble=false;
|
orderNibble=false;
|
||||||
|
@ -2100,7 +2147,7 @@ int FurnaceGUI::load(String path) {
|
||||||
|
|
||||||
void FurnaceGUI::pushRecentFile(String path) {
|
void FurnaceGUI::pushRecentFile(String path) {
|
||||||
if (path.empty()) return;
|
if (path.empty()) return;
|
||||||
if (path==backupPath) return;
|
if (path.find(backupPath)==0) return;
|
||||||
for (int i=0; i<(int)recentFile.size(); i++) {
|
for (int i=0; i<(int)recentFile.size(); i++) {
|
||||||
if (recentFile[i]==path) {
|
if (recentFile[i]==path) {
|
||||||
recentFile.erase(recentFile.begin()+i);
|
recentFile.erase(recentFile.begin()+i);
|
||||||
|
@ -2114,6 +2161,44 @@ void FurnaceGUI::pushRecentFile(String path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FurnaceGUI::delFirstBackup(String name) {
|
||||||
|
std::vector<String> listOfFiles;
|
||||||
|
#ifdef _WIN32
|
||||||
|
String findPath=backupPath+String(DIR_SEPARATOR_STR)+name+String("*.fur");
|
||||||
|
WIN32_FIND_DATAW next;
|
||||||
|
HANDLE backDir=FindFirstFileW(utf8To16(findPath.c_str()).c_str(),&next);
|
||||||
|
if (backDir!=INVALID_HANDLE_VALUE) {
|
||||||
|
do {
|
||||||
|
listOfFiles.push_back(utf16To8(next.cFileName));
|
||||||
|
} while (FindNextFileW(backDir,&next)!=0);
|
||||||
|
FindClose(backDir);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
DIR* backDir=opendir(backupPath.c_str());
|
||||||
|
if (backDir==NULL) {
|
||||||
|
logW("could not open backups dir!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
struct dirent* next=readdir(backDir);
|
||||||
|
if (next==NULL) break;
|
||||||
|
if (strstr(next->d_name,name.c_str())!=next->d_name) continue;
|
||||||
|
listOfFiles.push_back(String(next->d_name));
|
||||||
|
}
|
||||||
|
closedir(backDir);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::sort(listOfFiles.begin(),listOfFiles.end(),[](const String& a, const String& b) -> bool {
|
||||||
|
return strcmp(a.c_str(),b.c_str())<0;
|
||||||
|
});
|
||||||
|
|
||||||
|
int totalDelete=((int)listOfFiles.size())-5;
|
||||||
|
for (int i=0; i<totalDelete; i++) {
|
||||||
|
String toDelete=backupPath+String(DIR_SEPARATOR_STR)+listOfFiles[i];
|
||||||
|
deleteFile(toDelete.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int FurnaceGUI::loadStream(String path) {
|
int FurnaceGUI::loadStream(String path) {
|
||||||
if (!path.empty()) {
|
if (!path.empty()) {
|
||||||
logI("loading stream...");
|
logI("loading stream...");
|
||||||
|
@ -2786,7 +2871,9 @@ void FurnaceGUI::editOptions(bool topMenu) {
|
||||||
void FurnaceGUI::toggleMobileUI(bool enable, bool force) {
|
void FurnaceGUI::toggleMobileUI(bool enable, bool force) {
|
||||||
if (mobileUI!=enable || force) {
|
if (mobileUI!=enable || force) {
|
||||||
if (!mobileUI && enable) {
|
if (!mobileUI && enable) {
|
||||||
ImGui::SaveIniSettingsToDisk(finalLayoutPath);
|
if (!ImGui::SaveIniSettingsToDisk(finalLayoutPath,true)) {
|
||||||
|
reportError(fmt::sprintf("could NOT save layout! %s",strerror(errno)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mobileUI=enable;
|
mobileUI=enable;
|
||||||
if (mobileUI) {
|
if (mobileUI) {
|
||||||
|
@ -2796,7 +2883,10 @@ void FurnaceGUI::toggleMobileUI(bool enable, bool force) {
|
||||||
fileDialog->mobileUI=true;
|
fileDialog->mobileUI=true;
|
||||||
} else {
|
} else {
|
||||||
ImGui::GetIO().IniFilename=NULL;
|
ImGui::GetIO().IniFilename=NULL;
|
||||||
ImGui::LoadIniSettingsFromDisk(finalLayoutPath);
|
if (!ImGui::LoadIniSettingsFromDisk(finalLayoutPath,true)) {
|
||||||
|
reportError(fmt::sprintf("could NOT load layout! %s",strerror(errno)));
|
||||||
|
ImGui::LoadIniSettingsFromMemory(defaultLayout);
|
||||||
|
}
|
||||||
ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_InertialScrollEnable;
|
ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_InertialScrollEnable;
|
||||||
ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NoHoverColors;
|
ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NoHoverColors;
|
||||||
fileDialog->mobileUI=false;
|
fileDialog->mobileUI=false;
|
||||||
|
@ -2850,7 +2940,7 @@ int FurnaceGUI::processEvent(SDL_Event* ev) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (ev->type==SDL_KEYDOWN) {
|
if (ev->type==SDL_KEYDOWN) {
|
||||||
if (!ev->key.repeat && latchTarget==0 && !wantCaptureKeyboard && (ev->key.keysym.mod&(~(KMOD_NUM|KMOD_CAPS|KMOD_SCROLL)))==0) {
|
if (!ev->key.repeat && latchTarget==0 && !wantCaptureKeyboard && !sampleMapWaitingInput && (ev->key.keysym.mod&(~(KMOD_NUM|KMOD_CAPS|KMOD_SCROLL)))==0) {
|
||||||
if (settings.notePreviewBehavior==0) return 1;
|
if (settings.notePreviewBehavior==0) return 1;
|
||||||
switch (curWindow) {
|
switch (curWindow) {
|
||||||
case GUI_WINDOW_SAMPLE_EDIT:
|
case GUI_WINDOW_SAMPLE_EDIT:
|
||||||
|
@ -3181,7 +3271,62 @@ bool FurnaceGUI::detectOutOfBoundsWindow() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DECLARE_METRIC(_n) \
|
||||||
|
int __perfM##_n;
|
||||||
|
|
||||||
|
#define MEASURE_BEGIN(_n) \
|
||||||
|
__perfM##_n=SDL_GetPerformanceCounter();
|
||||||
|
|
||||||
|
#define MEASURE_END(_n) \
|
||||||
|
if (perfMetricsLen<64) { \
|
||||||
|
perfMetrics[perfMetricsLen++]=FurnaceGUIPerfMetric(#_n,SDL_GetPerformanceCounter()-__perfM##_n); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MEASURE(_n,_x) \
|
||||||
|
MEASURE_BEGIN(_n) \
|
||||||
|
_x; \
|
||||||
|
MEASURE_END(_n)
|
||||||
|
|
||||||
bool FurnaceGUI::loop() {
|
bool FurnaceGUI::loop() {
|
||||||
|
DECLARE_METRIC(calcChanOsc)
|
||||||
|
DECLARE_METRIC(mobileControls)
|
||||||
|
DECLARE_METRIC(mobileOrderSel)
|
||||||
|
DECLARE_METRIC(subSongs)
|
||||||
|
DECLARE_METRIC(findReplace)
|
||||||
|
DECLARE_METRIC(spoiler)
|
||||||
|
DECLARE_METRIC(pattern)
|
||||||
|
DECLARE_METRIC(editControls)
|
||||||
|
DECLARE_METRIC(speed)
|
||||||
|
DECLARE_METRIC(grooves)
|
||||||
|
DECLARE_METRIC(songInfo)
|
||||||
|
DECLARE_METRIC(orders)
|
||||||
|
DECLARE_METRIC(intro)
|
||||||
|
DECLARE_METRIC(sampleList)
|
||||||
|
DECLARE_METRIC(sampleEdit)
|
||||||
|
DECLARE_METRIC(waveList)
|
||||||
|
DECLARE_METRIC(waveEdit)
|
||||||
|
DECLARE_METRIC(insList)
|
||||||
|
DECLARE_METRIC(insEdit)
|
||||||
|
DECLARE_METRIC(mixer)
|
||||||
|
DECLARE_METRIC(readOsc)
|
||||||
|
DECLARE_METRIC(osc)
|
||||||
|
DECLARE_METRIC(chanOsc)
|
||||||
|
DECLARE_METRIC(volMeter)
|
||||||
|
DECLARE_METRIC(settings)
|
||||||
|
DECLARE_METRIC(debug)
|
||||||
|
DECLARE_METRIC(stats)
|
||||||
|
DECLARE_METRIC(compatFlags)
|
||||||
|
DECLARE_METRIC(piano)
|
||||||
|
DECLARE_METRIC(notes)
|
||||||
|
DECLARE_METRIC(channels)
|
||||||
|
DECLARE_METRIC(patManager)
|
||||||
|
DECLARE_METRIC(sysManager)
|
||||||
|
DECLARE_METRIC(clock)
|
||||||
|
DECLARE_METRIC(regView)
|
||||||
|
DECLARE_METRIC(log)
|
||||||
|
DECLARE_METRIC(effectList)
|
||||||
|
DECLARE_METRIC(popup)
|
||||||
|
|
||||||
#ifdef IS_MOBILE
|
#ifdef IS_MOBILE
|
||||||
bool doThreadedInput=true;
|
bool doThreadedInput=true;
|
||||||
#else
|
#else
|
||||||
|
@ -3203,6 +3348,11 @@ bool FurnaceGUI::loop() {
|
||||||
drawHalt=0;
|
drawHalt=0;
|
||||||
if (settings.powerSave) SDL_WaitEventTimeout(NULL,500);
|
if (settings.powerSave) SDL_WaitEventTimeout(NULL,500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(perfMetricsLast,perfMetrics,64*sizeof(FurnaceGUIPerfMetric));
|
||||||
|
perfMetricsLastLen=perfMetricsLen;
|
||||||
|
perfMetricsLen=0;
|
||||||
|
|
||||||
eventTimeBegin=SDL_GetPerformanceCounter();
|
eventTimeBegin=SDL_GetPerformanceCounter();
|
||||||
bool updateWindow=false;
|
bool updateWindow=false;
|
||||||
if (injectBackUp) {
|
if (injectBackUp) {
|
||||||
|
@ -3568,6 +3718,23 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!e->isRunning()) {
|
||||||
|
activeNotes.clear();
|
||||||
|
memset(chanOscVol,0,DIV_MAX_CHANS*sizeof(float));
|
||||||
|
memset(chanOscPitch,0,DIV_MAX_CHANS*sizeof(float));
|
||||||
|
memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float));
|
||||||
|
|
||||||
|
e->synchronized([this]() {
|
||||||
|
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||||
|
DivDispatchOscBuffer* buf=e->getOscBuffer(i);
|
||||||
|
if (buf!=NULL) {
|
||||||
|
buf->needle=0;
|
||||||
|
buf->readNeedle=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
layoutTimeBegin=SDL_GetPerformanceCounter();
|
layoutTimeBegin=SDL_GetPerformanceCounter();
|
||||||
|
|
||||||
ImGui_ImplSDLRenderer_NewFrame();
|
ImGui_ImplSDLRenderer_NewFrame();
|
||||||
|
@ -3581,7 +3748,7 @@ bool FurnaceGUI::loop() {
|
||||||
if (!mobileUI) {
|
if (!mobileUI) {
|
||||||
ImGui::BeginMainMenuBar();
|
ImGui::BeginMainMenuBar();
|
||||||
if (ImGui::BeginMenu("file")) {
|
if (ImGui::BeginMenu("file")) {
|
||||||
if (ImGui::MenuItem("new...")) {
|
if (ImGui::MenuItem("new...",BIND_FOR(GUI_ACTION_NEW))) {
|
||||||
if (modified) {
|
if (modified) {
|
||||||
showWarning("Unsaved changes! Save changes before creating a new song?",GUI_WARN_NEW);
|
showWarning("Unsaved changes! Save changes before creating a new song?",GUI_WARN_NEW);
|
||||||
} else {
|
} else {
|
||||||
|
@ -3623,7 +3790,7 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (ImGui::MenuItem("save",BIND_FOR(GUI_ACTION_SAVE))) {
|
if (ImGui::MenuItem("save",BIND_FOR(GUI_ACTION_SAVE))) {
|
||||||
if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) {
|
if (curFileName=="" || (curFileName.find(backupPath)==0) || e->song.version>=0xff00) {
|
||||||
openFileDialog(GUI_FILE_SAVE);
|
openFileDialog(GUI_FILE_SAVE);
|
||||||
} else {
|
} else {
|
||||||
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
|
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
|
||||||
|
@ -3671,11 +3838,11 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
ImGui::Checkbox("loop",&vgmExportLoop);
|
ImGui::Checkbox("loop",&vgmExportLoop);
|
||||||
if (vgmExportLoop && e->song.loopModality==2) {
|
if (vgmExportLoop && e->song.loopModality==2) {
|
||||||
ImGui::Text("trailing ticks:");
|
ImGui::Text("loop trail:");
|
||||||
if (ImGui::RadioButton("auto-detect",vgmExportTrailingTicks==-1)) {
|
if (ImGui::RadioButton("auto-detect",vgmExportTrailingTicks==-1)) {
|
||||||
vgmExportTrailingTicks=-1;
|
vgmExportTrailingTicks=-1;
|
||||||
}
|
}
|
||||||
if (ImGui::RadioButton("one loop",vgmExportTrailingTicks==-2)) {
|
if (ImGui::RadioButton("add one loop",vgmExportTrailingTicks==-2)) {
|
||||||
vgmExportTrailingTicks=-2;
|
vgmExportTrailingTicks=-2;
|
||||||
}
|
}
|
||||||
if (ImGui::RadioButton("custom",vgmExportTrailingTicks>=0)) {
|
if (ImGui::RadioButton("custom",vgmExportTrailingTicks>=0)) {
|
||||||
|
@ -4074,83 +4241,85 @@ bool FurnaceGUI::loop() {
|
||||||
ImGui::EndMainMenuBar();
|
ImGui::EndMainMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
calcChanOsc();
|
MEASURE(calcChanOsc,calcChanOsc());
|
||||||
|
|
||||||
if (mobileUI) {
|
if (mobileUI) {
|
||||||
globalWinFlags=ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoBringToFrontOnFocus;
|
globalWinFlags=ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoBringToFrontOnFocus;
|
||||||
//globalWinFlags=ImGuiWindowFlags_NoTitleBar;
|
//globalWinFlags=ImGuiWindowFlags_NoTitleBar;
|
||||||
// scene handling goes here!
|
// scene handling goes here!
|
||||||
drawMobileControls();
|
MEASURE(mobileControls,drawMobileControls());
|
||||||
switch (mobScene) {
|
switch (mobScene) {
|
||||||
case GUI_SCENE_ORDERS:
|
case GUI_SCENE_ORDERS:
|
||||||
ordersOpen=true;
|
ordersOpen=true;
|
||||||
curWindow=GUI_WINDOW_ORDERS;
|
curWindow=GUI_WINDOW_ORDERS;
|
||||||
drawOrders();
|
MEASURE(orders,drawOrders());
|
||||||
break;
|
break;
|
||||||
case GUI_SCENE_INSTRUMENT:
|
case GUI_SCENE_INSTRUMENT:
|
||||||
insEditOpen=true;
|
insEditOpen=true;
|
||||||
curWindow=GUI_WINDOW_INS_EDIT;
|
curWindow=GUI_WINDOW_INS_EDIT;
|
||||||
drawInsEdit();
|
MEASURE(insEdit,drawInsEdit());
|
||||||
drawPiano();
|
MEASURE(piano,drawPiano());
|
||||||
break;
|
break;
|
||||||
case GUI_SCENE_WAVETABLE:
|
case GUI_SCENE_WAVETABLE:
|
||||||
waveEditOpen=true;
|
waveEditOpen=true;
|
||||||
curWindow=GUI_WINDOW_WAVE_EDIT;
|
curWindow=GUI_WINDOW_WAVE_EDIT;
|
||||||
drawWaveEdit();
|
MEASURE(waveEdit,drawWaveEdit());
|
||||||
drawPiano();
|
MEASURE(piano,drawPiano());
|
||||||
break;
|
break;
|
||||||
case GUI_SCENE_SAMPLE:
|
case GUI_SCENE_SAMPLE:
|
||||||
sampleEditOpen=true;
|
sampleEditOpen=true;
|
||||||
curWindow=GUI_WINDOW_SAMPLE_EDIT;
|
curWindow=GUI_WINDOW_SAMPLE_EDIT;
|
||||||
drawSampleEdit();
|
MEASURE(sampleEdit,drawSampleEdit());
|
||||||
drawPiano();
|
MEASURE(piano,drawPiano());
|
||||||
break;
|
break;
|
||||||
case GUI_SCENE_CHANNELS:
|
case GUI_SCENE_CHANNELS:
|
||||||
channelsOpen=true;
|
channelsOpen=true;
|
||||||
curWindow=GUI_WINDOW_CHANNELS;
|
curWindow=GUI_WINDOW_CHANNELS;
|
||||||
drawChannels();
|
MEASURE(channels,drawChannels());
|
||||||
break;
|
break;
|
||||||
case GUI_SCENE_CHIPS:
|
case GUI_SCENE_CHIPS:
|
||||||
sysManagerOpen=true;
|
sysManagerOpen=true;
|
||||||
curWindow=GUI_WINDOW_SYS_MANAGER;
|
curWindow=GUI_WINDOW_SYS_MANAGER;
|
||||||
drawSysManager();
|
MEASURE(sysManager,drawSysManager());
|
||||||
break;
|
break;
|
||||||
case GUI_SCENE_MIXER:
|
case GUI_SCENE_MIXER:
|
||||||
mixerOpen=true;
|
mixerOpen=true;
|
||||||
curWindow=GUI_WINDOW_MIXER;
|
curWindow=GUI_WINDOW_MIXER;
|
||||||
drawMixer();
|
MEASURE(mixer,drawMixer());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
patternOpen=true;
|
patternOpen=true;
|
||||||
curWindow=GUI_WINDOW_PATTERN;
|
curWindow=GUI_WINDOW_PATTERN;
|
||||||
drawPattern();
|
MEASURE(pattern,drawPattern());
|
||||||
drawPiano();
|
MEASURE(piano,drawPiano());
|
||||||
drawMobileOrderSel();
|
MEASURE(mobileOrderSel,drawMobileOrderSel());
|
||||||
|
|
||||||
globalWinFlags=0;
|
globalWinFlags=0;
|
||||||
drawFindReplace();
|
MEASURE(findReplace,drawFindReplace());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
globalWinFlags=0;
|
globalWinFlags=0;
|
||||||
drawSettings();
|
MEASURE(settings,drawSettings());
|
||||||
drawDebug();
|
MEASURE(debug,drawDebug());
|
||||||
drawLog();
|
MEASURE(log,drawLog());
|
||||||
drawCompatFlags();
|
MEASURE(compatFlags,drawCompatFlags());
|
||||||
drawStats();
|
MEASURE(stats,drawStats());
|
||||||
} else {
|
} else {
|
||||||
globalWinFlags=0;
|
globalWinFlags=0;
|
||||||
ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoWindowMenuButton|ImGuiDockNodeFlags_NoMove|ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0);
|
ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoWindowMenuButton|ImGuiDockNodeFlags_NoMove|ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0);
|
||||||
|
|
||||||
drawSubSongs();
|
MEASURE(subSongs,drawSubSongs());
|
||||||
drawFindReplace();
|
MEASURE(findReplace,drawFindReplace());
|
||||||
drawSpoiler();
|
MEASURE(spoiler,drawSpoiler());
|
||||||
drawPattern();
|
MEASURE(pattern,drawPattern());
|
||||||
drawEditControls();
|
MEASURE(editControls,drawEditControls());
|
||||||
drawSpeed();
|
MEASURE(speed,drawSpeed());
|
||||||
if (!basicMode) drawGrooves();
|
if (!basicMode) {
|
||||||
drawSongInfo();
|
MEASURE(grooves,drawGrooves());
|
||||||
drawOrders();
|
}
|
||||||
|
MEASURE(songInfo,drawSongInfo());
|
||||||
|
MEASURE(orders,drawOrders());
|
||||||
if (introMonOpen) {
|
if (introMonOpen) {
|
||||||
int totalTicks=e->getTotalTicks();
|
int totalTicks=e->getTotalTicks();
|
||||||
int totalSeconds=e->getTotalSeconds();
|
int totalSeconds=e->getTotalSeconds();
|
||||||
|
@ -4162,36 +4331,38 @@ bool FurnaceGUI::loop() {
|
||||||
|
|
||||||
if (e->isPlaying()) monitorPos+=ImGui::GetIO().DeltaTime;
|
if (e->isPlaying()) monitorPos+=ImGui::GetIO().DeltaTime;
|
||||||
}
|
}
|
||||||
drawSampleList();
|
MEASURE(sampleList,drawSampleList());
|
||||||
drawSampleEdit();
|
MEASURE(sampleEdit,drawSampleEdit());
|
||||||
drawWaveList();
|
MEASURE(waveList,drawWaveList());
|
||||||
drawWaveEdit();
|
MEASURE(waveEdit,drawWaveEdit());
|
||||||
drawInsList();
|
MEASURE(insList,drawInsList());
|
||||||
drawInsEdit();
|
MEASURE(insEdit,drawInsEdit());
|
||||||
drawMixer();
|
MEASURE(mixer,drawMixer());
|
||||||
|
|
||||||
readOsc();
|
MEASURE(readOsc,readOsc());
|
||||||
|
|
||||||
drawOsc();
|
MEASURE(osc,drawOsc());
|
||||||
drawChanOsc();
|
MEASURE(chanOsc,drawChanOsc());
|
||||||
drawVolMeter();
|
MEASURE(volMeter,drawVolMeter());
|
||||||
drawSettings();
|
MEASURE(settings,drawSettings());
|
||||||
drawDebug();
|
MEASURE(debug,drawDebug());
|
||||||
drawStats();
|
MEASURE(stats,drawStats());
|
||||||
if (!basicMode) drawCompatFlags();
|
|
||||||
drawPiano();
|
|
||||||
drawNotes();
|
|
||||||
if (!basicMode) {
|
if (!basicMode) {
|
||||||
drawChannels();
|
MEASURE(compatFlags,drawCompatFlags());
|
||||||
}
|
}
|
||||||
drawPatManager();
|
MEASURE(piano,drawPiano());
|
||||||
|
MEASURE(notes,drawNotes());
|
||||||
if (!basicMode) {
|
if (!basicMode) {
|
||||||
drawSysManager();
|
MEASURE(channels,drawChannels());
|
||||||
}
|
}
|
||||||
drawClock();
|
MEASURE(patManager,drawPatManager());
|
||||||
drawRegView();
|
if (!basicMode) {
|
||||||
drawLog();
|
MEASURE(sysManager,drawSysManager());
|
||||||
drawEffectList();
|
}
|
||||||
|
MEASURE(clock,drawClock());
|
||||||
|
MEASURE(regView,drawRegView());
|
||||||
|
MEASURE(log,drawLog());
|
||||||
|
MEASURE(effectList,drawEffectList());
|
||||||
}
|
}
|
||||||
|
|
||||||
activateTutorial(GUI_TUTORIAL_OVERVIEW);
|
activateTutorial(GUI_TUTORIAL_OVERVIEW);
|
||||||
|
@ -4318,6 +4489,8 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_FILE_TEST_SAVE:
|
case GUI_FILE_TEST_SAVE:
|
||||||
workingDirTest=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
workingDirTest=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
||||||
break;
|
break;
|
||||||
|
case GUI_FILE_OPEN_BACKUP:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (fileDialog->isError()) {
|
if (fileDialog->isError()) {
|
||||||
#if defined(_WIN32) || defined(__APPLE__)
|
#if defined(_WIN32) || defined(__APPLE__)
|
||||||
|
@ -4394,6 +4567,7 @@ bool FurnaceGUI::loop() {
|
||||||
String copyOfName=fileName;
|
String copyOfName=fileName;
|
||||||
switch (curFileDialog) {
|
switch (curFileDialog) {
|
||||||
case GUI_FILE_OPEN:
|
case GUI_FILE_OPEN:
|
||||||
|
case GUI_FILE_OPEN_BACKUP:
|
||||||
if (load(copyOfName)>0) {
|
if (load(copyOfName)>0) {
|
||||||
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
|
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
|
||||||
}
|
}
|
||||||
|
@ -4422,9 +4596,7 @@ bool FurnaceGUI::loop() {
|
||||||
nextFile="";
|
nextFile="";
|
||||||
break;
|
break;
|
||||||
case GUI_WARN_OPEN_BACKUP:
|
case GUI_WARN_OPEN_BACKUP:
|
||||||
if (load(backupPath)>0) {
|
openFileDialog(GUI_FILE_OPEN_BACKUP);
|
||||||
showError("No backup available! (or unable to open it)");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -4840,6 +5012,8 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (displayNew) {
|
if (displayNew) {
|
||||||
|
newSongQuery="";
|
||||||
|
newSongFirstFrame=true;
|
||||||
displayNew=false;
|
displayNew=false;
|
||||||
ImGui::OpenPopup("New Song");
|
ImGui::OpenPopup("New Song");
|
||||||
}
|
}
|
||||||
|
@ -4850,6 +5024,8 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
if (aboutOpen) drawAbout();
|
if (aboutOpen) drawAbout();
|
||||||
|
|
||||||
|
MEASURE_BEGIN(popup);
|
||||||
|
|
||||||
if (ImGui::BeginPopupModal("Rendering...",NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
|
if (ImGui::BeginPopupModal("Rendering...",NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
ImGui::Text("Please wait...");
|
ImGui::Text("Please wait...");
|
||||||
if (ImGui::Button("Abort")) {
|
if (ImGui::Button("Abort")) {
|
||||||
|
@ -4891,7 +5067,7 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_WARN_QUIT:
|
case GUI_WARN_QUIT:
|
||||||
if (ImGui::Button("Yes")) {
|
if (ImGui::Button("Yes")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) {
|
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
|
||||||
openFileDialog(GUI_FILE_SAVE);
|
openFileDialog(GUI_FILE_SAVE);
|
||||||
postWarnAction=GUI_WARN_QUIT;
|
postWarnAction=GUI_WARN_QUIT;
|
||||||
} else {
|
} else {
|
||||||
|
@ -4915,7 +5091,7 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_WARN_NEW:
|
case GUI_WARN_NEW:
|
||||||
if (ImGui::Button("Yes")) {
|
if (ImGui::Button("Yes")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) {
|
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
|
||||||
openFileDialog(GUI_FILE_SAVE);
|
openFileDialog(GUI_FILE_SAVE);
|
||||||
postWarnAction=GUI_WARN_NEW;
|
postWarnAction=GUI_WARN_NEW;
|
||||||
} else {
|
} else {
|
||||||
|
@ -4939,7 +5115,7 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_WARN_OPEN:
|
case GUI_WARN_OPEN:
|
||||||
if (ImGui::Button("Yes")) {
|
if (ImGui::Button("Yes")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) {
|
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
|
||||||
openFileDialog(GUI_FILE_SAVE);
|
openFileDialog(GUI_FILE_SAVE);
|
||||||
postWarnAction=GUI_WARN_OPEN;
|
postWarnAction=GUI_WARN_OPEN;
|
||||||
} else {
|
} else {
|
||||||
|
@ -4963,25 +5139,21 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_WARN_OPEN_BACKUP:
|
case GUI_WARN_OPEN_BACKUP:
|
||||||
if (ImGui::Button("Yes")) {
|
if (ImGui::Button("Yes")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) {
|
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
|
||||||
openFileDialog(GUI_FILE_SAVE);
|
openFileDialog(GUI_FILE_SAVE);
|
||||||
postWarnAction=GUI_WARN_OPEN_BACKUP;
|
postWarnAction=GUI_WARN_OPEN_BACKUP;
|
||||||
} else {
|
} else {
|
||||||
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
|
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
|
||||||
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
|
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
|
||||||
} else {
|
} else {
|
||||||
if (load(backupPath)>0) {
|
openFileDialog(GUI_FILE_OPEN_BACKUP);
|
||||||
showError("No backup available! (or unable to open it)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("No")) {
|
if (ImGui::Button("No")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (load(backupPath)>0) {
|
openFileDialog(GUI_FILE_OPEN_BACKUP);
|
||||||
showError("No backup available! (or unable to open it)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Cancel")) {
|
if (ImGui::Button("Cancel")) {
|
||||||
|
@ -4991,7 +5163,7 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_WARN_OPEN_DROP:
|
case GUI_WARN_OPEN_DROP:
|
||||||
if (ImGui::Button("Yes")) {
|
if (ImGui::Button("Yes")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) {
|
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
|
||||||
openFileDialog(GUI_FILE_SAVE);
|
openFileDialog(GUI_FILE_SAVE);
|
||||||
postWarnAction=GUI_WARN_OPEN_DROP;
|
postWarnAction=GUI_WARN_OPEN_DROP;
|
||||||
} else {
|
} else {
|
||||||
|
@ -5025,7 +5197,9 @@ bool FurnaceGUI::loop() {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
if (!mobileUI) {
|
if (!mobileUI) {
|
||||||
ImGui::LoadIniSettingsFromMemory(defaultLayout);
|
ImGui::LoadIniSettingsFromMemory(defaultLayout);
|
||||||
ImGui::SaveIniSettingsToDisk(finalLayoutPath);
|
if (!ImGui::SaveIniSettingsToDisk(finalLayoutPath,true)) {
|
||||||
|
reportError(fmt::sprintf("could NOT save layout! %s",strerror(errno)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
@ -5383,12 +5557,16 @@ bool FurnaceGUI::loop() {
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MEASURE_END(popup);
|
||||||
|
|
||||||
if (!tutorial.introPlayed || settings.alwaysPlayIntro!=0) {
|
if (!tutorial.introPlayed || settings.alwaysPlayIntro!=0) {
|
||||||
|
MEASURE_BEGIN(intro);
|
||||||
initialScreenWipe=0;
|
initialScreenWipe=0;
|
||||||
if (settings.alwaysPlayIntro==1) {
|
if (settings.alwaysPlayIntro==1) {
|
||||||
shortIntro=true;
|
shortIntro=true;
|
||||||
}
|
}
|
||||||
drawIntro(introPos);
|
drawIntro(introPos);
|
||||||
|
MEASURE_END(intro);
|
||||||
} else {
|
} else {
|
||||||
introPos=12.0;
|
introPos=12.0;
|
||||||
}
|
}
|
||||||
|
@ -5401,16 +5579,73 @@ bool FurnaceGUI::loop() {
|
||||||
backupTimer=(backupTimer-ImGui::GetIO().DeltaTime);
|
backupTimer=(backupTimer-ImGui::GetIO().DeltaTime);
|
||||||
if (backupTimer<=0) {
|
if (backupTimer<=0) {
|
||||||
backupTask=std::async(std::launch::async,[this]() -> bool {
|
backupTask=std::async(std::launch::async,[this]() -> bool {
|
||||||
if (backupPath==curFileName) {
|
backupLock.lock();
|
||||||
|
logV("backupPath: %s",backupPath);
|
||||||
|
logV("curFileName: %s",curFileName);
|
||||||
|
if (curFileName.find(backupPath)==0) {
|
||||||
logD("backup file open. not saving backup.");
|
logD("backup file open. not saving backup.");
|
||||||
|
backupTimer=30.0;
|
||||||
|
backupLock.unlock();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!dirExists(backupPath.c_str())) {
|
||||||
|
if (!makeDir(backupPath.c_str())) {
|
||||||
|
logW("could not create backup directory!");
|
||||||
|
backupTimer=30.0;
|
||||||
|
backupLock.unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
logD("saving backup...");
|
logD("saving backup...");
|
||||||
SafeWriter* w=e->saveFur(true);
|
SafeWriter* w=e->saveFur(true);
|
||||||
logV("writing file...");
|
logV("writing file...");
|
||||||
|
|
||||||
if (w!=NULL) {
|
if (w!=NULL) {
|
||||||
FILE* outFile=ps_fopen(backupPath.c_str(),"wb");
|
size_t sepPos=curFileName.rfind(DIR_SEPARATOR);
|
||||||
|
String backupPreBaseName;
|
||||||
|
String backupBaseName;
|
||||||
|
String backupFileName;
|
||||||
|
if (sepPos==String::npos) {
|
||||||
|
backupPreBaseName=curFileName;
|
||||||
|
} else {
|
||||||
|
backupPreBaseName=curFileName.substr(sepPos+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t dotPos=backupPreBaseName.rfind('.');
|
||||||
|
if (dotPos!=String::npos) {
|
||||||
|
backupPreBaseName=backupPreBaseName.substr(0,dotPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (char i: backupPreBaseName) {
|
||||||
|
if (backupBaseName.size()>=48) break;
|
||||||
|
if ((i>='0' && i<='9') || (i>='A' && i<='Z') || (i>='a' && i<='z') || i=='_' || i=='-' || i==' ') backupBaseName+=i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backupBaseName.empty()) backupBaseName="untitled";
|
||||||
|
|
||||||
|
backupFileName=backupBaseName;
|
||||||
|
|
||||||
|
time_t curTime=time(NULL);
|
||||||
|
struct tm curTM;
|
||||||
|
#ifdef _WIN32
|
||||||
|
struct tm* tempTM=localtime(&curTime);
|
||||||
|
if (tempTM==NULL) {
|
||||||
|
backupFileName+="-unknownTime.fur";
|
||||||
|
} else {
|
||||||
|
curTM=*tempTM;
|
||||||
|
backupFileName+=fmt::sprintf("-%d%.2d%.2d-%.2d%.2d%.2d.fur",curTM.tm_year+1900,curTM.tm_mon+1,curTM.tm_mday,curTM.tm_hour,curTM.tm_min,curTM.tm_sec);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (localtime_r(&curTime,&curTM)==NULL) {
|
||||||
|
backupFileName+="-unknownTime.fur";
|
||||||
|
} else {
|
||||||
|
backupFileName+=fmt::sprintf("-%d%.2d%.2d-%.2d%.2d%.2d.fur",curTM.tm_year+1900,curTM.tm_mon+1,curTM.tm_mday,curTM.tm_hour,curTM.tm_min,curTM.tm_sec);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
String finalPath=backupPath+String(DIR_SEPARATOR_STR)+backupFileName;
|
||||||
|
|
||||||
|
FILE* outFile=ps_fopen(finalPath.c_str(),"wb");
|
||||||
if (outFile!=NULL) {
|
if (outFile!=NULL) {
|
||||||
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
|
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
|
||||||
logW("did not write backup entirely: %s!",strerror(errno));
|
logW("did not write backup entirely: %s!",strerror(errno));
|
||||||
|
@ -5421,17 +5656,69 @@ bool FurnaceGUI::loop() {
|
||||||
logW("could not save backup: %s!",strerror(errno));
|
logW("could not save backup: %s!",strerror(errno));
|
||||||
w->finish();
|
w->finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete previous backup if there are too many
|
||||||
|
delFirstBackup(backupBaseName);
|
||||||
}
|
}
|
||||||
logD("backup saved.");
|
logD("backup saved.");
|
||||||
backupTimer=30.0;
|
backupTimer=30.0;
|
||||||
|
backupLock.unlock();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sampleMapWaitingInput=(curWindow==GUI_WINDOW_INS_EDIT && sampleMapFocused);
|
||||||
|
|
||||||
curWindowThreadSafe=curWindow;
|
curWindowThreadSafe=curWindow;
|
||||||
|
|
||||||
|
if (curWindow!=curWindowLast) {
|
||||||
|
int curWindowCat=0;
|
||||||
|
int lastWindowCat=0;
|
||||||
|
|
||||||
|
switch (curWindow) {
|
||||||
|
case GUI_WINDOW_WAVE_LIST:
|
||||||
|
case GUI_WINDOW_WAVE_EDIT:
|
||||||
|
curWindowCat=1;
|
||||||
|
break;
|
||||||
|
case GUI_WINDOW_SAMPLE_LIST:
|
||||||
|
case GUI_WINDOW_SAMPLE_EDIT:
|
||||||
|
curWindowCat=2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
curWindowCat=0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (curWindowLast) {
|
||||||
|
case GUI_WINDOW_WAVE_LIST:
|
||||||
|
case GUI_WINDOW_WAVE_EDIT:
|
||||||
|
lastWindowCat=1;
|
||||||
|
break;
|
||||||
|
case GUI_WINDOW_SAMPLE_LIST:
|
||||||
|
case GUI_WINDOW_SAMPLE_EDIT:
|
||||||
|
lastWindowCat=2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
lastWindowCat=0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curWindowCat!=lastWindowCat) {
|
||||||
|
switch (lastWindowCat) {
|
||||||
|
case 0:
|
||||||
|
e->autoNoteOffAll();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
e->stopWavePreview();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
e->stopSamplePreview();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SDL_SetRenderDrawColor(sdlRend,uiColors[GUI_COLOR_BACKGROUND].x*255,
|
SDL_SetRenderDrawColor(sdlRend,uiColors[GUI_COLOR_BACKGROUND].x*255,
|
||||||
uiColors[GUI_COLOR_BACKGROUND].y*255,
|
uiColors[GUI_COLOR_BACKGROUND].y*255,
|
||||||
uiColors[GUI_COLOR_BACKGROUND].z*255,
|
uiColors[GUI_COLOR_BACKGROUND].z*255,
|
||||||
|
@ -5837,7 +6124,11 @@ bool FurnaceGUI::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(finalLayoutPath,(e->getConfigPath()+String(LAYOUT_INI)).c_str(),4095);
|
strncpy(finalLayoutPath,(e->getConfigPath()+String(LAYOUT_INI)).c_str(),4095);
|
||||||
backupPath=e->getConfigPath()+String(BACKUP_FUR);
|
backupPath=e->getConfigPath();
|
||||||
|
if (backupPath.size()>0) {
|
||||||
|
if (backupPath[backupPath.size()-1]==DIR_SEPARATOR) backupPath.resize(backupPath.size()-1);
|
||||||
|
}
|
||||||
|
backupPath+=String(BACKUPS_DIR);
|
||||||
prepareLayout();
|
prepareLayout();
|
||||||
|
|
||||||
ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_DockingEnable;
|
ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_DockingEnable;
|
||||||
|
@ -5907,7 +6198,9 @@ bool FurnaceGUI::init() {
|
||||||
|
|
||||||
void FurnaceGUI::commitState() {
|
void FurnaceGUI::commitState() {
|
||||||
if (!mobileUI) {
|
if (!mobileUI) {
|
||||||
ImGui::SaveIniSettingsToDisk(finalLayoutPath);
|
if (!ImGui::SaveIniSettingsToDisk(finalLayoutPath,true)) {
|
||||||
|
reportError(fmt::sprintf("could NOT save layout! %s",strerror(errno)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e->setConf("configVersion",(int)DIV_ENGINE_VERSION);
|
e->setConf("configVersion",(int)DIV_ENGINE_VERSION);
|
||||||
|
@ -6288,8 +6581,12 @@ FurnaceGUI::FurnaceGUI():
|
||||||
samplePreviewOn(false),
|
samplePreviewOn(false),
|
||||||
samplePreviewKey((SDL_Scancode)0),
|
samplePreviewKey((SDL_Scancode)0),
|
||||||
samplePreviewNote(0),
|
samplePreviewNote(0),
|
||||||
arpMacroScroll(-12),
|
sampleMapSelStart(-1),
|
||||||
pitchMacroScroll(-80),
|
sampleMapSelEnd(-1),
|
||||||
|
sampleMapDigit(0),
|
||||||
|
sampleMapColumn(0),
|
||||||
|
sampleMapFocused(false),
|
||||||
|
sampleMapWaitingInput(false),
|
||||||
macroDragStart(0,0),
|
macroDragStart(0,0),
|
||||||
macroDragAreaSize(0,0),
|
macroDragAreaSize(0,0),
|
||||||
macroDragCTarget(NULL),
|
macroDragCTarget(NULL),
|
||||||
|
@ -6350,6 +6647,7 @@ FurnaceGUI::FurnaceGUI():
|
||||||
eventTimeBegin(0),
|
eventTimeBegin(0),
|
||||||
eventTimeEnd(0),
|
eventTimeEnd(0),
|
||||||
eventTimeDelta(0),
|
eventTimeDelta(0),
|
||||||
|
perfMetricsLen(0),
|
||||||
chanToMove(-1),
|
chanToMove(-1),
|
||||||
sysToMove(-1),
|
sysToMove(-1),
|
||||||
sysToDelete(-1),
|
sysToDelete(-1),
|
||||||
|
|
|
@ -346,6 +346,7 @@ enum FurnaceGUIMobileScenes {
|
||||||
|
|
||||||
enum FurnaceGUIFileDialogs {
|
enum FurnaceGUIFileDialogs {
|
||||||
GUI_FILE_OPEN,
|
GUI_FILE_OPEN,
|
||||||
|
GUI_FILE_OPEN_BACKUP,
|
||||||
GUI_FILE_SAVE,
|
GUI_FILE_SAVE,
|
||||||
GUI_FILE_SAVE_DMF,
|
GUI_FILE_SAVE_DMF,
|
||||||
GUI_FILE_SAVE_DMF_LEGACY,
|
GUI_FILE_SAVE_DMF_LEGACY,
|
||||||
|
@ -416,6 +417,7 @@ enum FurnaceGUIFMAlgs {
|
||||||
|
|
||||||
enum FurnaceGUIActions {
|
enum FurnaceGUIActions {
|
||||||
GUI_ACTION_GLOBAL_MIN=0,
|
GUI_ACTION_GLOBAL_MIN=0,
|
||||||
|
GUI_ACTION_NEW,
|
||||||
GUI_ACTION_OPEN,
|
GUI_ACTION_OPEN,
|
||||||
GUI_ACTION_OPEN_BACKUP,
|
GUI_ACTION_OPEN_BACKUP,
|
||||||
GUI_ACTION_SAVE,
|
GUI_ACTION_SAVE,
|
||||||
|
@ -1151,6 +1153,17 @@ struct FurnaceGUIImage {
|
||||||
ch(0) {}
|
ch(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FurnaceGUIPerfMetric {
|
||||||
|
const char* name;
|
||||||
|
int elapsed;
|
||||||
|
FurnaceGUIPerfMetric(const char* n, int t):
|
||||||
|
name(n),
|
||||||
|
elapsed(t) {}
|
||||||
|
FurnaceGUIPerfMetric():
|
||||||
|
name(NULL),
|
||||||
|
elapsed(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
class FurnaceGUI {
|
class FurnaceGUI {
|
||||||
DivEngine* e;
|
DivEngine* e;
|
||||||
|
|
||||||
|
@ -1563,6 +1576,7 @@ class FurnaceGUI {
|
||||||
|
|
||||||
double exportFadeOut;
|
double exportFadeOut;
|
||||||
|
|
||||||
|
bool newSongFirstFrame;
|
||||||
bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen;
|
bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen;
|
||||||
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
|
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
|
||||||
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
|
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
|
||||||
|
@ -1677,8 +1691,11 @@ class FurnaceGUI {
|
||||||
std::vector<TouchPoint> pressedPoints;
|
std::vector<TouchPoint> pressedPoints;
|
||||||
std::vector<TouchPoint> releasedPoints;
|
std::vector<TouchPoint> releasedPoints;
|
||||||
|
|
||||||
int arpMacroScroll;
|
int sampleMapSelStart;
|
||||||
int pitchMacroScroll;
|
int sampleMapSelEnd;
|
||||||
|
int sampleMapDigit;
|
||||||
|
int sampleMapColumn;
|
||||||
|
bool sampleMapFocused, sampleMapWaitingInput;
|
||||||
|
|
||||||
ImVec2 macroDragStart;
|
ImVec2 macroDragStart;
|
||||||
ImVec2 macroDragAreaSize;
|
ImVec2 macroDragAreaSize;
|
||||||
|
@ -1732,6 +1749,12 @@ class FurnaceGUI {
|
||||||
int renderTimeBegin, renderTimeEnd, renderTimeDelta;
|
int renderTimeBegin, renderTimeEnd, renderTimeDelta;
|
||||||
int eventTimeBegin, eventTimeEnd, eventTimeDelta;
|
int eventTimeBegin, eventTimeEnd, eventTimeDelta;
|
||||||
|
|
||||||
|
FurnaceGUIPerfMetric perfMetrics[64];
|
||||||
|
int perfMetricsLen;
|
||||||
|
|
||||||
|
FurnaceGUIPerfMetric perfMetricsLast[64];
|
||||||
|
int perfMetricsLastLen;
|
||||||
|
|
||||||
std::map<FurnaceGUIImages,FurnaceGUIImage*> images;
|
std::map<FurnaceGUIImages,FurnaceGUIImage*> images;
|
||||||
|
|
||||||
int chanToMove, sysToMove, sysToDelete, opToMove;
|
int chanToMove, sysToMove, sysToDelete, opToMove;
|
||||||
|
@ -1933,6 +1956,7 @@ class FurnaceGUI {
|
||||||
|
|
||||||
void drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float availableWidth, int index);
|
void drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float availableWidth, int index);
|
||||||
void drawMacros(std::vector<FurnaceGUIMacroDesc>& macros, FurnaceGUIMacroEditState& state);
|
void drawMacros(std::vector<FurnaceGUIMacroDesc>& macros, FurnaceGUIMacroEditState& state);
|
||||||
|
void alterSampleMap(bool isNote, int val);
|
||||||
|
|
||||||
void drawOrderButtons();
|
void drawOrderButtons();
|
||||||
|
|
||||||
|
@ -2078,6 +2102,7 @@ class FurnaceGUI {
|
||||||
int loadStream(String path);
|
int loadStream(String path);
|
||||||
void pushRecentFile(String path);
|
void pushRecentFile(String path);
|
||||||
void exportAudio(String path, DivAudioExportModes mode);
|
void exportAudio(String path, DivAudioExportModes mode);
|
||||||
|
void delFirstBackup(String name);
|
||||||
|
|
||||||
bool parseSysEx(unsigned char* data, size_t len);
|
bool parseSysEx(unsigned char* data, size_t len);
|
||||||
|
|
||||||
|
|
|
@ -462,6 +462,7 @@ const FurnaceGUIColors fxColors[256]={
|
||||||
// format: ("ACTION_ENUM", "Action name", defaultBind)
|
// format: ("ACTION_ENUM", "Action name", defaultBind)
|
||||||
const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
||||||
D("GLOBAL_MIN", "---Global", NOT_AN_ACTION),
|
D("GLOBAL_MIN", "---Global", NOT_AN_ACTION),
|
||||||
|
D("NEW", "New", FURKMOD_CMD|SDLK_n),
|
||||||
D("OPEN", "Open file", FURKMOD_CMD|SDLK_o),
|
D("OPEN", "Open file", FURKMOD_CMD|SDLK_o),
|
||||||
D("OPEN_BACKUP", "Restore backup", 0),
|
D("OPEN_BACKUP", "Restore backup", 0),
|
||||||
D("SAVE", "Save file", FURKMOD_CMD|SDLK_s),
|
D("SAVE", "Save file", FURKMOD_CMD|SDLK_s),
|
||||||
|
|
|
@ -2023,6 +2023,59 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros, FurnaceGUI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FurnaceGUI::alterSampleMap(bool isNote, int val) {
|
||||||
|
if (curIns<0 || curIns>=(int)e->song.ins.size()) return;
|
||||||
|
DivInstrument* ins=e->song.ins[curIns];
|
||||||
|
int sampleMapMin=sampleMapSelStart;
|
||||||
|
int sampleMapMax=sampleMapSelEnd;
|
||||||
|
if (sampleMapMin>sampleMapMax) {
|
||||||
|
sampleMapMin^=sampleMapMax;
|
||||||
|
sampleMapMax^=sampleMapMin;
|
||||||
|
sampleMapMin^=sampleMapMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=sampleMapMin; i<=sampleMapMax; i++) {
|
||||||
|
if (i<0 || i>=120) continue;
|
||||||
|
|
||||||
|
if (sampleMapColumn==1 && isNote) {
|
||||||
|
ins->amiga.noteMap[i].freq=val;
|
||||||
|
} else if (sampleMapColumn==0 && !isNote) {
|
||||||
|
if (val<0) {
|
||||||
|
ins->amiga.noteMap[i].map=-1;
|
||||||
|
} else if (sampleMapDigit>0) {
|
||||||
|
ins->amiga.noteMap[i].map*=10;
|
||||||
|
ins->amiga.noteMap[i].map+=val;
|
||||||
|
} else {
|
||||||
|
ins->amiga.noteMap[i].map=val;
|
||||||
|
}
|
||||||
|
if (ins->amiga.noteMap[i].map>=(int)e->song.sample.size()) {
|
||||||
|
ins->amiga.noteMap[i].map=((int)e->song.sample.size())-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool advance=false;
|
||||||
|
if (sampleMapColumn==1 && isNote) {
|
||||||
|
advance=true;
|
||||||
|
} else if (sampleMapColumn==0 && !isNote) {
|
||||||
|
int digits=1;
|
||||||
|
if (e->song.sample.size()>=10) digits=2;
|
||||||
|
if (e->song.sample.size()>=100) digits=3;
|
||||||
|
if (++sampleMapDigit>=digits) {
|
||||||
|
sampleMapDigit=0;
|
||||||
|
advance=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advance && sampleMapMin==sampleMapMax) {
|
||||||
|
sampleMapSelStart++;
|
||||||
|
if (sampleMapSelStart>119) sampleMapSelStart=119;
|
||||||
|
sampleMapSelEnd=sampleMapSelStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
MARK_MODIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
#define DRUM_FREQ(name,db,df,prop) \
|
#define DRUM_FREQ(name,db,df,prop) \
|
||||||
ImGui::TableNextRow(); \
|
ImGui::TableNextRow(); \
|
||||||
ImGui::TableNextColumn(); \
|
ImGui::TableNextColumn(); \
|
||||||
|
@ -4341,6 +4394,7 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
ins->type==DIV_INS_GA20) {
|
ins->type==DIV_INS_GA20) {
|
||||||
if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) {
|
if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) {
|
||||||
String sName;
|
String sName;
|
||||||
|
bool wannaOpenSMPopup=false;
|
||||||
if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) {
|
if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) {
|
||||||
sName="none selected";
|
sName="none selected";
|
||||||
} else {
|
} else {
|
||||||
|
@ -4405,61 +4459,191 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
ImGui::BeginDisabled(ins->amiga.useWave);
|
ImGui::BeginDisabled(ins->amiga.useWave);
|
||||||
P(ImGui::Checkbox("Use sample map",&ins->amiga.useNoteMap));
|
P(ImGui::Checkbox("Use sample map",&ins->amiga.useNoteMap));
|
||||||
if (ins->amiga.useNoteMap) {
|
if (ins->amiga.useNoteMap) {
|
||||||
// TODO: frequency map?
|
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) sampleMapFocused=false;
|
||||||
if (ImGui::BeginTable("NoteMap",2/*3*/,ImGuiTableFlags_ScrollY|ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) {
|
if (curWindowLast!=GUI_WINDOW_INS_EDIT) sampleMapFocused=false;
|
||||||
|
if (!sampleMapFocused) sampleMapDigit=0;
|
||||||
|
if (ImGui::BeginTable("NoteMap",4,ImGuiTableFlags_ScrollY|ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) {
|
||||||
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
|
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
|
||||||
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch);
|
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed);
|
||||||
//ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
|
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed);
|
||||||
|
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch);
|
||||||
|
|
||||||
ImGui::TableSetupScrollFreeze(0,1);
|
ImGui::TableSetupScrollFreeze(0,1);
|
||||||
|
|
||||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Sample");
|
ImGui::Text("#");
|
||||||
/*ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Frequency");*/
|
ImGui::Text("note");
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::Text("sample name");
|
||||||
|
int sampleMapMin=sampleMapSelStart;
|
||||||
|
int sampleMapMax=sampleMapSelEnd;
|
||||||
|
if (sampleMapMin>sampleMapMax) {
|
||||||
|
sampleMapMin^=sampleMapMax;
|
||||||
|
sampleMapMax^=sampleMapMin;
|
||||||
|
sampleMapMin^=sampleMapMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(ImGuiCol_HeaderHovered));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_HeaderActive,ImGui::GetColorU32(ImGuiCol_HeaderHovered));
|
||||||
for (int i=0; i<120; i++) {
|
for (int i=0; i<120; i++) {
|
||||||
DivInstrumentAmiga::SampleMap& sampleMap=ins->amiga.noteMap[i];
|
DivInstrumentAmiga::SampleMap& sampleMap=ins->amiga.noteMap[i];
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::PushID(fmt::sprintf("NM_%d",i).c_str());
|
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,ImGui::GetColorU32(ImGuiCol_TableHeaderBg));
|
||||||
ImGui::Text("%s",noteNames[60+i]);
|
ImGui::Text("%s",noteNames[60+i]);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
if (sampleMap.map<0 || sampleMap.map>=e->song.sampleLen) {
|
if (sampleMap.map<0 || sampleMap.map>=e->song.sampleLen) {
|
||||||
sName="-- empty --";
|
sName=fmt::sprintf("---##SM%d",i);
|
||||||
sampleMap.map=-1;
|
sampleMap.map=-1;
|
||||||
} else {
|
} else {
|
||||||
sName=e->song.sample[sampleMap.map]->name;
|
sName=fmt::sprintf("%3d##SM%d",sampleMap.map,i);
|
||||||
}
|
}
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
ImGui::PushFont(patFont);
|
||||||
if (ImGui::BeginCombo("##SM",sName.c_str())) {
|
ImGui::SetNextItemWidth(ImGui::CalcTextSize("00000").x);
|
||||||
String id;
|
ImGui::Selectable(sName.c_str(),(sampleMapWaitingInput && sampleMapColumn==0 && i>=sampleMapMin && i<=sampleMapMax));
|
||||||
if (ImGui::Selectable("-- empty --",sampleMap.map==-1)) { PARAMETER
|
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||||
sampleMap.map=-1;
|
sampleMapFocused=true;
|
||||||
|
sampleMapColumn=0;
|
||||||
|
sampleMapDigit=0;
|
||||||
|
sampleMapSelStart=i;
|
||||||
|
sampleMapSelEnd=i;
|
||||||
|
|
||||||
|
sampleMapMin=sampleMapSelStart;
|
||||||
|
sampleMapMax=sampleMapSelEnd;
|
||||||
|
if (sampleMapMin>sampleMapMax) {
|
||||||
|
sampleMapMin^=sampleMapMax;
|
||||||
|
sampleMapMax^=sampleMapMin;
|
||||||
|
sampleMapMin^=sampleMapMax;
|
||||||
}
|
}
|
||||||
for (int j=0; j<e->song.sampleLen; j++) {
|
ImGui::InhibitInertialScroll();
|
||||||
id=fmt::sprintf("%d: %s",j,e->song.sample[j]->name);
|
}
|
||||||
if (ImGui::Selectable(id.c_str(),sampleMap.map==j)) { PARAMETER
|
if (sampleMapFocused && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) && ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
|
||||||
sampleMap.map=j;
|
sampleMapSelEnd=i;
|
||||||
if (sampleMap.freq<=0) sampleMap.freq=(int)((double)e->song.sample[j]->centerRate*pow(2.0,((double)i-48.0)/12.0));
|
}
|
||||||
|
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
||||||
|
if (sampleMapSelStart==sampleMapSelEnd) {
|
||||||
|
sampleMapFocused=true;
|
||||||
|
sampleMapColumn=0;
|
||||||
|
sampleMapDigit=0;
|
||||||
|
sampleMapSelStart=i;
|
||||||
|
sampleMapSelEnd=i;
|
||||||
|
|
||||||
|
sampleMapMin=sampleMapSelStart;
|
||||||
|
sampleMapMax=sampleMapSelEnd;
|
||||||
|
if (sampleMapMin>sampleMapMax) {
|
||||||
|
sampleMapMin^=sampleMapMax;
|
||||||
|
sampleMapMax^=sampleMapMin;
|
||||||
|
sampleMapMin^=sampleMapMax;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndCombo();
|
if (sampleMapFocused) {
|
||||||
|
wannaOpenSMPopup=true;
|
||||||
}
|
}
|
||||||
/*ImGui::TableNextColumn();
|
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
|
||||||
if (ImGui::InputInt("##SF",&sampleMap.freq,50,500)) { PARAMETER
|
|
||||||
if (sampleMap.freq<0) sampleMap.freq=0;
|
|
||||||
if (sampleMap.freq>262144) sampleMap.freq=262144;
|
|
||||||
}*/
|
|
||||||
ImGui::PopID();
|
|
||||||
}
|
}
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
sName="???";
|
||||||
|
if ((sampleMap.freq+60)>0 && (sampleMap.freq+60)<180) {
|
||||||
|
sName=noteNames[sampleMap.freq+60];
|
||||||
|
}
|
||||||
|
sName+=fmt::sprintf("##SN%d",i);
|
||||||
|
ImGui::PushFont(patFont);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::CalcTextSize("00000").x);
|
||||||
|
ImGui::Selectable(sName.c_str(),(sampleMapWaitingInput && sampleMapColumn==1 && i>=sampleMapMin && i<=sampleMapMax));
|
||||||
|
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||||
|
sampleMapFocused=true;
|
||||||
|
sampleMapColumn=1;
|
||||||
|
sampleMapDigit=0;
|
||||||
|
sampleMapSelStart=i;
|
||||||
|
sampleMapSelEnd=i;
|
||||||
|
|
||||||
|
sampleMapMin=sampleMapSelStart;
|
||||||
|
sampleMapMax=sampleMapSelEnd;
|
||||||
|
if (sampleMapMin>sampleMapMax) {
|
||||||
|
sampleMapMin^=sampleMapMax;
|
||||||
|
sampleMapMax^=sampleMapMin;
|
||||||
|
sampleMapMin^=sampleMapMax;
|
||||||
|
}
|
||||||
|
ImGui::InhibitInertialScroll();
|
||||||
|
}
|
||||||
|
if (sampleMapFocused && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) && ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
|
||||||
|
sampleMapSelEnd=i;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
||||||
|
if (sampleMapSelStart==sampleMapSelEnd) {
|
||||||
|
sampleMapFocused=true;
|
||||||
|
sampleMapColumn=1;
|
||||||
|
sampleMapDigit=0;
|
||||||
|
sampleMapSelStart=i;
|
||||||
|
sampleMapSelEnd=i;
|
||||||
|
|
||||||
|
sampleMapMin=sampleMapSelStart;
|
||||||
|
sampleMapMax=sampleMapSelEnd;
|
||||||
|
if (sampleMapMin>sampleMapMax) {
|
||||||
|
sampleMapMin^=sampleMapMax;
|
||||||
|
sampleMapMax^=sampleMapMin;
|
||||||
|
sampleMapMin^=sampleMapMax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sampleMapFocused) {
|
||||||
|
wannaOpenSMPopup=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
if (sampleMap.map>=0 && sampleMap.map<e->song.sampleLen) {
|
||||||
|
ImGui::TextUnformatted(e->song.sample[sampleMap.map]->name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
sampleMapFocused=false;
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
if (wannaOpenSMPopup) {
|
||||||
|
ImGui::OpenPopup("SampleMapUtils");
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopup("SampleMapUtils",ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
|
||||||
|
if (sampleMapSelStart==sampleMapSelEnd && sampleMapSelStart>=0 && sampleMapSelStart<120) {
|
||||||
|
if (ImGui::MenuItem("set entire map to this note")) {
|
||||||
|
if (sampleMapSelStart>=0 && sampleMapSelStart<120) {
|
||||||
|
for (int i=0; i<120; i++) {
|
||||||
|
if (i==sampleMapSelStart) continue;
|
||||||
|
ins->amiga.noteMap[i].freq=ins->amiga.noteMap[sampleMapSelStart].freq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::MenuItem("set entire map to this sample")) {
|
||||||
|
if (sampleMapSelStart>=0 && sampleMapSelStart<120) {
|
||||||
|
for (int i=0; i<120; i++) {
|
||||||
|
if (i==sampleMapSelStart) continue;
|
||||||
|
ins->amiga.noteMap[i].map=ins->amiga.noteMap[sampleMapSelStart].map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::MenuItem("reset notes")) {
|
||||||
|
for (int i=0; i<120; i++) {
|
||||||
|
ins->amiga.noteMap[i].freq=i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::MenuItem("clear map samples")) {
|
||||||
|
for (int i=0; i<120; i++) {
|
||||||
|
ins->amiga.noteMap[i].map=-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
|
} else {
|
||||||
|
sampleMapFocused=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ins->type==DIV_INS_N163) if (ImGui::BeginTabItem(settings.c163Name.c_str())) {
|
if (ins->type==DIV_INS_N163) if (ImGui::BeginTabItem(settings.c163Name.c_str())) {
|
||||||
|
|
|
@ -34,6 +34,8 @@ void FurnaceGUI::drawNewSong() {
|
||||||
avail.y-=ImGui::GetFrameHeightWithSpacing();
|
avail.y-=ImGui::GetFrameHeightWithSpacing();
|
||||||
|
|
||||||
if (ImGui::BeginChild("sysPickerC",avail,false,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
|
if (ImGui::BeginChild("sysPickerC",avail,false,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
|
||||||
|
if (newSongFirstFrame)
|
||||||
|
ImGui::SetKeyboardFocusHere();
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
if (ImGui::InputTextWithHint("##SysSearch","Search...",&newSongQuery)) {
|
if (ImGui::InputTextWithHint("##SysSearch","Search...",&newSongQuery)) {
|
||||||
String lowerCase=newSongQuery;
|
String lowerCase=newSongQuery;
|
||||||
|
@ -159,4 +161,6 @@ void FurnaceGUI::drawNewSong() {
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newSongFirstFrame=false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,8 +221,9 @@ void FurnaceGUI::drawOsc() {
|
||||||
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
|
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
|
||||||
}
|
}
|
||||||
if (settings.oscEscapesBoundary) {
|
if (settings.oscEscapesBoundary) {
|
||||||
ImDrawList* dlf=ImGui::GetForegroundDrawList();
|
dl->PushClipRectFullScreen();
|
||||||
dlf->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
|
dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
|
||||||
|
dl->PopClipRect();
|
||||||
} else {
|
} else {
|
||||||
dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
|
dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
|
||||||
}
|
}
|
||||||
|
|
|
@ -415,10 +415,14 @@ void FurnaceGUI::drawPiano() {
|
||||||
e->previewSample(curSample,note);
|
e->previewSample(curSample,note);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (sampleMapWaitingInput) {
|
||||||
|
alterSampleMap(true,note);
|
||||||
|
} else {
|
||||||
e->synchronized([this,note]() {
|
e->synchronized([this,note]() {
|
||||||
e->autoNoteOn(-1,curIns,note);
|
e->autoNoteOn(-1,curIns,note);
|
||||||
});
|
});
|
||||||
if (edit && curWindow!=GUI_WINDOW_INS_LIST && curWindow!=GUI_WINDOW_INS_EDIT) noteInput(note,0);
|
if (edit && curWindow!=GUI_WINDOW_INS_LIST && curWindow!=GUI_WINDOW_INS_EDIT) noteInput(note,0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1109,11 +1109,23 @@ void FurnaceGUI::initSystemPresets() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
ENTRY(
|
ENTRY(
|
||||||
"Commander X16", {
|
"Commander X16 (VERA only)", {
|
||||||
|
CH(DIV_SYSTEM_VERA, 1.0f, 0, "")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ENTRY(
|
||||||
|
"Commander X16 (with OPM)", {
|
||||||
CH(DIV_SYSTEM_VERA, 1.0f, 0, ""),
|
CH(DIV_SYSTEM_VERA, 1.0f, 0, ""),
|
||||||
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "")
|
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "")
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
ENTRY(
|
||||||
|
"Commander X16 (with Twin OPL3)", {
|
||||||
|
CH(DIV_SYSTEM_VERA, 1.0f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_OPL3, 1.0f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_OPL3, 1.0f, 0, "")
|
||||||
|
}
|
||||||
|
);
|
||||||
ENTRY(
|
ENTRY(
|
||||||
"TI-99/4A", {
|
"TI-99/4A", {
|
||||||
CH(DIV_SYSTEM_SMS, 1.0f, 0,
|
CH(DIV_SYSTEM_SMS, 1.0f, 0,
|
||||||
|
|
|
@ -276,6 +276,8 @@ void FurnaceGUI::drawSettings() {
|
||||||
ImVec2 setWindowSize=ImVec2(canvasW,canvasH);
|
ImVec2 setWindowSize=ImVec2(canvasW,canvasH);
|
||||||
ImGui::SetNextWindowPos(setWindowPos);
|
ImGui::SetNextWindowPos(setWindowPos);
|
||||||
ImGui::SetNextWindowSize(setWindowSize);
|
ImGui::SetNextWindowSize(setWindowSize);
|
||||||
|
} else {
|
||||||
|
ImGui::SetNextWindowSizeConstraints(ImVec2(200.0f*dpiScale,100.0f*dpiScale),ImVec2(canvasW,canvasH));
|
||||||
}
|
}
|
||||||
if (ImGui::Begin("Settings",&settingsOpen,ImGuiWindowFlags_NoDocking|globalWinFlags)) {
|
if (ImGui::Begin("Settings",&settingsOpen,ImGuiWindowFlags_NoDocking|globalWinFlags)) {
|
||||||
if (!settingsOpen) {
|
if (!settingsOpen) {
|
||||||
|
@ -2092,6 +2094,7 @@ void FurnaceGUI::drawSettings() {
|
||||||
if (ImGui::TreeNode("Global hotkeys")) {
|
if (ImGui::TreeNode("Global hotkeys")) {
|
||||||
KEYBIND_CONFIG_BEGIN("keysGlobal");
|
KEYBIND_CONFIG_BEGIN("keysGlobal");
|
||||||
|
|
||||||
|
UI_KEYBIND_CONFIG(GUI_ACTION_NEW);
|
||||||
UI_KEYBIND_CONFIG(GUI_ACTION_OPEN);
|
UI_KEYBIND_CONFIG(GUI_ACTION_OPEN);
|
||||||
UI_KEYBIND_CONFIG(GUI_ACTION_OPEN_BACKUP);
|
UI_KEYBIND_CONFIG(GUI_ACTION_OPEN_BACKUP);
|
||||||
UI_KEYBIND_CONFIG(GUI_ACTION_SAVE);
|
UI_KEYBIND_CONFIG(GUI_ACTION_SAVE);
|
||||||
|
|
|
@ -1776,6 +1776,21 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case DIV_SYSTEM_SEGAPCM:
|
||||||
|
case DIV_SYSTEM_SEGAPCM_COMPAT: {
|
||||||
|
bool oldSlides=flags.getBool("oldSlides",false);
|
||||||
|
|
||||||
|
if (ImGui::Checkbox("Legacy slides and pitch (compatibility)",&oldSlides)) {
|
||||||
|
altered=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (altered) {
|
||||||
|
e->lockSave([&]() {
|
||||||
|
flags.set("oldSlides",oldSlides);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case DIV_SYSTEM_SM8521:/* {
|
case DIV_SYSTEM_SM8521:/* {
|
||||||
bool noAntiClick=flags.getBool("noAntiClick",false);
|
bool noAntiClick=flags.getBool("noAntiClick",false);
|
||||||
|
|
||||||
|
|
39
src/log.cpp
39
src/log.cpp
|
@ -18,13 +18,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ta-log.h"
|
#include "ta-log.h"
|
||||||
|
#include "fileutils.h"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef IS_MOBILE
|
#ifdef IS_MOBILE
|
||||||
int logLevel=LOGLEVEL_TRACE;
|
int logLevel=LOGLEVEL_TRACE;
|
||||||
#else
|
#else
|
||||||
int logLevel=LOGLEVEL_INFO;
|
int logLevel=LOGLEVEL_TRACE; // until done
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FILE* logFile;
|
FILE* logFile;
|
||||||
|
@ -133,7 +138,16 @@ int writeLog(int level, const char* msg, fmt::printf_args args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void initLog() {
|
void initLog() {
|
||||||
// initalize log buffer
|
// initialize coloring on Windows
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE winout=GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
int termprop=0;
|
||||||
|
GetConsoleMode(winout,(LPDWORD)&termprop);
|
||||||
|
termprop|=ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||||
|
SetConsoleMode(winout,termprop);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// initialize log buffer
|
||||||
logPosition=0;
|
logPosition=0;
|
||||||
for (int i=0; i<TA_LOG_SIZE; i++) {
|
for (int i=0; i<TA_LOG_SIZE; i++) {
|
||||||
logEntries[i].text.reserve(128);
|
logEntries[i].text.reserve(128);
|
||||||
|
@ -169,9 +183,28 @@ bool startLogFile(const char* path) {
|
||||||
if (logFileAvail) return true;
|
if (logFileAvail) return true;
|
||||||
|
|
||||||
// rotate log file if possible
|
// rotate log file if possible
|
||||||
|
char oldPath[4096];
|
||||||
|
char newPath[4096];
|
||||||
|
|
||||||
|
if (fileExists(path)==1) {
|
||||||
|
for (int i=4; i>=0; i--) {
|
||||||
|
if (i>0) {
|
||||||
|
snprintf(oldPath,4095,"%s.%d",path,i);
|
||||||
|
} else {
|
||||||
|
strncpy(oldPath,path,4095);
|
||||||
|
}
|
||||||
|
snprintf(newPath,4095,"%s.%d",path,i+1);
|
||||||
|
|
||||||
|
if (i>=4) {
|
||||||
|
deleteFile(oldPath);
|
||||||
|
} else {
|
||||||
|
moveFiles(oldPath,newPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// open log file
|
// open log file
|
||||||
if ((logFile=fopen(path,"w+"))==NULL) {
|
if ((logFile=ps_fopen(path,"w+"))==NULL) {
|
||||||
logFileAvail=false;
|
logFileAvail=false;
|
||||||
logW("could not open log file! (%s)",strerror(errno));
|
logW("could not open log file! (%s)",strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <combaseapi.h>
|
#include <combaseapi.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
|
|
@ -66,4 +66,6 @@ struct TAParam {
|
||||||
func(f) {}
|
func(f) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void reportError(String what);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue