Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt

* 'master' of https://github.com/tildearrow/furnace: (53 commits)
  prepare for better backward writing
  VGM export: fix oops
  GUI: drag-and-drop ins/wave/sample loading
  GUI: add "set loop" to sample editor
  MSM6295: VGM export!
  oops
  MSM6295: add rate select effect (20xx)
  update meteor shower
  MSVC is better than GCC right?
  update to-do list
  door into summer
  GUI: implement input for touch events
  GUI: update SDL hints
  fix Termux build
  add another demo song
  add demos/ecolove.fur
  update to-do list
  update demos/README.md
  add new demo songs
  dev99 - major Fractal system change
  ...

# Conflicts:
#	src/engine/dispatch.h
#	src/engine/platform/genesis.cpp
#	src/engine/playback.cpp
#	src/engine/song.h
#	src/engine/vgmOps.cpp
This commit is contained in:
cam900 2022-05-29 13:47:39 +09:00
commit 32152fd89b
72 changed files with 967 additions and 304 deletions

View file

@ -25,6 +25,10 @@ set(SYSTEM_SDL2_DEFAULT OFF)
if (ANDROID) if (ANDROID)
set(USE_RTMIDI_DEFAULT OFF) set(USE_RTMIDI_DEFAULT OFF)
set(USE_BACKWARD_DEFAULT OFF) set(USE_BACKWARD_DEFAULT OFF)
find_library(TERMUX rt)
if (TERMUX)
message(STATUS "Termux detected")
endif()
else() else()
set(USE_RTMIDI_DEFAULT ON) set(USE_RTMIDI_DEFAULT ON)
set(USE_BACKWARD_DEFAULT ON) set(USE_BACKWARD_DEFAULT ON)
@ -53,7 +57,7 @@ option(WARNINGS_ARE_ERRORS "Whether warnings in furnace's C++ code should be tre
set(DEPENDENCIES_INCLUDE_DIRS "") set(DEPENDENCIES_INCLUDE_DIRS "")
if (ANDROID) if (ANDROID AND NOT TERMUX)
set(DEPENDENCIES_DEFINES "IS_MOBILE") set(DEPENDENCIES_DEFINES "IS_MOBILE")
else() else()
set(DEPENDENCIES_DEFINES "") set(DEPENDENCIES_DEFINES "")
@ -188,7 +192,7 @@ if (USE_SDL2)
endif() endif()
message(STATUS "Using system-installed SDL2") message(STATUS "Using system-installed SDL2")
else() else()
if (ANDROID) if (ANDROID AND NOT TERMUX)
set(SDL_SHARED ON CACHE BOOL "Force no dynamically-linked SDL" FORCE) set(SDL_SHARED ON CACHE BOOL "Force no dynamically-linked SDL" FORCE)
set(SDL_STATIC OFF CACHE BOOL "Force statically-linked SDL" FORCE) set(SDL_STATIC OFF CACHE BOOL "Force statically-linked SDL" FORCE)
else() else()
@ -203,7 +207,7 @@ if (USE_SDL2)
add_subdirectory(extern/SDL EXCLUDE_FROM_ALL) add_subdirectory(extern/SDL EXCLUDE_FROM_ALL)
list(APPEND DEPENDENCIES_DEFINES HAVE_SDL2) list(APPEND DEPENDENCIES_DEFINES HAVE_SDL2)
list(APPEND DEPENDENCIES_INCLUDE_DIRS extern/SDL/include) list(APPEND DEPENDENCIES_INCLUDE_DIRS extern/SDL/include)
if (ANDROID) if (ANDROID AND NOT TERMUX)
list(APPEND DEPENDENCIES_LIBRARIES SDL2) list(APPEND DEPENDENCIES_LIBRARIES SDL2)
else() else()
list(APPEND DEPENDENCIES_LIBRARIES SDL2-static) list(APPEND DEPENDENCIES_LIBRARIES SDL2-static)
@ -579,7 +583,7 @@ endif()
if (MSVC) if (MSVC)
add_executable(furnace WIN32 ${USED_SOURCES}) add_executable(furnace WIN32 ${USED_SOURCES})
elseif(ANDROID) elseif(ANDROID AND NOT TERMUX)
add_library(furnace SHARED ${USED_SOURCES}) add_library(furnace SHARED ${USED_SOURCES})
else() else()
add_executable(furnace ${USED_SOURCES}) add_executable(furnace ${USED_SOURCES})
@ -602,7 +606,7 @@ if (PKG_CONFIG_FOUND AND (SYSTEM_FMT OR SYSTEM_LIBSNDFILE OR SYSTEM_ZLIB OR SYST
endif() endif()
endif() endif()
if (NOT ANDROID) if (NOT ANDROID OR TERMUX)
install(TARGETS furnace RUNTIME DESTINATION bin) install(TARGETS furnace RUNTIME DESTINATION bin)
if (NOT WIN32 AND NOT APPLE) if (NOT WIN32 AND NOT APPLE)

View file

@ -8,12 +8,10 @@
- additional YM2612 features - additional YM2612 features
- CSM - CSM
- DualPCM - MSM6258 pitch and clock select
- reverse sample playback - the last three compat flags
- ADPCM chips - add OPL drum instrument type
- Game Boy envelope macro/sequence - Game Boy envelope macro/sequence
- drag-and-drop ins/wave/sample loading
- "set loop" in right click menu of sample editor
- sample editor preview in selection - sample editor preview in selection
- rewrite the system name detection function anyway - rewrite the system name detection function anyway
- unified data view - unified data view

BIN
demos/AY-3-8910_Jam.fur Normal file

Binary file not shown.

BIN
demos/AgentX.fur Normal file

Binary file not shown.

BIN
demos/C64 junk.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/HoldOn.fur Normal file

Binary file not shown.

BIN
demos/Lagrange_Point.fur Normal file

Binary file not shown.

BIN
demos/LedStorm.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -2,8 +2,13 @@
demo songs for Furnace. demo songs for Furnace.
these demo songs are not under the GPL. in the case of covers, all rights are reserved to the original author. these demo songs are not under the GPL. all rights are reserved to the original author(s).
# submit demo songs! # submit demo songs!
just send a pull request if you want your song to be added to this collection. thank you! contact me or send a pull request if you want your song to be added to this collection. be noted we have two rules:
- Nintendo covers are frowned upon
- big label music covers also are discouraged
thank you for contributing!

BIN
demos/Red_Planet.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/Tyrian-Camanis.fur Normal file

Binary file not shown.

BIN
demos/UNATCOPCM.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/chippylotus.fur Normal file

Binary file not shown.

BIN
demos/doorintosummer.fur Normal file

Binary file not shown.

BIN
demos/ecolove.fur Normal file

Binary file not shown.

BIN
demos/fight and flight.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/insert_title_lynx.fur Normal file

Binary file not shown.

BIN
demos/meteor_shower.fur Normal file

Binary file not shown.

BIN
demos/oby1_ingame.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/puggs_in_space.fur Normal file

Binary file not shown.

BIN
demos/sijofsjfsoeife.fur Normal file

Binary file not shown.

BIN
demos/skate_or_die.fur Normal file

Binary file not shown.

BIN
demos/su_memory.fur Normal file

Binary file not shown.

Binary file not shown.

View file

@ -8,6 +8,10 @@ const float ympsg_vol[17] = {
1.0, 0.772, 0.622, 0.485, 0.382, 0.29, 0.229, 0.174, 0.132, 0.096, 0.072, 0.051, 0.034, 0.019, 0.009, 0.0, -1.059 1.0, 0.772, 0.622, 0.485, 0.382, 0.29, 0.229, 0.174, 0.132, 0.096, 0.072, 0.051, 0.034, 0.019, 0.009, 0.0, -1.059
}; };
const float tipsg_vol[17] = {
1.0, 0.794, 0.631, 0.501, 0.398, 0.316, 0.251, 0.2, 0.158, 0.126, 0.1, 0.079, 0.063, 0.05, 0.04, 0.0, -1.059
};
static void YMPSG_WriteLatch(ympsg_t *chip) static void YMPSG_WriteLatch(ympsg_t *chip)
{ {
uint8_t data = chip->data; uint8_t data = chip->data;
@ -260,6 +264,10 @@ void YMPSG_Init(ympsg_t *chip, uint8_t real_sn)
YMPSG_SetIC(chip, 1); YMPSG_SetIC(chip, 1);
chip->noise_tap2 = real_sn ? 13 : 15; chip->noise_tap2 = real_sn ? 13 : 15;
chip->noise_size = real_sn ? 16383 : 32767; chip->noise_size = real_sn ? 16383 : 32767;
for (i = 0; i < 17; i++)
{
chip->vol_table[i]=(real_sn?tipsg_vol[i]:ympsg_vol[i]) * 8192.0f;
}
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
{ {
YMPSG_Clock(chip); YMPSG_Clock(chip);
@ -307,29 +315,29 @@ void YMPSG_Clock(ympsg_t *chip)
} }
} }
float YMPSG_GetOutput(ympsg_t *chip) int YMPSG_GetOutput(ympsg_t *chip)
{ {
float sample = 0.f; int sample = 0;
uint32_t i; uint32_t i;
YMPSG_UpdateSample(chip); YMPSG_UpdateSample(chip);
if (chip->test & 1) if (chip->test & 1)
{ {
sample += ympsg_vol[chip->volume_out[chip->test >> 1]]; sample += chip->vol_table[chip->volume_out[chip->test >> 1]];
sample += ympsg_vol[16] * 3.f; sample += chip->vol_table[16] * 3;
} }
else if (!chip->mute) else if (!chip->mute)
{ {
sample += ympsg_vol[chip->volume_out[0]]; sample += chip->vol_table[chip->volume_out[0]];
sample += ympsg_vol[chip->volume_out[1]]; sample += chip->vol_table[chip->volume_out[1]];
sample += ympsg_vol[chip->volume_out[2]]; sample += chip->vol_table[chip->volume_out[2]];
sample += ympsg_vol[chip->volume_out[3]]; sample += chip->vol_table[chip->volume_out[3]];
} }
else else
{ {
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
{ {
if (!((chip->mute>>i) & 1)) if (!((chip->mute>>i) & 1))
sample += ympsg_vol[chip->volume_out[i]]; sample += chip->vol_table[chip->volume_out[i]];
} }
} }
return sample; return sample;

View file

@ -58,6 +58,10 @@ typedef struct {
uint64_t writebuf_lasttime; uint64_t writebuf_lasttime;
ympsg_writebuf writebuf[YMPSG_WRITEBUF_SIZE]; ympsg_writebuf writebuf[YMPSG_WRITEBUF_SIZE];
//
short vol_table[17];
uint8_t mute; uint8_t mute;
} ympsg_t; } ympsg_t;
@ -67,7 +71,7 @@ uint16_t YMPSG_Read(ympsg_t *chip);
void YMPSG_Init(ympsg_t *chip, uint8_t real_sn); void YMPSG_Init(ympsg_t *chip, uint8_t real_sn);
void YMPSG_SetIC(ympsg_t *chip, uint32_t ic); void YMPSG_SetIC(ympsg_t *chip, uint32_t ic);
void YMPSG_Clock(ympsg_t *chip); void YMPSG_Clock(ympsg_t *chip);
float YMPSG_GetOutput(ympsg_t *chip); int YMPSG_GetOutput(ympsg_t *chip);
void YMPSG_Test(ympsg_t *chip, uint16_t test); void YMPSG_Test(ympsg_t *chip, uint16_t test);

View file

@ -4252,7 +4252,20 @@ public:
st.load_here(32, reinterpret_cast<void *>(uctx), info->si_addr); st.load_here(32, reinterpret_cast<void *>(uctx), info->si_addr);
} }
FILE* crashDump=fopen("furnace_crash.txt","w"); #ifdef _WIN32
MessageBox(NULL,"Error","Furnace has crashed! please report this to the issue tracker immediately:\r\nhttps://github.com/tildearrow/furnace/issues/new\r\n\r\na file called furnace_crash.txt will be created in your user directory.\r\nthis will be important for locating the origin of the crash.",MB_OK|MB_ICONERROR);
std::string crashLocation;
char* userProfile=getenv("USERPROFILE");
if (userProfile==NULL) {
crashLocation="C:\\furnace_crash.txt";
} else {
crashLocation=userProfile;
crashLocation+="\\furnace_crash.txt";
}
FILE* crashDump=fopen(crashLocation.c_str(),"w");
#else
FILE* crashDump=fopen("/tmp/furnace_crash.txt","w");
#endif
Printer printer; Printer printer;
printer.address = true; printer.address = true;
@ -4263,6 +4276,16 @@ public:
printer.address = true; printer.address = true;
printer.print(st, crashDump); printer.print(st, crashDump);
fclose(crashDump); fclose(crashDump);
} else {
#ifdef _WIN32
std::string str;
Printer failedPrinter;
failedPrinter.address = true;
failedPrinter.print(st, str);
str+="\r\ncould not open furnace_crash.txt!\r\nplease take a screenshot of this error message box!";
fprintf(stderr,"NOTICE: could not open furnace_crash.txt!\n");
MessageBox(NULL,"Error",str.c_str(),MB_OK|MB_ICONERROR);
#endif
} }
#if (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) || \ #if (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) || \

View file

@ -559,14 +559,44 @@ template<int BITCOUNT, int OFFSET = 0>
struct ImBitArray struct ImBitArray
{ {
ImU32 Storage[(BITCOUNT + 31) >> 5]; ImU32 Storage[(BITCOUNT + 31) >> 5];
ImBitArray() { ClearAllBits(); } ImBitArray() { ClearAllBits(); }
void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); } void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); }
void SetAllBits() { memset(Storage, 255, sizeof(Storage)); } void SetAllBits() { memset(Storage, 255, sizeof(Storage)); }
bool TestBit(int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return ImBitArrayTestBit(Storage, n); } bool TestBit(int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return ImBitArrayTestBit(Storage, n); }
void SetBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); } void SetBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); }
void ClearBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Storage, n); } void ClearBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Storage, n); }
void SetBitRange(int n, int n2) { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2) void SetBitRange(int n, int n2) { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2)
bool operator[](int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return ImBitArrayTestBit(Storage, n); } bool operator[](int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return ImBitArrayTestBit(Storage, n); }
ImBitArray& operator|=(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); return *this; }
bool operator&(int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return ImBitArrayTestBit(Storage, n); }
bool operator==(ImBitArray const &a) const
{
for (int i = 0; i < ((BITCOUNT + 31) >> 5); ++i)
if (Storage[i] != a.Storage[i])
return false;
return true;
}
bool operator!=(ImBitArray const &a) const
{
for (int i = 0; i < ((BITCOUNT + 31) >> 5); ++i)
if (Storage[i] == a.Storage[i])
return false;
return true;
}
template<int DSTBITCOUNT> bool operator==(ImBitArray<DSTBITCOUNT> const &a) const
{
for (int i = 0; i < ImMin((DSTBITCOUNT + 31) >> 5, (BITCOUNT + 31) >> 5); ++i)
if (Storage[i] != a.Storage[i])
return false;
return true;
}
template<int DSTBITCOUNT> bool operator!=(ImBitArray<DSTBITCOUNT> const &a) const
{
for (int i = 0; i < ImMin((DSTBITCOUNT + 31) >> 5, (BITCOUNT + 31) >> 5); ++i)
if (Storage[i] == a.Storage[i])
return false;
return true;
}
}; };
// Helper: ImBitVector // Helper: ImBitVector
@ -574,11 +604,33 @@ struct ImBitArray
struct IMGUI_API ImBitVector struct IMGUI_API ImBitVector
{ {
ImVector<ImU32> Storage; ImVector<ImU32> Storage;
void Create(int sz) { Storage.resize((sz + 31) >> 5); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); } int BitCount = 0;
void Clear() { Storage.clear(); } ImBitVector(int sz = 0) { if (sz > 0) { Create(sz); } }
bool TestBit(int n) const { IM_ASSERT(n < (Storage.Size << 5)); return ImBitArrayTestBit(Storage.Data, n); } void Create(int sz) { BitCount = sz; Storage.resize((sz + 31) >> 5); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); }
void SetBit(int n) { IM_ASSERT(n < (Storage.Size << 5)); ImBitArraySetBit(Storage.Data, n); } void Clear() { Storage.clear(); }
void ClearBit(int n) { IM_ASSERT(n < (Storage.Size << 5)); ImBitArrayClearBit(Storage.Data, n); } void ClearAllBits() { IM_ASSERT(Storage.Size > 0); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); }
void SetAllBits() { IM_ASSERT(Storage.Size > 0); memset(Storage.Data, 255, (size_t)Storage.Size * sizeof(Storage.Data[0])); }
bool TestBit(int n) const { IM_ASSERT(n >= 0 && n < BitCount); return ImBitArrayTestBit(Storage.Data, n); }
void SetBit(int n) { IM_ASSERT(n >= 0 && n < BitCount); ImBitArraySetBit(Storage.Data, n); }
void SetBitRange(int n, int n2) { IM_ASSERT(n >= 0 && n < BitCount && n2 > n && n2 <= BitCount); ImBitArraySetBitRange(Storage.Data, n, n2); } // Works on range [n..n2)
void ClearBit(int n) { IM_ASSERT(n >= 0 && n < BitCount); ImBitArrayClearBit(Storage.Data, n); }
bool operator[](int n) const { IM_ASSERT(n >= 0 && n < BitCount); return ImBitArrayTestBit(Storage.Data, n); }
ImBitVector& operator|=(int n) { IM_ASSERT(n >= 0 && n < BitCount); ImBitArraySetBit(Storage.Data, n); return *this; }
bool operator&(int n) const { IM_ASSERT(n >= 0 && n < BitCount); return ImBitArrayTestBit(Storage.Data, n); }
bool operator==(ImBitVector const &a) const
{
for (int i = 0; i < ImMin((a.BitCount + 31) >> 5, (BitCount + 31) >> 5); ++i)
if (Storage[i] != a.Storage[i])
return false;
return true;
}
bool operator!=(ImBitVector const &a) const
{
for (int i = 0; i < ImMin((a.BitCount + 31) >> 5, (BitCount + 31) >> 5); ++i)
if (Storage[i] == a.Storage[i])
return false;
return true;
}
}; };
// Helper: ImSpan<> // Helper: ImSpan<>
@ -2447,13 +2499,12 @@ struct IMGUI_API ImGuiTabBar
// [SECTION] Table support // [SECTION] Table support
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color. #define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color.
#define IMGUI_TABLE_MAX_COLUMNS 64 // sizeof(ImU64) * 8. This is solely because we frequently encode columns set in a ImU64. #define IMGUI_TABLE_DRAW_CHANNELS(c) (4 + (c) * 2) // See TableSetupDrawChannels()
#define IMGUI_TABLE_MAX_DRAW_CHANNELS (4 + 64 * 2) // See TableSetupDrawChannels()
// Our current column maximum is 64 but we may raise that in the future. // Our current column maximum is IMGUI_TABLE_MAX_COLUMNS but we may raise that in the future.
typedef ImS8 ImGuiTableColumnIdx; typedef ImS32 ImGuiTableColumnIdx;
typedef ImU8 ImGuiTableDrawChannelIdx; typedef ImU32 ImGuiTableDrawChannelIdx;
// [Internal] sizeof() ~ 104 // [Internal] sizeof() ~ 104
// We use the terminology "Enabled" to refer to a column that is not Hidden by user/api. // We use the terminology "Enabled" to refer to a column that is not Hidden by user/api.
@ -2543,10 +2594,10 @@ struct IMGUI_API ImGuiTable
ImSpan<ImGuiTableColumn> Columns; // Point within RawData[] ImSpan<ImGuiTableColumn> Columns; // Point within RawData[]
ImSpan<ImGuiTableColumnIdx> DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1) ImSpan<ImGuiTableColumnIdx> DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1)
ImSpan<ImGuiTableCellData> RowCellData; // Point within RawData[]. Store cells background requests for current row. ImSpan<ImGuiTableCellData> RowCellData; // Point within RawData[]. Store cells background requests for current row.
ImU64 EnabledMaskByDisplayOrder; // Column DisplayOrder -> IsEnabled map ImBitVector EnabledMaskByDisplayOrder; // Column DisplayOrder -> IsEnabled map
ImU64 EnabledMaskByIndex; // Column Index -> IsEnabled map (== not hidden by user/api) in a format adequate for iterating column without touching cold data ImBitVector EnabledMaskByIndex; // Column Index -> IsEnabled map (== not hidden by user/api) in a format adequate for iterating column without touching cold data
ImU64 VisibleMaskByIndex; // Column Index -> IsVisibleX|IsVisibleY map (== not hidden by user/api && not hidden by scrolling/cliprect) ImBitVector VisibleMaskByIndex; // Column Index -> IsVisibleX|IsVisibleY map (== not hidden by user/api && not hidden by scrolling/cliprect)
ImU64 RequestOutputMaskByIndex; // Column Index -> IsVisible || AutoFit (== expect user to submit items) ImBitVector RequestOutputMaskByIndex; // Column Index -> IsVisible || AutoFit (== expect user to submit items)
ImGuiTableFlags SettingsLoadedFlags; // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order) ImGuiTableFlags SettingsLoadedFlags; // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order)
int SettingsOffset; // Offset in g.SettingsTables int SettingsOffset; // Offset in g.SettingsTables
int LastFrameActive; int LastFrameActive;

View file

@ -315,7 +315,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
return false; return false;
// Sanity checks // Sanity checks
IM_ASSERT(columns_count > 0 && columns_count <= IMGUI_TABLE_MAX_COLUMNS && "Only 1..64 columns allowed!"); IM_ASSERT(columns_count > 0 && "Only 1..64 columns allowed!");
if (flags & ImGuiTableFlags_ScrollX) if (flags & ImGuiTableFlags_ScrollX)
IM_ASSERT(inner_width >= 0.0f); IM_ASSERT(inner_width >= 0.0f);
@ -358,6 +358,16 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->LastFrameActive = g.FrameCount; table->LastFrameActive = g.FrameCount;
table->OuterWindow = table->InnerWindow = outer_window; table->OuterWindow = table->InnerWindow = outer_window;
table->ColumnsCount = columns_count; table->ColumnsCount = columns_count;
if (table->EnabledMaskByDisplayOrder.BitCount < columns_count ||
table->EnabledMaskByIndex.BitCount < columns_count ||
table->VisibleMaskByIndex.BitCount < columns_count ||
table->RequestOutputMaskByIndex.BitCount < columns_count)
{
table->EnabledMaskByDisplayOrder.Create(columns_count);
table->EnabledMaskByIndex.Create(columns_count);
table->VisibleMaskByIndex.Create(columns_count);
table->RequestOutputMaskByIndex.Create(columns_count);
}
table->IsLayoutLocked = false; table->IsLayoutLocked = false;
table->InnerWidth = inner_width; table->InnerWidth = inner_width;
temp_data->UserOuterSize = outer_size; temp_data->UserOuterSize = outer_size;
@ -721,8 +731,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_); const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_);
table->IsDefaultDisplayOrder = true; table->IsDefaultDisplayOrder = true;
table->ColumnsEnabledCount = 0; table->ColumnsEnabledCount = 0;
table->EnabledMaskByIndex = 0x00; table->EnabledMaskByIndex.ClearAllBits();
table->EnabledMaskByDisplayOrder = 0x00; table->EnabledMaskByDisplayOrder.ClearAllBits();
table->LeftMostEnabledColumn = -1; table->LeftMostEnabledColumn = -1;
table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE
@ -787,8 +797,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
else else
table->LeftMostEnabledColumn = (ImGuiTableColumnIdx)column_n; table->LeftMostEnabledColumn = (ImGuiTableColumnIdx)column_n;
column->IndexWithinEnabledSet = table->ColumnsEnabledCount++; column->IndexWithinEnabledSet = table->ColumnsEnabledCount++;
table->EnabledMaskByIndex |= (ImU64)1 << column_n; table->EnabledMaskByIndex |= column_n;
table->EnabledMaskByDisplayOrder |= (ImU64)1 << column->DisplayOrder; table->EnabledMaskByDisplayOrder |= column->DisplayOrder;
prev_visible_column_idx = column_n; prev_visible_column_idx = column_n;
IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder); IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder);
@ -836,7 +846,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1; table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++) for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{ {
if (!(table->EnabledMaskByIndex & ((ImU64)1 << column_n))) if (!(table->EnabledMaskByIndex & column_n))
continue; continue;
ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiTableColumn* column = &table->Columns[column_n];
@ -852,7 +862,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!) // Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!)
if (column->AutoFitQueue != 0x00) if (column->AutoFitQueue != 0x00)
column->WidthRequest = width_auto; column->WidthRequest = width_auto;
else if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable && (table->RequestOutputMaskByIndex & ((ImU64)1 << column_n))) else if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable && (table->RequestOutputMaskByIndex & column_n))
column->WidthRequest = width_auto; column->WidthRequest = width_auto;
// FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets // FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets
@ -899,7 +909,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount; table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++) for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{ {
if (!(table->EnabledMaskByIndex & ((ImU64)1 << column_n))) if (!(table->EnabledMaskByIndex & column_n))
continue; continue;
ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiTableColumn* column = &table->Columns[column_n];
@ -926,7 +936,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths)) if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths))
for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--) for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--)
{ {
if (!(table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n))) if (!(table->EnabledMaskByDisplayOrder & order_n))
continue; continue;
ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]]; ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]];
if (!(column->Flags & ImGuiTableColumnFlags_WidthStretch)) if (!(column->Flags & ImGuiTableColumnFlags_WidthStretch))
@ -949,8 +959,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1; float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1;
ImRect host_clip_rect = table->InnerClipRect; ImRect host_clip_rect = table->InnerClipRect;
//host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2; //host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2;
table->VisibleMaskByIndex = 0x00; table->VisibleMaskByIndex.ClearAllBits();
table->RequestOutputMaskByIndex = 0x00; table->RequestOutputMaskByIndex.ClearAllBits();
for (int order_n = 0; order_n < table->ColumnsCount; order_n++) for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{ {
const int column_n = table->DisplayOrderToIndex[order_n]; const int column_n = table->DisplayOrderToIndex[order_n];
@ -967,7 +977,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// Clear status flags // Clear status flags
column->Flags &= ~ImGuiTableColumnFlags_StatusMask_; column->Flags &= ~ImGuiTableColumnFlags_StatusMask_;
if ((table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n)) == 0) if ((table->EnabledMaskByDisplayOrder & order_n) == 0)
{ {
// Hidden column: clear a few fields and we are done with it for the remainder of the function. // Hidden column: clear a few fields and we are done with it for the remainder of the function.
// We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper. // We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper.
@ -1020,12 +1030,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
column->IsVisibleY = true; // (column->ClipRect.Max.y > column->ClipRect.Min.y); column->IsVisibleY = true; // (column->ClipRect.Max.y > column->ClipRect.Min.y);
const bool is_visible = column->IsVisibleX; //&& column->IsVisibleY; const bool is_visible = column->IsVisibleX; //&& column->IsVisibleY;
if (is_visible) if (is_visible)
table->VisibleMaskByIndex |= ((ImU64)1 << column_n); table->VisibleMaskByIndex |= column_n;
// Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output. // Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output.
column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0; column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0;
if (column->IsRequestOutput) if (column->IsRequestOutput)
table->RequestOutputMaskByIndex |= ((ImU64)1 << column_n); table->RequestOutputMaskByIndex |= column_n;
// Mark column as SkipItems (ignoring all items/layout) // Mark column as SkipItems (ignoring all items/layout)
column->IsSkipItems = !column->IsEnabled || table->HostSkipItems; column->IsSkipItems = !column->IsEnabled || table->HostSkipItems;
@ -1153,7 +1163,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
for (int order_n = 0; order_n < table->ColumnsCount; order_n++) for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{ {
if (!(table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n))) if (!(table->EnabledMaskByDisplayOrder & order_n))
continue; continue;
const int column_n = table->DisplayOrderToIndex[order_n]; const int column_n = table->DisplayOrderToIndex[order_n];
@ -1289,7 +1299,7 @@ void ImGui::EndTable()
float auto_fit_width_for_stretched = 0.0f; float auto_fit_width_for_stretched = 0.0f;
float auto_fit_width_for_stretched_min = 0.0f; float auto_fit_width_for_stretched_min = 0.0f;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++) for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
if (table->EnabledMaskByIndex & ((ImU64)1 << column_n)) if (table->EnabledMaskByIndex & column_n)
{ {
ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiTableColumn* column = &table->Columns[column_n];
float column_width_request = ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !(column->Flags & ImGuiTableColumnFlags_NoResize)) ? column->WidthRequest : TableGetColumnWidthAuto(table, column); float column_width_request = ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !(column->Flags & ImGuiTableColumnFlags_NoResize)) ? column->WidthRequest : TableGetColumnWidthAuto(table, column);
@ -1480,7 +1490,7 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows)
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!"); IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!");
IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!"); IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS); IM_ASSERT(columns >= 0);
IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit
table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)ImMin(columns, table->ColumnsCount) : 0; table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)ImMin(columns, table->ColumnsCount) : 0;
@ -1635,7 +1645,7 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n
return; return;
if (column_n == -1) if (column_n == -1)
column_n = table->CurrentColumn; column_n = table->CurrentColumn;
if ((table->VisibleMaskByIndex & ((ImU64)1 << column_n)) == 0) if ((table->VisibleMaskByIndex & column_n) == 0)
return; return;
if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n) if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n)
table->RowCellDataCurrent++; table->RowCellDataCurrent++;
@ -1910,7 +1920,7 @@ bool ImGui::TableSetColumnIndex(int column_n)
// Return whether the column is visible. User may choose to skip submitting items based on this return value, // Return whether the column is visible. User may choose to skip submitting items based on this return value,
// however they shouldn't skip submitting for columns that may have the tallest contribution to row height. // however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
return (table->RequestOutputMaskByIndex & ((ImU64)1 << column_n)) != 0; return (table->RequestOutputMaskByIndex & column_n) != 0;
} }
// [Public] Append into the next column, wrap and create a new row when already on last column // [Public] Append into the next column, wrap and create a new row when already on last column
@ -1936,7 +1946,7 @@ bool ImGui::TableNextColumn()
// Return whether the column is visible. User may choose to skip submitting items based on this return value, // Return whether the column is visible. User may choose to skip submitting items based on this return value,
// however they shouldn't skip submitting for columns that may have the tallest contribution to row height. // however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
int column_n = table->CurrentColumn; int column_n = table->CurrentColumn;
return (table->RequestOutputMaskByIndex & ((ImU64)1 << column_n)) != 0; return (table->RequestOutputMaskByIndex & column_n) != 0;
} }
@ -2349,17 +2359,23 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
{ {
ImRect ClipRect; ImRect ClipRect;
int ChannelsCount; int ChannelsCount;
ImBitArray<IMGUI_TABLE_MAX_DRAW_CHANNELS> ChannelsMask; ImBitVector ChannelsMask;
MergeGroup() { ChannelsCount = 0; } MergeGroup(int sz) : ChannelsMask(sz) { ChannelsCount = 0; }
}; };
int merge_group_mask = 0x00; int merge_group_mask = 0x00;
MergeGroup merge_groups[4]; int merge_group_bitlen = IMGUI_TABLE_DRAW_CHANNELS(table->ColumnsCount);
MergeGroup merge_groups[4]{
merge_group_bitlen,
merge_group_bitlen,
merge_group_bitlen,
merge_group_bitlen
};
// 1. Scan channels and take note of those which can be merged // 1. Scan channels and take note of those which can be merged
for (int column_n = 0; column_n < table->ColumnsCount; column_n++) for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{ {
if ((table->VisibleMaskByIndex & ((ImU64)1 << column_n)) == 0) if ((table->VisibleMaskByIndex & column_n) == 0)
continue; continue;
ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiTableColumn* column = &table->Columns[column_n];
@ -2391,7 +2407,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
} }
const int merge_group_n = (has_freeze_h && column_n < table->FreezeColumnsCount ? 0 : 1) + (has_freeze_v && merge_group_sub_n == 0 ? 0 : 2); const int merge_group_n = (has_freeze_h && column_n < table->FreezeColumnsCount ? 0 : 1) + (has_freeze_v && merge_group_sub_n == 0 ? 0 : 2);
IM_ASSERT(channel_no < IMGUI_TABLE_MAX_DRAW_CHANNELS); IM_ASSERT(channel_no < merge_group_bitlen);
MergeGroup* merge_group = &merge_groups[merge_group_n]; MergeGroup* merge_group = &merge_groups[merge_group_n];
if (merge_group->ChannelsCount == 0) if (merge_group->ChannelsCount == 0)
merge_group->ClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); merge_group->ClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
@ -2431,7 +2447,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
const int LEADING_DRAW_CHANNELS = 2; const int LEADING_DRAW_CHANNELS = 2;
g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized
ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data; ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data;
ImBitArray<IMGUI_TABLE_MAX_DRAW_CHANNELS> remaining_mask; // We need 132-bit of storage ImBitVector remaining_mask(merge_group_bitlen); // We need 132-bit of storage
remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count); remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count);
remaining_mask.ClearBit(table->Bg2DrawChannelUnfrozen); remaining_mask.ClearBit(table->Bg2DrawChannelUnfrozen);
IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN); IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN);
@ -2466,7 +2482,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
GetOverlayDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200)); GetOverlayDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200));
#endif #endif
remaining_count -= merge_group->ChannelsCount; remaining_count -= merge_group->ChannelsCount;
for (int n = 0; n < IM_ARRAYSIZE(remaining_mask.Storage); n++) for (int n = 0; n < remaining_mask.Storage.size(); n++)
remaining_mask.Storage[n] &= ~merge_group->ChannelsMask.Storage[n]; remaining_mask.Storage[n] &= ~merge_group->ChannelsMask.Storage[n];
for (int n = 0; n < splitter->_Count && merge_channels_count != 0; n++) for (int n = 0; n < splitter->_Count && merge_channels_count != 0; n++)
{ {
@ -2523,7 +2539,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
{ {
for (int order_n = 0; order_n < table->ColumnsCount; order_n++) for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{ {
if (!(table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n))) if (!(table->EnabledMaskByDisplayOrder & order_n))
continue; continue;
const int column_n = table->DisplayOrderToIndex[order_n]; const int column_n = table->DisplayOrderToIndex[order_n];

View file

@ -29,6 +29,8 @@ furthermore, an `or reserved` indicates this field is always present, but is res
the format versions are: the format versions are:
- 99: Furnace dev99
- 98: Furnace dev98
- 97: Furnace dev97 - 97: Furnace dev97
- 96: Furnace dev96 - 96: Furnace dev96
- 95: Furnace dev95 - 95: Furnace dev95
@ -226,6 +228,9 @@ size | description
| - 0xb9: Namco WSG - 3 channels | - 0xb9: Namco WSG - 3 channels
| - 0xba: Namco 15xx - 8 channels | - 0xba: Namco 15xx - 8 channels
| - 0xbb: Namco CUS30 - 8 channels | - 0xbb: Namco CUS30 - 8 channels
| - 0xbc: reserved - 8 channels
| - 0xbd: YM2612 extra features extended - 11 channels
| - 0xbe: YM2612 extra features - 7 channels
| - 0xde: YM2610B extended - 19 channels | - 0xde: YM2610B extended - 19 channels
| - 0xe0: QSound - 19 channels | - 0xe0: QSound - 19 channels
| - 0xfd: Dummy System - 8 channels | - 0xfd: Dummy System - 8 channels
@ -302,7 +307,11 @@ size | description
1 | pitch macro is linear (>=90) or reserved 1 | pitch macro is linear (>=90) or reserved
1 | pitch slide speed in full linear pitch mode (>=94) or reserved 1 | pitch slide speed in full linear pitch mode (>=94) or reserved
1 | old octave boundary behavior (>=97) or reserved 1 | old octave boundary behavior (>=97) or reserved
13 | reserved 1 | disable OPN2 DAC volume control (>=98) or reserved
1 | new volume scaling strategy (>=99) or reserved
1 | volume macro still applies after end (>=99) or reserved
1 | broken outVol (>=99) or reserved
9 | reserved
--- | **virtual tempo data** --- | **virtual tempo data**
2 | virtual tempo numerator of first song (>=96) or reserved 2 | virtual tempo numerator of first song (>=96) or reserved
2 | virtual tempo denominator of first song (>=96) or reserved 2 | virtual tempo denominator of first song (>=96) or reserved

View file

@ -59,6 +59,7 @@ enum DivDispatchCmds {
DIV_CMD_SAMPLE_FREQ, // (frequency) DIV_CMD_SAMPLE_FREQ, // (frequency)
DIV_CMD_SAMPLE_BANK, // (bank) DIV_CMD_SAMPLE_BANK, // (bank)
DIV_CMD_SAMPLE_POS, // (pos) DIV_CMD_SAMPLE_POS, // (pos)
DIV_CMD_SAMPLE_DIR, // (direction)
DIV_CMD_SAMPLE_TRANSWAVE_SLICE_MODE, // (enabled) DIV_CMD_SAMPLE_TRANSWAVE_SLICE_MODE, // (enabled)
DIV_CMD_SAMPLE_TRANSWAVE_SLICE_POS, // (slice) DIV_CMD_SAMPLE_TRANSWAVE_SLICE_POS, // (slice)
@ -236,6 +237,8 @@ struct DivRegWrite {
* - data is the sample rate * - data is the sample rate
* - 0xffffxx02: stop sample playback * - 0xffffxx02: stop sample playback
* - xx is the instance ID * - xx is the instance ID
* - 0xffffxx03: set sample playback direction
* - x is the instance ID
* - 0xffffffff: reset * - 0xffffffff: reset
*/ */
unsigned int addr; unsigned int addr;

View file

@ -185,10 +185,22 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_YM2612: case DIV_SYSTEM_YM2612:
dispatch=new DivPlatformGenesis; dispatch=new DivPlatformGenesis;
((DivPlatformGenesis*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0)); ((DivPlatformGenesis*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
((DivPlatformGenesis*)dispatch)->setSoftPCM(false);
break; break;
case DIV_SYSTEM_YM2612_EXT: case DIV_SYSTEM_YM2612_EXT:
dispatch=new DivPlatformGenesisExt; dispatch=new DivPlatformGenesisExt;
((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0)); ((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
((DivPlatformGenesisExt*)dispatch)->setSoftPCM(false);
break;
case DIV_SYSTEM_YM2612_FRAC:
dispatch=new DivPlatformGenesis;
((DivPlatformGenesis*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
((DivPlatformGenesis*)dispatch)->setSoftPCM(true);
break;
case DIV_SYSTEM_YM2612_FRAC_EXT:
dispatch=new DivPlatformGenesisExt;
((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
((DivPlatformGenesisExt*)dispatch)->setSoftPCM(true);
break; break;
case DIV_SYSTEM_SMS: case DIV_SYSTEM_SMS:
dispatch=new DivPlatformSMS; dispatch=new DivPlatformSMS;

View file

@ -697,7 +697,7 @@ void DivEngine::initSongWithDesc(const int* description) {
song.systemFlags[index]=description[i+3]; song.systemFlags[index]=description[i+3];
index++; index++;
chanCount+=getChannelCount(song.system[index]); chanCount+=getChannelCount(song.system[index]);
if (chanCount>=63) break; if (chanCount>=DIV_MAX_CHANS) break;
if (index>=32) break; if (index>=32) break;
} }
song.systemLen=index; song.systemLen=index;
@ -887,9 +887,8 @@ bool DivEngine::addSystem(DivSystem which) {
lastError="max number of systems is 32"; lastError="max number of systems is 32";
return false; return false;
} }
// this was DIV_MAX_CHANS but I am setting it to 63 for now due to an ImGui limitation if (chans+getChannelCount(which)>DIV_MAX_CHANS) {
if (chans+getChannelCount(which)>63) { lastError=fmt::sprintf("max number of total channels is %d",DIV_MAX_CHANS);
lastError="max number of total channels is 63";
return false; return false;
} }
quitDispatch(); quitDispatch();
@ -1773,7 +1772,7 @@ int DivEngine::addWave() {
return waveCount; return waveCount;
} }
bool DivEngine::addWaveFromFile(const char* path) { bool DivEngine::addWaveFromFile(const char* path, bool addRaw) {
if (song.wave.size()>=256) { if (song.wave.size()>=256) {
lastError="too many wavetables!"; lastError="too many wavetables!";
return false; return false;
@ -1869,8 +1868,27 @@ bool DivEngine::addWaveFromFile(const char* path) {
} }
} else { } else {
// read as binary // read as binary
logI("reading binary..."); if (addRaw) {
logI("reading binary...");
len=reader.size();
if (len>256) len=256;
reader.seek(0,SEEK_SET);
for (int i=0; i<len; i++) {
wave->data[i]=(unsigned char)reader.readC();
if (wave->max<wave->data[i]) wave->max=wave->data[i];
}
wave->len=len;
} else {
delete wave;
delete[] buf;
return false;
}
}
} catch (EndOfFileException& e) {
// read as binary
if (addRaw) {
len=reader.size(); len=reader.size();
logI("reading binary for being too small...");
if (len>256) len=256; if (len>256) len=256;
reader.seek(0,SEEK_SET); reader.seek(0,SEEK_SET);
for (int i=0; i<len; i++) { for (int i=0; i<len; i++) {
@ -1878,18 +1896,11 @@ bool DivEngine::addWaveFromFile(const char* path) {
if (wave->max<wave->data[i]) wave->max=wave->data[i]; if (wave->max<wave->data[i]) wave->max=wave->data[i];
} }
wave->len=len; wave->len=len;
} else {
delete wave;
delete[] buf;
return false;
} }
} catch (EndOfFileException& e) {
// read as binary
len=reader.size();
logI("reading binary for being too small...");
if (len>256) len=256;
reader.seek(0,SEEK_SET);
for (int i=0; i<len; i++) {
wave->data[i]=(unsigned char)reader.readC();
if (wave->max<wave->data[i]) wave->max=wave->data[i];
}
wave->len=len;
} }
} }
} catch (EndOfFileException& e) { } catch (EndOfFileException& e) {

View file

@ -45,8 +45,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false; #define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev97" #define DIV_VERSION "dev99"
#define DIV_ENGINE_VERSION 97 #define DIV_ENGINE_VERSION 99
// for imports // for imports
#define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_MOD 0xff01
@ -377,7 +377,7 @@ class DivEngine {
void processRow(int i, bool afterDelay); void processRow(int i, bool afterDelay);
void nextOrder(); void nextOrder();
void nextRow(); void nextRow();
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool isSecond); void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond);
// returns true if end of song. // returns true if end of song.
bool nextTick(bool noAccum=false, bool inhibitLowLat=false); bool nextTick(bool noAccum=false, bool inhibitLowLat=false);
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal); bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);
@ -676,7 +676,7 @@ class DivEngine {
int addWave(); int addWave();
// add wavetable from file // add wavetable from file
bool addWaveFromFile(const char* path); bool addWaveFromFile(const char* path, bool loadRaw=true);
// delete wavetable // delete wavetable
void delWave(int index); void delWave(int index);

View file

@ -167,6 +167,10 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.fbPortaPause=true; ds.fbPortaPause=true;
ds.snDutyReset=true; ds.snDutyReset=true;
ds.oldOctaveBoundary=false; ds.oldOctaveBoundary=false;
ds.noOPN2Vol=true;
ds.newVolumeScaling=false;
ds.volMacroLinger=false;
ds.brokenOutVol=true; // ???
// 1.1 compat flags // 1.1 compat flags
if (ds.version>24) { if (ds.version>24) {
@ -1031,6 +1035,14 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<97) { if (ds.version<97) {
ds.oldOctaveBoundary=true; ds.oldOctaveBoundary=true;
} }
if (ds.version<97) { // actually should be 98 but yky uses this feature ahead of time
ds.noOPN2Vol=true;
}
if (ds.version<99) {
ds.newVolumeScaling=false;
ds.volMacroLinger=false;
ds.brokenOutVol=true;
}
ds.isDMF=false; ds.isDMF=false;
reader.readS(); // reserved reader.readS(); // reserved
@ -1413,7 +1425,21 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} else { } else {
reader.readC(); reader.readC();
} }
for (int i=0; i<13; i++) { if (ds.version>=98) {
ds.noOPN2Vol=reader.readC();
} else {
reader.readC();
}
if (ds.version>=99) {
ds.newVolumeScaling=reader.readC();
ds.volMacroLinger=reader.readC();
ds.brokenOutVol=reader.readC();
} else {
reader.readC();
reader.readC();
reader.readC();
}
for (int i=0; i<9; i++) {
reader.readC(); reader.readC();
} }
} }
@ -2894,7 +2920,11 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeC(song.pitchMacroIsLinear); w->writeC(song.pitchMacroIsLinear);
w->writeC(song.pitchSlideSpeed); w->writeC(song.pitchSlideSpeed);
w->writeC(song.oldOctaveBoundary); w->writeC(song.oldOctaveBoundary);
for (int i=0; i<13; i++) { w->writeC(song.noOPN2Vol);
w->writeC(song.newVolumeScaling);
w->writeC(song.volMacroLinger);
w->writeC(song.brokenOutVol);
for (int i=0; i<9; i++) {
w->writeC(0); w->writeC(0);
} }

View file

@ -125,50 +125,108 @@ const char* DivPlatformGenesis::getEffectName(unsigned char effect) {
case 0x5f: case 0x5f:
return "5Fxx: Set decay 2 of operator 4 (0 to 1F)"; return "5Fxx: Set decay 2 of operator 4 (0 to 1F)";
break; break;
case 0xdf:
return "DFxx: Set sample playback direction (0: normal; 1: reverse)";
break;
} }
return NULL; return NULL;
} }
void DivPlatformGenesis::processDAC() {
if (softPCM) {
softPCMTimer+=chipClock/576;
if (softPCMTimer>rate) {
softPCMTimer-=rate;
int sample=0;
for (int i=5; i<7; i++) {
if (chan[i].dacSample!=-1) {
DivSample* s=parent->getSample(chan[i].dacSample);
if (!isMuted[i] && s->samples>0) {
if (parent->song.noOPN2Vol) {
sample+=s->data8[chan[i].getDacDirection()?(s->samples-chan[i].dacPos-1):chan[i].dacPos];
} else {
sample+=(s->data8[chan[i].getDacDirection()?(s->samples-chan[i].dacPos-1):chan[i].dacPos]*dacVolTable[chan[i].outVol])>>7;
}
}
chan[i].dacPeriod+=chan[i].dacRate;
if (chan[i].dacPeriod>=(chipClock/576)) {
if (s->samples>0) {
while (chan[i].dacPeriod>=(chipClock/576)) {
chan[i].dacPos++;
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && chan[i].dacPos>=s->loopEnd) || (chan[i].dacPos>=s->samples)) {
if (s->isLoopable() && !chan[i].getDacDirection()) {
chan[i].dacPos=s->loopStart;
} else {
chan[i].dacSample=-1;
chan[i].dacPeriod=0;
break;
}
}
chan[i].dacPeriod-=(chipClock/576);
}
} else {
chan[i].dacSample=-1;
}
}
}
}
//sample>>=1;
if (sample<-128) sample=-128;
if (sample>127) sample=127;
urgentWrite(0x2a,(unsigned char)sample+0x80);
}
} else {
if (!chan[5].dacReady) {
chan[5].dacDelay+=32000;
if (chan[5].dacDelay>=rate) {
chan[5].dacDelay-=rate;
chan[5].dacReady=true;
}
}
if (chan[5].dacMode && chan[5].dacSample!=-1) {
chan[5].dacPeriod+=chan[5].dacRate;
if (chan[5].dacPeriod>=rate) {
DivSample* s=parent->getSample(chan[5].dacSample);
if (s->samples>0) {
if (!isMuted[5]) {
if (chan[5].dacReady && writes.size()<16) {
int sample;
if (parent->song.noOPN2Vol) {
sample=s->data8[chan[5].getDacDirection()?(s->samples-chan[5].dacPos-1):chan[5].dacPos];
} else {
sample=(s->data8[chan[5].getDacDirection()?(s->samples-chan[5].dacPos-1):chan[5].dacPos]*dacVolTable[chan[5].outVol])>>7;
}
urgentWrite(0x2a,(unsigned char)sample+0x80);
chan[5].dacReady=false;
}
}
chan[5].dacPos++;
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && chan[5].dacPos>=s->loopEnd) || (chan[5].dacPos>=s->samples)) {
if (s->isLoopable() && !chan[5].getDacDirection()) {
chan[5].dacPos=s->loopStart;
} else {
chan[5].dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
}
}
while (chan[5].dacPeriod>=rate) chan[5].dacPeriod-=rate;
} else {
chan[5].dacSample=-1;
}
}
}
}
}
void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) { void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
static short o[2]; static short o[2];
static int os[2]; static int os[2];
for (size_t h=start; h<start+len; h++) { for (size_t h=start; h<start+len; h++) {
if (!dacReady) { processDAC();
dacDelay+=32000;
if (dacDelay>=rate) {
dacDelay-=rate;
dacReady=true;
}
}
if (dacMode && dacSample!=-1) {
dacPeriod+=dacRate;
if (dacPeriod>=rate) {
DivSample* s=parent->getSample(dacSample);
if (s->samples>0) {
if (!isMuted[5]) {
if (dacReady && writes.size()<16) {
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
dacReady=false;
}
}
dacPos++;
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && dacPos>=s->loopEnd) || (dacPos>=s->samples)) {
if (s->isLoopable()) {
dacPos=s->loopStart;
} else {
dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
}
}
while (dacPeriod>=rate) dacPeriod-=rate;
} else {
dacSample=-1;
}
}
}
os[0]=0; os[1]=0; os[0]=0; os[1]=0;
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
@ -215,41 +273,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
ymfm::ym2612::fm_engine* fme=fm_ymfm->debug_engine(); ymfm::ym2612::fm_engine* fme=fm_ymfm->debug_engine();
for (size_t h=start; h<start+len; h++) { for (size_t h=start; h<start+len; h++) {
if (!dacReady) { processDAC();
dacDelay+=32000;
if (dacDelay>=rate) {
dacDelay-=rate;
dacReady=true;
}
}
if (dacMode && dacSample!=-1) {
dacPeriod+=dacRate;
if (dacPeriod>=rate) {
DivSample* s=parent->getSample(dacSample);
if (s->samples>0) {
if (!isMuted[5]) {
if (dacReady && writes.size()<16) {
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
dacReady=false;
}
}
dacPos++;
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && dacPos>=s->loopEnd) || (dacPos>=s->samples)) {
if (s->isLoopable()) {
dacPos=s->loopStart;
} else {
dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
}
}
while (dacPeriod>=rate) dacPeriod-=rate;
} else {
dacSample=-1;
}
}
}
os[0]=0; os[1]=0; os[0]=0; os[1]=0;
if (!writes.empty()) { if (!writes.empty()) {
@ -474,7 +498,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
} }
for (int i=0; i<6; i++) { for (int i=0; i<8; i++) {
if (i==2 && extMode) continue; if (i==2 && extMode) continue;
if (chan[i].freqChanged) { if (chan[i].freqChanged) {
if (parent->song.linearPitch==2) { if (parent->song.linearPitch==2) {
@ -493,12 +517,14 @@ void DivPlatformGenesis::tick(bool sysTick) {
chan[i].freq=(block<<11)|fNum; chan[i].freq=(block<<11)|fNum;
} }
if (chan[i].freq>0x3fff) chan[i].freq=0x3fff; if (chan[i].freq>0x3fff) chan[i].freq=0x3fff;
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8); if (i<6) {
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff); immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
if (chan[i].furnaceDac && dacMode) { immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
}
if (chan[i].furnaceDac && chan[i].dacMode) {
double off=1.0; double off=1.0;
if (dacSample>=0 && dacSample<parent->song.sampleLen) { if (chan[i].dacSample>=0 && chan[i].dacSample<parent->song.sampleLen) {
DivSample* s=parent->getSample(dacSample); DivSample* s=parent->getSample(chan[i].dacSample);
if (s->centerRate<1) { if (s->centerRate<1) {
off=1.0; off=1.0;
} else { } else {
@ -506,14 +532,14 @@ void DivPlatformGenesis::tick(bool sysTick) {
} }
} }
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,1,1); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,1,1);
dacRate=chan[i].freq*off; chan[i].dacRate=chan[i].freq*off;
if (dacRate<1) dacRate=1; if (chan[i].dacRate<1) chan[i].dacRate=1;
if (dumpWrites) addWrite(0xffff0001,dacRate); if (dumpWrites) addWrite(0xffff0001,chan[i].dacRate);
} }
chan[i].freqChanged=false; chan[i].freqChanged=false;
} }
if (chan[i].keyOn) { if (chan[i].keyOn) {
immWrite(0x28,0xf0|konOffs[i]); if (i<6) immWrite(0x28,0xf0|konOffs[i]);
chan[i].keyOn=false; chan[i].keyOn=false;
} }
} }
@ -521,6 +547,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
void DivPlatformGenesis::muteChannel(int ch, bool mute) { void DivPlatformGenesis::muteChannel(int ch, bool mute) {
isMuted[ch]=mute; isMuted[ch]=mute;
if (ch>5) return;
for (int j=0; j<4; j++) { for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[j]; unsigned short baseAddr=chanOffs[ch]|opOffs[j];
DivInstrumentFM::Operator& op=chan[ch].state.op[j]; DivInstrumentFM::Operator& op=chan[ch].state.op[j];
@ -541,29 +568,33 @@ int DivPlatformGenesis::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_FM); DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (c.chan==5) { if (c.chan>=5) {
if (ins->type==DIV_INS_AMIGA) { if (ins->type==DIV_INS_AMIGA) {
dacMode=1; chan[c.chan].dacMode=1;
rWrite(0x2b,1<<7); rWrite(0x2b,1<<7);
} else if (chan[c.chan].furnaceDac) { } else if (chan[c.chan].furnaceDac) {
dacMode=0; chan[c.chan].dacMode=0;
rWrite(0x2b,0<<7); rWrite(0x2b,0<<7);
} }
} }
if (c.chan==5 && 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
dacSample=ins->amiga.getSample(c.value); chan[c.chan].dacSample=ins->amiga.getSample(c.value);
if (dacSample<0 || dacSample>=parent->song.sampleLen) { if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
dacSample=-1; chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0); if (dumpWrites) addWrite(0xffff0002,0);
break; break;
} else { } else {
chan[c.chan].dacReversed=ins->amiga.getReversed(c.value);
rWrite(0x2b,1<<7); rWrite(0x2b,1<<7);
if (dumpWrites) addWrite(0xffff0000,dacSample); if (dumpWrites) {
addWrite(0xffff0000,chan[c.chan].dacSample);
addWrite(0xffff0003,chan[c.chan].getDacDirection());
}
} }
dacPos=0; chan[c.chan].dacPos=0;
dacPeriod=0; chan[c.chan].dacPeriod=0;
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
@ -573,23 +604,25 @@ int DivPlatformGenesis::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;
} }
dacSample=12*sampleBank+chan[c.chan].note%12; chan[c.chan].dacSample=12*chan[c.chan].sampleBank+chan[c.chan].note%12;
if (dacSample>=parent->song.sampleLen) { if (chan[c.chan].dacSample>=parent->song.sampleLen) {
dacSample=-1; chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0); if (dumpWrites) addWrite(0xffff0002,0);
break; break;
} else { } else {
rWrite(0x2b,1<<7); rWrite(0x2b,1<<7);
if (dumpWrites) addWrite(0xffff0000,dacSample); if (dumpWrites) addWrite(0xffff0000,chan[c.chan].dacSample);
} }
dacPos=0; chan[c.chan].dacPos=0;
dacPeriod=0; chan[c.chan].dacPeriod=0;
dacRate=MAX(1,parent->getSample(dacSample)->rate); chan[c.chan].dacRate=MAX(1,parent->getSample(chan[c.chan].dacSample)->rate);
if (dumpWrites) addWrite(0xffff0001,parent->getSample(dacSample)->rate); if (dumpWrites) addWrite(0xffff0001,parent->getSample(chan[c.chan].dacSample)->rate);
chan[c.chan].furnaceDac=false; chan[c.chan].furnaceDac=false;
chan[c.chan].dacReversed=false;
} }
break; break;
} }
if (c.chan>=6) break;
if (chan[c.chan].insChanged) { if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm; chan[c.chan].state=ins->fm;
@ -642,12 +675,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_NOTE_OFF: case DIV_CMD_NOTE_OFF:
if (c.chan==5) { if (c.chan>=5) {
dacSample=-1; chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0); if (dumpWrites) addWrite(0xffff0002,0);
if (parent->song.brokenDACMode) { if (parent->song.brokenDACMode) {
rWrite(0x2b,0); rWrite(0x2b,0);
if (dacMode) break; if (chan[c.chan].dacMode) break;
} }
} }
chan[c.chan].keyOff=true; chan[c.chan].keyOff=true;
@ -655,8 +688,8 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
chan[c.chan].active=false; chan[c.chan].active=false;
break; break;
case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_NOTE_OFF_ENV:
if (c.chan==5) { if (c.chan>=5) {
dacSample=-1; chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0); if (dumpWrites) addWrite(0xffff0002,0);
} }
chan[c.chan].keyOff=true; chan[c.chan].keyOff=true;
@ -672,6 +705,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) { if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value; chan[c.chan].outVol=c.value;
} }
if (c.chan>=6) break;
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -698,6 +732,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
chan[c.chan].ins=c.value; chan[c.chan].ins=c.value;
break; break;
case DIV_CMD_PANNING: { case DIV_CMD_PANNING: {
if (c.chan>5) c.chan=5;
if (c.value==0 && c.value2==0) { if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3; chan[c.chan].pan=3;
} else { } else {
@ -735,7 +770,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
} }
break; break;
} }
if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) { if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
int destFreq=parent->calcBaseFreq(1,1,c.value2,false); int destFreq=parent->calcBaseFreq(1,1,c.value2,false);
bool return2=false; bool return2=false;
if (destFreq>chan[c.chan].baseFreq) { if (destFreq>chan[c.chan].baseFreq) {
@ -762,18 +797,26 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_SAMPLE_MODE: { case DIV_CMD_SAMPLE_MODE: {
dacMode=c.value; if (c.chan<5) c.chan=5;
chan[c.chan].dacMode=c.value;
rWrite(0x2b,c.value<<7); rWrite(0x2b,c.value<<7);
break; break;
} }
case DIV_CMD_SAMPLE_BANK: case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value; if (c.chan<5) c.chan=5;
if (sampleBank>(parent->song.sample.size()/12)) { chan[c.chan].sampleBank=c.value;
sampleBank=parent->song.sample.size()/12; if (chan[c.chan].sampleBank>(parent->song.sample.size()/12)) {
chan[c.chan].sampleBank=parent->song.sample.size()/12;
} }
break; break;
case DIV_CMD_SAMPLE_DIR: {
if (c.chan<5) c.chan=5;
chan[c.chan].dacDirection=c.value;
if (dumpWrites) addWrite(0xffff0003,chan[c.chan].dacDirection);
break;
}
case DIV_CMD_LEGATO: { case DIV_CMD_LEGATO: {
if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) { if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
} else { } else {
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
@ -783,16 +826,19 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_LFO: { case DIV_CMD_FM_LFO: {
if (c.chan>=6) break;
lfoValue=(c.value&7)|((c.value>>4)<<3); lfoValue=(c.value&7)|((c.value>>4)<<3);
rWrite(0x22,lfoValue); rWrite(0x22,lfoValue);
break; break;
} }
case DIV_CMD_FM_FB: { case DIV_CMD_FM_FB: {
if (c.chan>=6) break;
chan[c.chan].state.fb=c.value&7; chan[c.chan].state.fb=c.value&7;
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
break; break;
} }
case DIV_CMD_FM_MULT: { case DIV_CMD_FM_MULT: {
if (c.chan>=6) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.mult=c.value2&15; op.mult=c.value2&15;
@ -800,6 +846,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_TL: { case DIV_CMD_FM_TL: {
if (c.chan>=6) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2; op.tl=c.value2;
@ -815,6 +862,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_AR: { case DIV_CMD_FM_AR: {
if (c.chan>=6) break;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -831,6 +879,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_RS: { case DIV_CMD_FM_RS: {
if (c.chan>=6) break;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -847,6 +896,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_AM: { case DIV_CMD_FM_AM: {
if (c.chan>=6) break;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -863,6 +913,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_DR: { case DIV_CMD_FM_DR: {
if (c.chan>=6) break;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -879,6 +930,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_SL: { case DIV_CMD_FM_SL: {
if (c.chan>=6) break;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -895,6 +947,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_RR: { case DIV_CMD_FM_RR: {
if (c.chan>=6) break;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -911,6 +964,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_D2R: { case DIV_CMD_FM_D2R: {
if (c.chan>=6) break;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -927,6 +981,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_DT: { case DIV_CMD_FM_DT: {
if (c.chan>=6) break;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -943,6 +998,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_SSG: { case DIV_CMD_FM_SSG: {
if (c.chan>=6) break;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -959,6 +1015,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_HARD_RESET: case DIV_CMD_FM_HARD_RESET:
if (c.chan>=6) break;
chan[c.chan].hardReset=c.value; chan[c.chan].hardReset=c.value;
break; break;
case DIV_ALWAYS_SET_VOLUME: case DIV_ALWAYS_SET_VOLUME:
@ -1007,7 +1064,7 @@ void DivPlatformGenesis::forceIns() {
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
} }
if (dacMode) { if (chan[5].dacMode) {
rWrite(0x2b,0x80); rWrite(0x2b,0x80);
} }
immWrite(0x22,lfoValue); immWrite(0x22,lfoValue);
@ -1057,18 +1114,18 @@ void DivPlatformGenesis::reset() {
} }
lastBusy=60; lastBusy=60;
dacMode=0;
dacPeriod=0;
dacPos=0;
dacRate=0;
dacDelay=0;
dacReady=true;
dacSample=-1;
sampleBank=0;
lfoValue=8; lfoValue=8;
softPCMTimer=0;
extMode=false; extMode=false;
if (softPCM) {
chan[5].dacMode=true;
chan[6].dacMode=true;
}
// normal sample direction
if (dumpWrites) addWrite(0xffff0003,0);
// LFO // LFO
immWrite(0x22,lfoValue); immWrite(0x22,lfoValue);
@ -1088,7 +1145,7 @@ bool DivPlatformGenesis::keyOffAffectsPorta(int ch) {
} }
void DivPlatformGenesis::notifyInsChange(int ins) { void DivPlatformGenesis::notifyInsChange(int ins) {
for (int i=0; i<6; i++) { for (int i=0; i<10; i++) {
if (chan[i].ins==ins) { if (chan[i].ins==ins) {
chan[i].insChanged=true; chan[i].insChanged=true;
} }
@ -1114,6 +1171,10 @@ void DivPlatformGenesis::setYMFM(bool use) {
useYMFM=use; useYMFM=use;
} }
void DivPlatformGenesis::setSoftPCM(bool value) {
softPCM=value;
}
void DivPlatformGenesis::setFlags(unsigned int flags) { void DivPlatformGenesis::setFlags(unsigned int flags) {
switch (flags) { switch (flags) {
case 1: chipClock=COLOR_PAL*12.0/7.0; break; case 1: chipClock=COLOR_PAL*12.0/7.0; break;
@ -1152,6 +1213,11 @@ int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, unsigned i
fm_ymfm=NULL; fm_ymfm=NULL;
setFlags(flags); setFlags(flags);
for (int i=0; i<128; i++) {
dacVolTable[127-i]=128*pow(10.0f,(float)(-i)*0.75f/20.0f);
}
dacVolTable[0]=0;
reset(); reset();
return 10; return 10;
} }

View file

@ -41,10 +41,24 @@ class DivPlatformGenesis: public DivDispatch {
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset;
int vol, outVol; int vol, outVol;
unsigned char pan; unsigned char pan;
bool dacMode;
int dacPeriod;
int dacRate;
unsigned int dacPos;
int dacSample;
int dacDelay;
bool dacReady;
bool dacDirection;
bool dacReversed;
unsigned char sampleBank;
void macroInit(DivInstrument* which) { void macroInit(DivInstrument* which) {
std.init(which); std.init(which);
pitch2=0; pitch2=0;
} }
bool getDacDirection() {
return dacReversed^dacDirection;
}
Channel(): Channel():
freqH(0), freqH(0),
freqL(0), freqL(0),
@ -65,7 +79,18 @@ class DivPlatformGenesis: public DivDispatch {
inPorta(false), inPorta(false),
hardReset(false), hardReset(false),
vol(0), vol(0),
pan(3) {} outVol(0),
pan(3),
dacMode(false),
dacPeriod(0),
dacRate(0),
dacPos(0),
dacSample(-1),
dacDelay(0),
dacReady(true),
dacDirection(false),
dacReversed(false),
sampleBank(0) {}
}; };
Channel chan[10]; Channel chan[10];
DivDispatchOscBuffer* oscBuf[10]; DivDispatchOscBuffer* oscBuf[10];
@ -86,24 +111,21 @@ class DivPlatformGenesis: public DivDispatch {
DivYM2612Interface iface; DivYM2612Interface iface;
unsigned char regPool[512]; unsigned char regPool[512];
bool dacMode;
int dacPeriod;
int dacRate;
unsigned int dacPos;
int dacSample;
int dacDelay;
bool dacReady;
unsigned char sampleBank;
unsigned char lfoValue; unsigned char lfoValue;
bool extMode, useYMFM; int softPCMTimer;
bool extMode, softPCM, useYMFM;
bool ladder; bool ladder;
short oldWrites[512]; short oldWrites[512];
short pendingWrites[512]; short pendingWrites[512];
unsigned char dacVolTable[128];
friend void putDispatchChan(void*,int,int); friend void putDispatchChan(void*,int,int);
inline void processDAC();
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len); void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
void acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len); void acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len);
@ -126,6 +148,7 @@ class DivPlatformGenesis: public DivDispatch {
void setFlags(unsigned int flags); void setFlags(unsigned int flags);
void notifyInsChange(int ins); void notifyInsChange(int ins);
void notifyInsDeletion(void* ins); void notifyInsDeletion(void* ins);
void setSoftPCM(bool value);
int getPortaFloor(int ch); int getPortaFloor(int ch);
void poke(unsigned int addr, unsigned short val); void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);

View file

@ -156,16 +156,16 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
case DIV_CMD_SAMPLE_MODE: { case DIV_CMD_SAMPLE_MODE: {
// not ignored actually! // not ignored actually!
if (!parent->song.ignoreDACModeOutsideIntendedChannel) { if (!parent->song.ignoreDACModeOutsideIntendedChannel) {
dacMode=c.value; chan[5].dacMode=c.value;
rWrite(0x2b,c.value<<7); rWrite(0x2b,c.value<<7);
} }
break; break;
} }
case DIV_CMD_SAMPLE_BANK: case DIV_CMD_SAMPLE_BANK:
if (!parent->song.ignoreDACModeOutsideIntendedChannel) { if (!parent->song.ignoreDACModeOutsideIntendedChannel) {
sampleBank=c.value; chan[5].sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) { if (chan[5].sampleBank>(parent->song.sample.size()/12)) {
sampleBank=parent->song.sample.size()/12; chan[5].sampleBank=parent->song.sample.size()/12;
} }
} }
break; break;
@ -484,7 +484,7 @@ void DivPlatformGenesisExt::forceIns() {
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
} }
if (dacMode) { if (chan[5].dacMode) {
rWrite(0x2b,0x80); rWrite(0x2b,0x80);
} }
immWrite(0x22,lfoValue); immWrite(0x22,lfoValue);

View file

@ -25,6 +25,7 @@
#define WRITE_VOLUME(ch,v) rWrite(0x20+(ch<<3),(v)) #define WRITE_VOLUME(ch,v) rWrite(0x20+(ch<<3),(v))
#define WRITE_FEEDBACK(ch,v) rWrite(0x21+(ch<<3),(v)) #define WRITE_FEEDBACK(ch,v) rWrite(0x21+(ch<<3),(v))
#define WRITE_OUTPUT(ch,v) rWrite(0x22+(ch<<3),(v))
#define WRITE_LFSR(ch,v) rWrite(0x23+(ch<<3),(v)) #define WRITE_LFSR(ch,v) rWrite(0x23+(ch<<3),(v))
#define WRITE_BACKUP(ch,v) rWrite(0x24+(ch<<3),(v)) #define WRITE_BACKUP(ch,v) rWrite(0x24+(ch<<3),(v))
#define WRITE_CONTROL(ch,v) rWrite(0x25+(ch<<3),(v)) #define WRITE_CONTROL(ch,v) rWrite(0x25+(ch<<3),(v))
@ -151,13 +152,18 @@ void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len
DivSample* s=parent->getSample(chan[i].sample); DivSample* s=parent->getSample(chan[i].sample);
if (s!=NULL) { if (s!=NULL) {
if (isMuted[i]) { if (isMuted[i]) {
WRITE_VOLUME(i,0); WRITE_OUTPUT(i,0);
chan[i].samplePos++; chan[i].samplePos++;
} else { } else {
WRITE_VOLUME(i,(s->data8[chan[i].samplePos++]*chan[i].outVol)>>7); WRITE_OUTPUT(i,(s->data8[chan[i].samplePos++]*chan[i].outVol)>>7);
} }
if (chan[i].samplePos>=(int)s->samples) { if (chan[i].samplePos>=(int)s->samples) {
chan[i].sample=-1; if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[i].samplePos=s->loopStart;
} else {
chan[i].sample=-1;
}
} }
} }
} }
@ -176,8 +182,8 @@ void DivPlatformLynx::tick(bool sysTick) {
chan[i].outVol=((chan[i].vol&127)*MIN(64,chan[i].std.vol.val))>>6; chan[i].outVol=((chan[i].vol&127)*MIN(64,chan[i].std.vol.val))>>6;
} else { } else {
chan[i].outVol=((chan[i].vol&127)*MIN(127,chan[i].std.vol.val))>>7; chan[i].outVol=((chan[i].vol&127)*MIN(127,chan[i].std.vol.val))>>7;
WRITE_VOLUME(i,(isMuted[i]?0:(chan[i].outVol&127)));
} }
WRITE_VOLUME(i,(isMuted[i]?0:(chan[i].outVol&127)));
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
@ -244,11 +250,6 @@ void DivPlatformLynx::tick(bool sysTick) {
} }
} }
chan[i].sampleFreq=off*parent->calcFreq(chan[i].sampleBaseFreq,chan[i].pitch,false,2,chan[i].pitch2,1,1); chan[i].sampleFreq=off*parent->calcFreq(chan[i].sampleBaseFreq,chan[i].pitch,false,2,chan[i].pitch2,1,1);
WRITE_FEEDBACK(i,0);
WRITE_LFSR(i,0);
WRITE_OTHER(i,0);
WRITE_CONTROL(i,0x18);
WRITE_BACKUP(i,2);
} else { } else {
if (chan[i].lfsr >= 0) { if (chan[i].lfsr >= 0) {
WRITE_LFSR(i, (chan[i].lfsr&0xff)); WRITE_LFSR(i, (chan[i].lfsr&0xff));
@ -300,7 +301,8 @@ int DivPlatformLynx::dispatch(DivCommand c) {
} }
case DIV_CMD_NOTE_OFF: case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false; chan[c.chan].active=false;
WRITE_VOLUME(c.chan, 0); WRITE_VOLUME(c.chan,0);
WRITE_CONTROL(c.chan,0);
chan[c.chan].macroInit(NULL); chan[c.chan].macroInit(NULL);
if (chan[c.chan].pcm) { if (chan[c.chan].pcm) {
chan[c.chan].pcm=false; chan[c.chan].pcm=false;

View file

@ -23,13 +23,18 @@
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#define rWrite(v) if (!skipRegisterWrites) {writes.emplace(0,v); if (dumpWrites) {addWrite(0,v);} } #define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
const char** DivPlatformMSM6295::getRegisterSheet() { const char** DivPlatformMSM6295::getRegisterSheet() {
return NULL; return NULL;
} }
const char* DivPlatformMSM6295::getEffectName(unsigned char effect) { const char* DivPlatformMSM6295::getEffectName(unsigned char effect) {
switch (effect) {
case 0x20:
return "20xx: Set chip output rate (0: clock/132; 1: clock/165)";
break;
}
return NULL; return NULL;
} }
@ -42,7 +47,28 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t
if (delay<=0) { if (delay<=0) {
if (!writes.empty()) { if (!writes.empty()) {
QueuedWrite& w=writes.front(); QueuedWrite& w=writes.front();
msm->command_w(w.val); switch (w.addr) {
case 0: // command
msm->command_w(w.val);
break;
case 8: // chip clock select (VGM)
case 9:
case 10:
case 11:
break;
case 12: // rate select
msm->ss_w(!w.val);
break;
case 14: // enable bankswitch
break;
case 15: // set bank base
break;
case 16: // switch bank
case 17:
case 18:
case 19:
break;
}
writes.pop(); writes.pop();
delay=32; delay=32;
} }
@ -92,9 +118,9 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
} }
chan[c.chan].active=true; chan[c.chan].active=true;
chan[c.chan].keyOn=true; chan[c.chan].keyOn=true;
rWrite((8<<c.chan)); // turn off rWrite(0,(8<<c.chan)); // turn off
rWrite(0x80|chan[c.chan].sample); // set phrase rWrite(0,0x80|chan[c.chan].sample); // set phrase
rWrite((16<<c.chan)|(8-chan[c.chan].outVol)); // turn on rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
} else { } else {
break; break;
} }
@ -107,9 +133,9 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
} }
//DivSample* s=parent->getSample(12*sampleBank+c.value%12); //DivSample* s=parent->getSample(12*sampleBank+c.value%12);
chan[c.chan].sample=12*sampleBank+c.value%12; chan[c.chan].sample=12*sampleBank+c.value%12;
rWrite((8<<c.chan)); // turn off rWrite(0,(8<<c.chan)); // turn off
rWrite(0x80|chan[c.chan].sample); // set phrase rWrite(0,0x80|chan[c.chan].sample); // set phrase
rWrite((16<<c.chan)|(8-chan[c.chan].outVol)); // turn on rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
} }
break; break;
} }
@ -117,14 +143,14 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
chan[c.chan].keyOff=true; chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false; chan[c.chan].keyOn=false;
chan[c.chan].active=false; chan[c.chan].active=false;
rWrite((8<<c.chan)); // turn off rWrite(0,(8<<c.chan)); // turn off
chan[c.chan].macroInit(NULL); chan[c.chan].macroInit(NULL);
break; break;
case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].keyOff=true; chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false; chan[c.chan].keyOn=false;
chan[c.chan].active=false; chan[c.chan].active=false;
rWrite((8<<c.chan)); // turn off rWrite(0,(8<<c.chan)); // turn off
chan[c.chan].std.release(); chan[c.chan].std.release();
break; break;
case DIV_CMD_ENV_RELEASE: case DIV_CMD_ENV_RELEASE:
@ -153,6 +179,10 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
case DIV_CMD_NOTE_PORTA: { case DIV_CMD_NOTE_PORTA: {
return 2; return 2;
} }
case DIV_CMD_SAMPLE_FREQ:
rateSel=c.value;
rWrite(12,!rateSel);
break;
case DIV_CMD_SAMPLE_BANK: case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value; sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) { if (sampleBank>(parent->song.sample.size()/12)) {
@ -190,6 +220,7 @@ void DivPlatformMSM6295::forceIns() {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i].insChanged=true; chan[i].insChanged=true;
} }
rWrite(12,!rateSel);
} }
void* DivPlatformMSM6295::getChanState(int ch) { void* DivPlatformMSM6295::getChanState(int ch) {
@ -219,6 +250,7 @@ void DivPlatformMSM6295::poke(std::vector<DivRegWrite>& wlist) {
void DivPlatformMSM6295::reset() { void DivPlatformMSM6295::reset() {
while (!writes.empty()) writes.pop(); while (!writes.empty()) writes.pop();
msm->reset(); msm->reset();
msm->ss_w(false);
if (dumpWrites) { if (dumpWrites) {
addWrite(0xffffffff,0); addWrite(0xffffffff,0);
} }
@ -232,6 +264,7 @@ void DivPlatformMSM6295::reset() {
} }
sampleBank=0; sampleBank=0;
rateSel=false;
delay=0; delay=0;
} }
@ -240,6 +273,10 @@ bool DivPlatformMSM6295::keyOffAffectsArp(int ch) {
return false; return false;
} }
float DivPlatformMSM6295::getPostAmp() {
return 3.0f;
}
void DivPlatformMSM6295::notifyInsChange(int ins) { void DivPlatformMSM6295::notifyInsChange(int ins) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
if (chan[i].ins==ins) { if (chan[i].ins==ins) {
@ -302,12 +339,21 @@ void DivPlatformMSM6295::renderSamples() {
} }
void DivPlatformMSM6295::setFlags(unsigned int flags) { void DivPlatformMSM6295::setFlags(unsigned int flags) {
if (flags&1) { switch (flags) {
chipClock=8448000; case 0:
} else { chipClock=4000000/4;
chipClock=8000000; break;
case 1:
chipClock=4224000/4;
break;
case 2:
chipClock=4000000;
break;
case 3:
chipClock=4224000;
break;
} }
rate=chipClock/((flags&2)?6:24); rate=chipClock/3;
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
isMuted[i]=false; isMuted[i]=false;
oscBuf[i]->rate=rate/22; oscBuf[i]->rate=rate/22;

View file

@ -101,6 +101,7 @@ class DivPlatformMSM6295: public DivDispatch {
int delay, updateOsc; int delay, updateOsc;
bool extMode; bool extMode;
bool rateSel;
short oldWrites[512]; short oldWrites[512];
short pendingWrites[512]; short pendingWrites[512];
@ -119,6 +120,7 @@ class DivPlatformMSM6295: public DivDispatch {
void tick(bool sysTick=true); void tick(bool sysTick=true);
void muteChannel(int ch, bool mute); void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch); bool keyOffAffectsArp(int ch);
float getPostAmp();
void notifyInsChange(int ins); void notifyInsChange(int ins);
void notifyInsDeletion(void* ins); void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val); void poke(unsigned int addr, unsigned short val);

View file

@ -735,6 +735,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
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);
immWrite(7,0x01); // reset
immWrite(9,(s->offB>>2)&0xff); immWrite(9,(s->offB>>2)&0xff);
immWrite(10,(s->offB>>10)&0xff); immWrite(10,(s->offB>>10)&0xff);
int end=s->offB+s->lengthB-1; int end=s->offB+s->lengthB-1;
@ -770,6 +771,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
} }
DivSample* s=parent->getSample(12*sampleBank+c.value%12); DivSample* s=parent->getSample(12*sampleBank+c.value%12);
immWrite(8,0); immWrite(8,0);
immWrite(7,0x01); // reset
immWrite(9,(s->offB>>2)&0xff); immWrite(9,(s->offB>>2)&0xff);
immWrite(10,(s->offB>>10)&0xff); immWrite(10,(s->offB>>10)&0xff);
int end=s->offB+s->lengthB-1; int end=s->offB+s->lengthB-1;

View file

@ -42,6 +42,7 @@ const char* DivPlatformSMS::getEffectName(unsigned char effect) {
} }
void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) { void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
int o=0;
for (size_t h=start; h<start+len; h++) { for (size_t h=start; h<start+len; h++) {
if (!writes.empty()) { if (!writes.empty()) {
unsigned char w=writes.front(); unsigned char w=writes.front();
@ -64,7 +65,10 @@ void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_
YMPSG_Clock(&sn_nuked); YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked); YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked); YMPSG_Clock(&sn_nuked);
bufL[h]=YMPSG_GetOutput(&sn_nuked)*8192.0; o=YMPSG_GetOutput(&sn_nuked);
if (o<-32768) o=-32768;
if (o>32767) o=32767;
bufL[h]=o;
/* /*
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
if (isMuted[i]) { if (isMuted[i]) {

View file

@ -63,6 +63,7 @@ const char* cmdName[]={
"SAMPLE_FREQ", "SAMPLE_FREQ",
"SAMPLE_BANK", "SAMPLE_BANK",
"SAMPLE_POS", "SAMPLE_POS",
"SAMPLE_DIR",
"SAMPLE_TRANSWAVE_SLICE_MODE", // (enabled) "SAMPLE_TRANSWAVE_SLICE_MODE", // (enabled)
"SAMPLE_TRANSWAVE_SLICE_POS", // (slice) "SAMPLE_TRANSWAVE_SLICE_POS", // (slice)
@ -570,6 +571,9 @@ void DivEngine::processRow(int i, bool afterDelay) {
clockDrift=0; clockDrift=0;
subticks=0; subticks=0;
break; break;
case 0xdf: // set sample direction
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_DIR,i,effectVal));
break;
case 0xe0: // arp speed case 0xe0: // arp speed
if (effectVal>0) { if (effectVal>0) {
curSubSong->arpLen=effectVal; curSubSong->arpLen=effectVal;

View file

@ -111,6 +111,9 @@ enum DivSystem {
DIV_SYSTEM_NAMCO, DIV_SYSTEM_NAMCO,
DIV_SYSTEM_NAMCO_15XX, DIV_SYSTEM_NAMCO_15XX,
DIV_SYSTEM_NAMCO_CUS30, DIV_SYSTEM_NAMCO_CUS30,
DIV_SYSTEM_YM2612_FRAC,
DIV_SYSTEM_YM2612_FRAC_EXT,
DIV_SYSTEM_RESERVED_8,
DIV_SYSTEM_DUMMY, DIV_SYSTEM_DUMMY,
DIV_SYSTEM_MAX // boundary for max system number DIV_SYSTEM_MAX // boundary for max system number
}; };
@ -395,6 +398,10 @@ struct DivSong {
bool snDutyReset; bool snDutyReset;
bool pitchMacroIsLinear; bool pitchMacroIsLinear;
bool oldOctaveBoundary; bool oldOctaveBoundary;
bool noOPN2Vol;
bool newVolumeScaling;
bool volMacroLinger;
bool brokenOutVol;
std::vector<DivInstrument*> ins; std::vector<DivInstrument*> ins;
std::vector<DivWavetable*> wave; std::vector<DivWavetable*> wave;
@ -488,7 +495,11 @@ struct DivSong {
fbPortaPause(false), fbPortaPause(false),
snDutyReset(false), snDutyReset(false),
pitchMacroIsLinear(true), pitchMacroIsLinear(true),
oldOctaveBoundary(false) { oldOctaveBoundary(false),
noOPN2Vol(false),
newVolumeScaling(true),
volMacroLinger(true),
brokenOutVol(false) {
for (int i=0; i<32; i++) { for (int i=0; i<32; i++) {
system[i]=DIV_SYSTEM_NULL; system[i]=DIV_SYSTEM_NULL;
systemVol[i]=64; systemVol[i]=64;

View file

@ -2027,12 +2027,23 @@ void DivEngine::registerSystems() {
); );
sysDefs[DIV_SYSTEM_MSM6295]=new DivSysDef( sysDefs[DIV_SYSTEM_MSM6295]=new DivSysDef(
"OKI MSM6295", NULL, 0xaa, 0, 4, false, true, 0, false, "OKI MSM6295", NULL, 0xaa, 0, 4, false, true, 0x161, false,
"an ADPCM sound chip manufactured by OKI and used in many arcade boards.", "an ADPCM sound chip manufactured by OKI and used in many arcade boards.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4"},
{"CH1", "CH2", "CH3", "CH4"}, {"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA},
{},
[this](int ch, unsigned char effect, unsigned char effectVal) -> bool {
switch (effect) {
case 0x20: // select rate
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal));
break;
default:
return false;
}
return true;
}
); );
sysDefs[DIV_SYSTEM_MSM6258]=new DivSysDef( sysDefs[DIV_SYSTEM_MSM6258]=new DivSysDef(
@ -2100,6 +2111,40 @@ void DivEngine::registerSystems() {
namcoEffectHandler namcoEffectHandler
); );
// replace with an 8-channel chip in a future
sysDefs[DIV_SYSTEM_RESERVED_8]=new DivSysDef(
"Reserved", NULL, 0xbc, 0, 8, false, true, 0, false,
"this was YM2612_FRAC, but due to changes this ID is reserved.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
{DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE},
{DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}
);
sysDefs[DIV_SYSTEM_YM2612_FRAC]=new DivSysDef(
"Yamaha YM2612 (OPN2) with DualPCM", NULL, 0xbe, 0, 7, true, false, 0, false,
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).\nthis system uses software mixing to provide two sample channels.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6/PCM 1", "PCM 2"},
{"F1", "F2", "F3", "F4", "F5", "P1", "P2"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM},
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AMIGA},
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_NULL},
opn2EffectHandler,
fmPostEffectHandler
);
sysDefs[DIV_SYSTEM_YM2612_FRAC_EXT]=new DivSysDef(
"Yamaha YM2612 (OPN2) Extended Channel 3 with DualPCM and CSM", NULL, 0xbd, 0, 11, true, false, 0, false,
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).\nthis system uses software mixing to provide two sample channels.\nthis one is in Extended Channel mode, which turns the second FM channel into four operators with independent notes/frequencies.",
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6/PCM 1", "PCM 2", "CSM Timer"},
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "P1", "P2", "CSM"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_NOISE},
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AMIGA, DIV_INS_FM},
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_NULL, DIV_INS_NULL},
opn2EffectHandler,
fmPostEffectHandler
);
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false,
"this is a system designed for testing purposes.", "this is a system designed for testing purposes.",

View file

@ -24,7 +24,7 @@
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0; constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool isSecond) { void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond) {
unsigned char baseAddr1=isSecond?0xa0:0x50; unsigned char baseAddr1=isSecond?0xa0:0x50;
unsigned char baseAddr2=isSecond?0x80:0; unsigned char baseAddr2=isSecond?0x80:0;
unsigned short baseAddr2S=isSecond?0x8000:0; unsigned short baseAddr2S=isSecond?0x8000:0;
@ -420,22 +420,22 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
case DIV_SYSTEM_Y8950_DRUMS: case DIV_SYSTEM_Y8950_DRUMS:
// disable envelope // disable envelope
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
w->writeC(0x0b|baseAddr1); w->writeC(0x0c|baseAddr1);
w->writeC(0x80+i); w->writeC(0x80+i);
w->writeC(0x0f); w->writeC(0x0f);
w->writeC(0x0b|baseAddr1); w->writeC(0x0c|baseAddr1);
w->writeC(0x88+i); w->writeC(0x88+i);
w->writeC(0x0f); w->writeC(0x0f);
w->writeC(0x0b|baseAddr1); w->writeC(0x0c|baseAddr1);
w->writeC(0x90+i); w->writeC(0x90+i);
w->writeC(0x0f); w->writeC(0x0f);
} }
// key off + freq reset // key off + freq reset
for (int i=0; i<9; i++) { for (int i=0; i<9; i++) {
w->writeC(0x0b|baseAddr1); w->writeC(0x0c|baseAddr1);
w->writeC(0xa0+i); w->writeC(0xa0+i);
w->writeC(0); w->writeC(0);
w->writeC(0x0b|baseAddr1); w->writeC(0x0c|baseAddr1);
w->writeC(0xb0+i); w->writeC(0xb0+i);
w->writeC(0); w->writeC(0);
} }
@ -522,6 +522,15 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(rf5c68Addr); w->writeC(rf5c68Addr);
w->writeC(8); w->writeC(8);
w->writeC(0xff); w->writeC(0xff);
break;
case DIV_SYSTEM_MSM6295:
w->writeC(0xb8); // disable all channels
w->writeC(baseAddr2|0);
w->writeC(0x78);
w->writeC(0xb8); // select rate
w->writeC(baseAddr2|12);
w->writeC(1);
break;
default: default:
break; break;
} }
@ -536,8 +545,8 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(0x95); w->writeC(0x95);
w->writeC(streamID); w->writeC(streamID);
w->writeS(write.val); // sample number w->writeS(write.val); // sample number
w->writeC((sample->loopStart==0)); // flags w->writeC((sample->loopStart==0)|(sampleDir[streamID]?0x10:0)); // flags
if (sample->loopStart>0) { if (sample->loopStart>0 && !sampleDir[streamID]) {
loopTimer[streamID]=(double)sample->loopEnd; loopTimer[streamID]=(double)sample->loopEnd;
loopSample[streamID]=write.val; loopSample[streamID]=write.val;
} }
@ -554,6 +563,9 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(streamID); w->writeC(streamID);
loopSample[streamID]=-1; loopSample[streamID]=-1;
break; break;
case 3: // set sample direction
sampleDir[streamID]=write.val;
break;
} }
return; return;
} }
@ -818,6 +830,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(write.addr&0xff); w->writeC(write.addr&0xff);
w->writeC(write.val); w->writeC(write.val);
break; break;
case DIV_SYSTEM_MSM6295:
w->writeC(0xb8);
w->writeC(baseAddr2|(write.addr&0x7f));
w->writeC(write.val);
break;
default: default:
logW("write not handled!"); logW("write not handled!");
break; break;
@ -923,11 +940,13 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
double loopTimer[DIV_MAX_CHANS]; double loopTimer[DIV_MAX_CHANS];
double loopFreq[DIV_MAX_CHANS]; double loopFreq[DIV_MAX_CHANS];
int loopSample[DIV_MAX_CHANS]; int loopSample[DIV_MAX_CHANS];
bool sampleDir[DIV_MAX_CHANS];
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
loopTimer[i]=0; loopTimer[i]=0;
loopFreq[i]=0; loopFreq[i]=0;
loopSample[i]=-1; loopSample[i]=-1;
sampleDir[i]=false;
} }
bool writeDACSamples=false; bool writeDACSamples=false;
@ -942,6 +961,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
DivDispatch* writeES5506[2]={NULL,NULL}; DivDispatch* writeES5506[2]={NULL,NULL};
DivDispatch* writeZ280[2]={NULL,NULL}; DivDispatch* writeZ280[2]={NULL,NULL};
DivDispatch* writeRF5C68[2]={NULL,NULL}; DivDispatch* writeRF5C68[2]={NULL,NULL};
DivDispatch* writeMSM6295[2]={NULL,NULL};
for (int i=0; i<song.systemLen; i++) { for (int i=0; i<song.systemLen; i++) {
willExport[i]=false; willExport[i]=false;
@ -1348,6 +1368,19 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
writeRF5C68[0]=disCont[i].dispatch; writeRF5C68[0]=disCont[i].dispatch;
} }
break; break;
case DIV_SYSTEM_MSM6295:
if (!hasOKIM6295) {
hasOKIM6295=disCont[i].dispatch->chipClock;
willExport[i]=true;
writeMSM6295[0]=disCont[i].dispatch;
} else if (!(hasOKIM6295&0x40000000)) {
isSecond[i]=true;
willExport[i]=true;
writeMSM6295[1]=disCont[i].dispatch;
hasOKIM6295|=0x40000000;
howManyChips++;
}
break;
default: default:
break; break;
} }
@ -1694,6 +1727,15 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
w->writeI(0); w->writeI(0);
w->write(writeRF5C68[i]->getSampleMem(),writeRF5C68[i]->getSampleMemUsage()); w->write(writeRF5C68[i]->getSampleMem(),writeRF5C68[i]->getSampleMemUsage());
} }
if (writeMSM6295[i]!=NULL && writeMSM6295[i]->getSampleMemUsage()>0) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x8b);
w->writeI((writeMSM6295[i]->getSampleMemUsage()+8)|(i*0x80000000));
w->writeI(writeMSM6295[i]->getSampleMemCapacity());
w->writeI(0);
w->write(writeMSM6295[i]->getSampleMem(),writeMSM6295[i]->getSampleMemUsage());
}
} }
// TODO // TODO
@ -1829,7 +1871,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
for (int i=0; i<song.systemLen; i++) { for (int i=0; i<song.systemLen; i++) {
std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites(); std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites();
for (DivRegWrite& j: writes) { for (DivRegWrite& j: writes) {
performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,isSecond[i]); performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i]);
writeCount++; writeCount++;
} }
writes.clear(); writes.clear();

View file

@ -186,7 +186,7 @@ bool DivWaveSynth::tick(bool skipSubDiv) {
break; break;
case DIV_WS_PHASE_MOD: case DIV_WS_PHASE_MOD:
for (int i=0; i<=state.speed; i++) { for (int i=0; i<=state.speed; i++) {
int mod=(wave2[pos]*(state.param2-stage)*width)/512; int mod=(wave2[pos]*(state.param2-stage)*width)/(64*(height+1));
output[pos]=wave1[(pos+mod)%width]; output[pos]=wave1[(pos+mod)%width];
if (++pos>=width) { if (++pos>=width) {
pos=0; pos=0;

View file

@ -31,26 +31,42 @@ void FurnaceGUI::drawChannels() {
if (!channelsOpen) return; if (!channelsOpen) return;
if (ImGui::Begin("Channels",&channelsOpen,globalWinFlags)) { if (ImGui::Begin("Channels",&channelsOpen,globalWinFlags)) {
if (ImGui::BeginTable("ChannelList",3)) { if (ImGui::BeginTable("ChannelList",3)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,0.0);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,48.0f*dpiScale); ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed,48.0f*dpiScale);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text("Visible");
ImGui::TableNextColumn();
ImGui::Text("Name");
for (int i=0; i<e->getTotalChannelCount(); i++) { for (int i=0; i<e->getTotalChannelCount(); i++) {
ImGui::PushID(i); ImGui::PushID(i);
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Checkbox("##Visible",&e->curSubSong->chanShow[i]); ImGui::Checkbox("##Visible",&e->curSubSong->chanShow[i]);
ImGui::SameLine(); ImGui::SameLine();
ImGui::BeginDisabled(i==0); if (ImGui::Button(ICON_FA_ARROWS)) {
if (ImGui::Button(ICON_FA_CHEVRON_UP)) {
e->swapChannelsP(i,i-1);
} }
ImGui::EndDisabled(); if (ImGui::BeginDragDropSource()) {
ImGui::SameLine(); chanToMove=i;
ImGui::BeginDisabled(i==(e->getTotalChannelCount()-1)); ImGui::SetDragDropPayload("FUR_CHAN",NULL,0,ImGuiCond_Once);
if (ImGui::Button(ICON_FA_CHEVRON_DOWN)) { ImGui::Button(ICON_FA_ARROWS "##ChanDrag");
e->swapChannelsP(i,i+1); ImGui::EndDragDropSource();
} else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s #%d\n(drag to swap channels)",e->getSystemName(e->sysOfChan[i]),e->dispatchChanOfChan[i]);
}
if (ImGui::BeginDragDropTarget()) {
const ImGuiPayload* dragItem=ImGui::AcceptDragDropPayload("FUR_CHAN");
if (dragItem!=NULL) {
if (dragItem->IsDataType("FUR_CHAN")) {
if (chanToMove!=i && chanToMove>=0) {
e->swapChannelsP(chanToMove,i);
}
chanToMove=-1;
}
}
ImGui::EndDragDropTarget();
} }
ImGui::EndDisabled();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->curSubSong->chanName[i]); ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->curSubSong->chanName[i]);

View file

@ -213,6 +213,10 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6"); ImGui::SetTooltip("behavior changed in 0.6");
} }
ImGui::Checkbox("No OPN2 DAC volume control",&e->song.noOPN2Vol);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6");
}
} }
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS;
ImGui::End(); ImGui::End();

View file

@ -394,7 +394,7 @@ void FurnaceGUI::drawSampleList() {
} }
void FurnaceGUI::actualWaveList() { void FurnaceGUI::actualWaveList() {
float wavePreview[256]; float wavePreview[257];
for (int i=0; i<(int)e->song.wave.size(); i++) { for (int i=0; i<(int)e->song.wave.size(); i++) {
DivWavetable* wave=e->song.wave[i]; DivWavetable* wave=e->song.wave[i];
for (int i=0; i<wave->len; i++) { for (int i=0; i<wave->len; i++) {

View file

@ -20,6 +20,7 @@
#include "gui.h" #include "gui.h"
#include "debug.h" #include "debug.h"
#include "IconsFontAwesome4.h" #include "IconsFontAwesome4.h"
#include <SDL_timer.h>
#include <fmt/printf.h> #include <fmt/printf.h>
#include <imgui.h> #include <imgui.h>
@ -373,6 +374,13 @@ void FurnaceGUI::drawDebug() {
} }
ImGui::TreePop(); ImGui::TreePop();
} }
if (ImGui::TreeNode("Performance")) {
double perfFreq=SDL_GetPerformanceFrequency()/1000000.0;
ImGui::Text("render: %.0fµs",(double)renderTimeDelta/perfFreq);
ImGui::Text("layout: %.0fµs",(double)layoutTimeDelta/perfFreq);
ImGui::Text("event: %.0fµs",(double)eventTimeDelta/perfFreq);
ImGui::TreePop();
}
if (ImGui::TreeNode("Settings")) { if (ImGui::TreeNode("Settings")) {
if (ImGui::Button("Sync")) syncSettings(); if (ImGui::Button("Sync")) syncSettings();
ImGui::SameLine(); ImGui::SameLine();

View file

@ -1233,6 +1233,22 @@ void FurnaceGUI::doAction(int what) {
} }
break; break;
} }
case GUI_ACTION_SAMPLE_SET_LOOP: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample];
sample->prepareUndo(true);
e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN;
sample->trim(0,end);
sample->loopStart=start;
updateSampleTex=true;
e->renderSamples();
});
MARK_MODIFIED;
break;
}
case GUI_ACTION_ORDERS_UP: case GUI_ACTION_ORDERS_UP:
if (curOrder>0) { if (curOrder>0) {

View file

@ -2368,6 +2368,10 @@ void FurnaceGUI::processPoint(SDL_Event& ev) {
point->x=ev.tfinger.x*scrW*dpiScale; point->x=ev.tfinger.x*scrW*dpiScale;
point->y=ev.tfinger.y*scrH*dpiScale; point->y=ev.tfinger.y*scrH*dpiScale;
point->z=ev.tfinger.pressure; point->z=ev.tfinger.pressure;
if (point->id==0) {
ImGui::GetIO().AddMousePosEvent(point->x,point->y);
}
} }
break; break;
} }
@ -2383,6 +2387,11 @@ void FurnaceGUI::processPoint(SDL_Event& ev) {
TouchPoint newPoint(ev.tfinger.fingerId,ev.tfinger.x*scrW*dpiScale,ev.tfinger.y*scrH*dpiScale,ev.tfinger.pressure); TouchPoint newPoint(ev.tfinger.fingerId,ev.tfinger.x*scrW*dpiScale,ev.tfinger.y*scrH*dpiScale,ev.tfinger.pressure);
activePoints.push_back(newPoint); activePoints.push_back(newPoint);
pressedPoints.push_back(newPoint); pressedPoints.push_back(newPoint);
if (newPoint.id==0) {
ImGui::GetIO().AddMousePosEvent(newPoint.x,newPoint.y);
ImGui::GetIO().AddMouseButtonEvent(ImGuiMouseButton_Left,true);
}
break; break;
} }
case SDL_FINGERUP: { case SDL_FINGERUP: {
@ -2391,6 +2400,11 @@ void FurnaceGUI::processPoint(SDL_Event& ev) {
if (point.id==ev.tfinger.fingerId) { if (point.id==ev.tfinger.fingerId) {
releasedPoints.push_back(point); releasedPoints.push_back(point);
activePoints.erase(activePoints.begin()+i); activePoints.erase(activePoints.begin()+i);
if (point.id==0) {
ImGui::GetIO().AddMouseButtonEvent(ImGuiMouseButton_Left,false);
ImGui::GetIO().AddMousePosEvent(-FLT_MAX,-FLT_MAX);
}
break; break;
} }
} }
@ -2411,6 +2425,7 @@ bool FurnaceGUI::loop() {
drawHalt=0; drawHalt=0;
if (settings.powerSave) SDL_WaitEventTimeout(NULL,500); if (settings.powerSave) SDL_WaitEventTimeout(NULL,500);
} }
eventTimeBegin=SDL_GetPerformanceCounter();
while (SDL_PollEvent(&ev)) { while (SDL_PollEvent(&ev)) {
WAKE_UP; WAKE_UP;
ImGui_ImplSDL2_ProcessEvent(&ev); ImGui_ImplSDL2_ProcessEvent(&ev);
@ -2529,7 +2544,23 @@ bool FurnaceGUI::loop() {
break; break;
case SDL_DROPFILE: case SDL_DROPFILE:
if (ev.drop.file!=NULL) { if (ev.drop.file!=NULL) {
if (modified) { std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file);
if (!instruments.empty()) {
if (!e->getWarnings().empty()) {
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
}
for (DivInstrument* i: instruments) {
e->addInstrumentPtr(i);
}
nextWindow=GUI_WINDOW_INS_LIST;
MARK_MODIFIED;
} else if (e->addWaveFromFile(ev.drop.file,false)) {
nextWindow=GUI_WINDOW_WAVE_LIST;
MARK_MODIFIED;
} else if (e->addSampleFromFile(ev.drop.file)!=-1) {
nextWindow=GUI_WINDOW_SAMPLE_LIST;
MARK_MODIFIED;
} else if (modified) {
nextFile=ev.drop.file; nextFile=ev.drop.file;
showWarning("Unsaved changes! Save changes before opening file?",GUI_WARN_OPEN_DROP); showWarning("Unsaved changes! Save changes before opening file?",GUI_WARN_OPEN_DROP);
} else { } else {
@ -2721,6 +2752,10 @@ bool FurnaceGUI::loop() {
midiLock.unlock(); midiLock.unlock();
} }
eventTimeEnd=SDL_GetPerformanceCounter();
layoutTimeBegin=SDL_GetPerformanceCounter();
ImGui_ImplSDLRenderer_NewFrame(); ImGui_ImplSDLRenderer_NewFrame();
ImGui_ImplSDL2_NewFrame(sdlWin); ImGui_ImplSDL2_NewFrame(sdlWin);
ImGui::NewFrame(); ImGui::NewFrame();
@ -3740,6 +3775,8 @@ bool FurnaceGUI::loop() {
ImGui::EndPopup(); ImGui::EndPopup();
} }
layoutTimeEnd=SDL_GetPerformanceCounter();
// backup trigger // backup trigger
if (modified) { if (modified) {
if (backupTimer>0) { if (backupTimer>0) {
@ -3778,10 +3815,16 @@ bool FurnaceGUI::loop() {
uiColors[GUI_COLOR_BACKGROUND].z*255, uiColors[GUI_COLOR_BACKGROUND].z*255,
uiColors[GUI_COLOR_BACKGROUND].w*255); uiColors[GUI_COLOR_BACKGROUND].w*255);
SDL_RenderClear(sdlRend); SDL_RenderClear(sdlRend);
renderTimeBegin=SDL_GetPerformanceCounter();
ImGui::Render(); ImGui::Render();
renderTimeEnd=SDL_GetPerformanceCounter();
ImGui_ImplSDLRenderer_RenderDrawData(ImGui::GetDrawData()); ImGui_ImplSDLRenderer_RenderDrawData(ImGui::GetDrawData());
SDL_RenderPresent(sdlRend); SDL_RenderPresent(sdlRend);
layoutTimeDelta=layoutTimeEnd-layoutTimeBegin;
renderTimeDelta=renderTimeEnd-renderTimeBegin;
eventTimeDelta=eventTimeEnd-eventTimeBegin;
if (--soloTimeout<0) soloTimeout=0; if (--soloTimeout<0) soloTimeout=0;
wheelX=0; wheelX=0;
@ -3867,6 +3910,16 @@ bool FurnaceGUI::init() {
if (orderEditMode<0) orderEditMode=0; if (orderEditMode<0) orderEditMode=0;
if (orderEditMode>3) orderEditMode=3; if (orderEditMode>3) orderEditMode=3;
pianoOctaves=e->getConfInt("pianoOctaves",pianoOctaves);
pianoOctavesEdit=e->getConfInt("pianoOctavesEdit",pianoOctavesEdit);
pianoOptions=e->getConfBool("pianoOptions",pianoOptions);
pianoSharePosition=e->getConfBool("pianoSharePosition",pianoSharePosition);
pianoOptionsSet=e->getConfBool("pianoOptionsSet",pianoOptionsSet);
pianoOffset=e->getConfInt("pianoOffset",pianoOffset);
pianoOffsetEdit=e->getConfInt("pianoOffsetEdit",pianoOffsetEdit);
pianoView=e->getConfInt("pianoView",pianoView);
pianoInputPadMode=e->getConfInt("pianoInputPadMode",pianoInputPadMode);
syncSettings(); syncSettings();
if (settings.dpiScale>=0.5f) { if (settings.dpiScale>=0.5f) {
@ -3887,8 +3940,11 @@ bool FurnaceGUI::init() {
SDL_Rect displaySize; SDL_Rect displaySize;
#endif #endif
SDL_SetHint("SDL_HINT_VIDEO_ALLOW_SCREENSAVER","1"); SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER,"1");
SDL_SetHint("SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH","1"); SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS,"0");
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS,"0");
// don't disable compositing on KWin
SDL_SetHint(SDL_HINT_X11_WINDOW_TYPE,"_NET_WM_WINDOW_TYPE_NORMAL");
SDL_Init(SDL_INIT_VIDEO); SDL_Init(SDL_INIT_VIDEO);
@ -4064,6 +4120,17 @@ bool FurnaceGUI::finish() {
e->setConf("followPattern",followPattern); e->setConf("followPattern",followPattern);
e->setConf("orderEditMode",orderEditMode); e->setConf("orderEditMode",orderEditMode);
// commit piano state
e->setConf("pianoOctaves",pianoOctaves);
e->setConf("pianoOctavesEdit",pianoOctavesEdit);
e->setConf("pianoOptions",pianoOptions);
e->setConf("pianoSharePosition",pianoSharePosition);
e->setConf("pianoOptionsSet",pianoOptionsSet);
e->setConf("pianoOffset",pianoOffset);
e->setConf("pianoOffsetEdit",pianoOffsetEdit);
e->setConf("pianoView",pianoView);
e->setConf("pianoInputPadMode",pianoInputPadMode);
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
delete oldPat[i]; delete oldPat[i];
} }
@ -4283,6 +4350,16 @@ FurnaceGUI::FurnaceGUI():
bindSetPending(false), bindSetPending(false),
nextScroll(-1.0f), nextScroll(-1.0f),
nextAddScroll(0.0f), nextAddScroll(0.0f),
layoutTimeBegin(0),
layoutTimeEnd(0),
layoutTimeDelta(0),
renderTimeBegin(0),
renderTimeEnd(0),
renderTimeDelta(0),
eventTimeBegin(0),
eventTimeEnd(0),
eventTimeDelta(0),
chanToMove(-1),
transposeAmount(0), transposeAmount(0),
randomizeMin(0), randomizeMin(0),
randomizeMax(255), randomizeMax(255),

View file

@ -491,6 +491,7 @@ enum FurnaceGUIActions {
GUI_ACTION_SAMPLE_ZOOM_OUT, GUI_ACTION_SAMPLE_ZOOM_OUT,
GUI_ACTION_SAMPLE_ZOOM_AUTO, GUI_ACTION_SAMPLE_ZOOM_AUTO,
GUI_ACTION_SAMPLE_MAKE_INS, GUI_ACTION_SAMPLE_MAKE_INS,
GUI_ACTION_SAMPLE_SET_LOOP,
GUI_ACTION_SAMPLE_MAX, GUI_ACTION_SAMPLE_MAX,
GUI_ACTION_ORDERS_MIN, GUI_ACTION_ORDERS_MIN,
@ -944,6 +945,11 @@ class FurnaceGUI {
int noMultiSystem; int noMultiSystem;
int oldMacroVSlider; int oldMacroVSlider;
int displayAllInsTypes; int displayAllInsTypes;
int noteCellSpacing;
int insCellSpacing;
int volCellSpacing;
int effectCellSpacing;
int effectValCellSpacing;
unsigned int maxUndoSteps; unsigned int maxUndoSteps;
String mainFontPath; String mainFontPath;
String patFontPath; String patFontPath;
@ -1037,6 +1043,11 @@ class FurnaceGUI {
noMultiSystem(0), noMultiSystem(0),
oldMacroVSlider(0), oldMacroVSlider(0),
displayAllInsTypes(0), displayAllInsTypes(0),
noteCellSpacing(0),
insCellSpacing(0),
volCellSpacing(0),
effectCellSpacing(0),
effectValCellSpacing(0),
maxUndoSteps(100), maxUndoSteps(100),
mainFontPath(""), mainFontPath(""),
patFontPath(""), patFontPath(""),
@ -1180,10 +1191,17 @@ class FurnaceGUI {
float nextScroll, nextAddScroll; float nextScroll, nextAddScroll;
int layoutTimeBegin, layoutTimeEnd, layoutTimeDelta;
int renderTimeBegin, renderTimeEnd, renderTimeDelta;
int eventTimeBegin, eventTimeEnd, eventTimeDelta;
int chanToMove;
ImVec2 patWindowPos, patWindowSize; ImVec2 patWindowPos, patWindowSize;
// pattern view specific // pattern view specific
ImVec2 fourChars, threeChars, twoChars; ImVec2 fourChars, threeChars, twoChars;
ImVec2 noteCellSize, insCellSize, volCellSize, effectCellSize, effectValCellSize;
SelectionPoint sel1, sel2; SelectionPoint sel1, sel2;
int dummyRows, demandX; int dummyRows, demandX;
int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax; int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax;

View file

@ -401,7 +401,7 @@ const FurnaceGUIColors fxColors[256]={
GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_MISC, // DF
// E0-FF extended effects // E0-FF extended effects
GUI_COLOR_PATTERN_EFFECT_MISC, // E0 GUI_COLOR_PATTERN_EFFECT_MISC, // E0
@ -638,6 +638,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("SAMPLE_ZOOM_OUT", "Zoom out", FURKMOD_CMD|SDLK_MINUS), D("SAMPLE_ZOOM_OUT", "Zoom out", FURKMOD_CMD|SDLK_MINUS),
D("SAMPLE_ZOOM_AUTO", "Toggle auto-zoom", FURKMOD_CMD|SDLK_0), D("SAMPLE_ZOOM_AUTO", "Toggle auto-zoom", FURKMOD_CMD|SDLK_0),
D("SAMPLE_MAKE_INS", "Create instrument from sample", 0), D("SAMPLE_MAKE_INS", "Create instrument from sample", 0),
D("SAMPLE_SET_LOOP", "Set loop to selection", FURKMOD_CMD|SDLK_l),
D("SAMPLE_MAX", "", NOT_AN_ACTION), D("SAMPLE_MAX", "", NOT_AN_ACTION),
D("ORDERS_MIN", "---Orders", NOT_AN_ACTION), D("ORDERS_MIN", "---Orders", NOT_AN_ACTION),
@ -832,6 +833,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
const int availableSystems[]={ const int availableSystems[]={
DIV_SYSTEM_YM2612, DIV_SYSTEM_YM2612,
DIV_SYSTEM_YM2612_EXT, DIV_SYSTEM_YM2612_EXT,
DIV_SYSTEM_YM2612_FRAC,
DIV_SYSTEM_YM2612_FRAC_EXT,
DIV_SYSTEM_SMS, DIV_SYSTEM_SMS,
DIV_SYSTEM_GB, DIV_SYSTEM_GB,
DIV_SYSTEM_PCE, DIV_SYSTEM_PCE,

View file

@ -1868,12 +1868,12 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
op.ar&=maxArDr; op.ar&=maxArDr;
CENTER_VSLIDER; CENTER_VSLIDER;
P(CWVSliderScalar("##AR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ar,&_ZERO,&maxArDr)); P(CWVSliderScalar("##AR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ar,&maxArDr,&_ZERO));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
op.dr&=maxArDr; op.dr&=maxArDr;
CENTER_VSLIDER; CENTER_VSLIDER;
P(CWVSliderScalar("##DR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.dr,&_ZERO,&maxArDr)); P(CWVSliderScalar("##DR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.dr,&maxArDr,&_ZERO));
if (settings.susPosition==0) { if (settings.susPosition==0) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -1886,13 +1886,13 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
op.d2r&=31; op.d2r&=31;
CENTER_VSLIDER; CENTER_VSLIDER;
P(CWVSliderScalar("##D2R",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.d2r,&_ZERO,&_THIRTY_ONE)); P(CWVSliderScalar("##D2R",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO));
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
op.rr&=15; op.rr&=15;
CENTER_VSLIDER; CENTER_VSLIDER;
P(CWVSliderScalar("##RR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.rr,&_ZERO,&_FIFTEEN)); P(CWVSliderScalar("##RR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO));
if (settings.susPosition==1) { if (settings.susPosition==1) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();

View file

@ -138,12 +138,12 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_CURSOR]); ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_CURSOR]);
ImGui::PushStyleColor(ImGuiCol_HeaderActive,uiColors[GUI_COLOR_PATTERN_CURSOR_ACTIVE]); ImGui::PushStyleColor(ImGuiCol_HeaderActive,uiColors[GUI_COLOR_PATTERN_CURSOR_ACTIVE]);
ImGui::PushStyleColor(ImGuiCol_HeaderHovered,uiColors[GUI_COLOR_PATTERN_CURSOR_HOVER]); ImGui::PushStyleColor(ImGuiCol_HeaderHovered,uiColors[GUI_COLOR_PATTERN_CURSOR_HOVER]);
ImGui::Selectable(id,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,threeChars); ImGui::Selectable(id,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,noteCellSize);
demandX=ImGui::GetCursorPosX(); demandX=ImGui::GetCursorPosX();
ImGui::PopStyleColor(3); ImGui::PopStyleColor(3);
} else { } else {
if (selectedNote) ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]); if (selectedNote) ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]);
ImGui::Selectable(id,isPushing || selectedNote,ImGuiSelectableFlags_NoPadWithHalfSpacing,threeChars); ImGui::Selectable(id,isPushing || selectedNote,ImGuiSelectableFlags_NoPadWithHalfSpacing,noteCellSize);
if (selectedNote) ImGui::PopStyleColor(); if (selectedNote) ImGui::PopStyleColor();
} }
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked()) {
@ -178,12 +178,12 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_CURSOR]); ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_CURSOR]);
ImGui::PushStyleColor(ImGuiCol_HeaderActive,uiColors[GUI_COLOR_PATTERN_CURSOR_ACTIVE]); ImGui::PushStyleColor(ImGuiCol_HeaderActive,uiColors[GUI_COLOR_PATTERN_CURSOR_ACTIVE]);
ImGui::PushStyleColor(ImGuiCol_HeaderHovered,uiColors[GUI_COLOR_PATTERN_CURSOR_HOVER]); ImGui::PushStyleColor(ImGuiCol_HeaderHovered,uiColors[GUI_COLOR_PATTERN_CURSOR_HOVER]);
ImGui::Selectable(id,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,twoChars); ImGui::Selectable(id,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,insCellSize);
demandX=ImGui::GetCursorPosX(); demandX=ImGui::GetCursorPosX();
ImGui::PopStyleColor(3); ImGui::PopStyleColor(3);
} else { } else {
if (selectedIns) ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]); if (selectedIns) ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]);
ImGui::Selectable(id,isPushing || selectedIns,ImGuiSelectableFlags_NoPadWithHalfSpacing,twoChars); ImGui::Selectable(id,isPushing || selectedIns,ImGuiSelectableFlags_NoPadWithHalfSpacing,insCellSize);
if (selectedIns) ImGui::PopStyleColor(); if (selectedIns) ImGui::PopStyleColor();
} }
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked()) {
@ -212,12 +212,12 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_CURSOR]); ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_CURSOR]);
ImGui::PushStyleColor(ImGuiCol_HeaderActive,uiColors[GUI_COLOR_PATTERN_CURSOR_ACTIVE]); ImGui::PushStyleColor(ImGuiCol_HeaderActive,uiColors[GUI_COLOR_PATTERN_CURSOR_ACTIVE]);
ImGui::PushStyleColor(ImGuiCol_HeaderHovered,uiColors[GUI_COLOR_PATTERN_CURSOR_HOVER]); ImGui::PushStyleColor(ImGuiCol_HeaderHovered,uiColors[GUI_COLOR_PATTERN_CURSOR_HOVER]);
ImGui::Selectable(id,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,twoChars); ImGui::Selectable(id,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,volCellSize);
demandX=ImGui::GetCursorPosX(); demandX=ImGui::GetCursorPosX();
ImGui::PopStyleColor(3); ImGui::PopStyleColor(3);
} else { } else {
if (selectedVol) ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]); if (selectedVol) ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]);
ImGui::Selectable(id,isPushing || selectedVol,ImGuiSelectableFlags_NoPadWithHalfSpacing,twoChars); ImGui::Selectable(id,isPushing || selectedVol,ImGuiSelectableFlags_NoPadWithHalfSpacing,volCellSize);
if (selectedVol) ImGui::PopStyleColor(); if (selectedVol) ImGui::PopStyleColor();
} }
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked()) {
@ -257,12 +257,12 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_CURSOR]); ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_CURSOR]);
ImGui::PushStyleColor(ImGuiCol_HeaderActive,uiColors[GUI_COLOR_PATTERN_CURSOR_ACTIVE]); ImGui::PushStyleColor(ImGuiCol_HeaderActive,uiColors[GUI_COLOR_PATTERN_CURSOR_ACTIVE]);
ImGui::PushStyleColor(ImGuiCol_HeaderHovered,uiColors[GUI_COLOR_PATTERN_CURSOR_HOVER]); ImGui::PushStyleColor(ImGuiCol_HeaderHovered,uiColors[GUI_COLOR_PATTERN_CURSOR_HOVER]);
ImGui::Selectable(id,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,twoChars); ImGui::Selectable(id,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,effectCellSize);
demandX=ImGui::GetCursorPosX(); demandX=ImGui::GetCursorPosX();
ImGui::PopStyleColor(3); ImGui::PopStyleColor(3);
} else { } else {
if (selectedEffect) ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]); if (selectedEffect) ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]);
ImGui::Selectable(id,isPushing || selectedEffect,ImGuiSelectableFlags_NoPadWithHalfSpacing,twoChars); ImGui::Selectable(id,isPushing || selectedEffect,ImGuiSelectableFlags_NoPadWithHalfSpacing,effectCellSize);
if (selectedEffect) ImGui::PopStyleColor(); if (selectedEffect) ImGui::PopStyleColor();
} }
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked()) {
@ -283,12 +283,12 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_CURSOR]); ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_CURSOR]);
ImGui::PushStyleColor(ImGuiCol_HeaderActive,uiColors[GUI_COLOR_PATTERN_CURSOR_ACTIVE]); ImGui::PushStyleColor(ImGuiCol_HeaderActive,uiColors[GUI_COLOR_PATTERN_CURSOR_ACTIVE]);
ImGui::PushStyleColor(ImGuiCol_HeaderHovered,uiColors[GUI_COLOR_PATTERN_CURSOR_HOVER]); ImGui::PushStyleColor(ImGuiCol_HeaderHovered,uiColors[GUI_COLOR_PATTERN_CURSOR_HOVER]);
ImGui::Selectable(id,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,twoChars); ImGui::Selectable(id,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,effectValCellSize);
demandX=ImGui::GetCursorPosX(); demandX=ImGui::GetCursorPosX();
ImGui::PopStyleColor(3); ImGui::PopStyleColor(3);
} else { } else {
if (selectedEffectVal) ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]); if (selectedEffectVal) ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]);
ImGui::Selectable(id,isPushing || selectedEffectVal,ImGuiSelectableFlags_NoPadWithHalfSpacing,twoChars); ImGui::Selectable(id,isPushing || selectedEffectVal,ImGuiSelectableFlags_NoPadWithHalfSpacing,effectValCellSize);
if (selectedEffectVal) ImGui::PopStyleColor(); if (selectedEffectVal) ImGui::PopStyleColor();
} }
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked()) {
@ -549,7 +549,20 @@ void FurnaceGUI::drawPattern() {
threeChars=ImVec2(oneCharSize*3.0f,lineHeight); threeChars=ImVec2(oneCharSize*3.0f,lineHeight);
twoChars=ImVec2(oneCharSize*2.0f,lineHeight); twoChars=ImVec2(oneCharSize*2.0f,lineHeight);
//ImVec2 oneChar=ImVec2(oneCharSize,lineHeight); //ImVec2 oneChar=ImVec2(oneCharSize,lineHeight);
noteCellSize=threeChars;
noteCellSize.x+=(float)settings.noteCellSpacing*dpiScale;
insCellSize=twoChars;
insCellSize.x+=(float)settings.insCellSpacing*dpiScale;
volCellSize=twoChars;
volCellSize.x+=(float)settings.volCellSpacing*dpiScale;
effectCellSize=twoChars;
effectCellSize.x+=(float)settings.effectCellSpacing*dpiScale;
effectValCellSize=twoChars;
effectValCellSize.x+=(float)settings.effectValCellSpacing*dpiScale;
dummyRows=(ImGui::GetWindowSize().y/lineHeight)/2; dummyRows=(ImGui::GetWindowSize().y/lineHeight)/2;
// オップナー2608 i owe you one more for this horrible code // オップナー2608 i owe you one more for this horrible code
// previous pattern // previous pattern
ImGui::BeginDisabled(); ImGui::BeginDisabled();

View file

@ -102,6 +102,18 @@ void FurnaceGUI::initSystemPresets() {
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2612 (OPN2) with DualPCM", {
DIV_SYSTEM_YM2612, 64, 0, (int)0x80000000,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2612 (extended channel 3) with DualPCM", {
DIV_SYSTEM_YM2612_EXT, 64, 0, (int)0x80000000,
0
}
));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2413 (OPLL)", { "Yamaha YM2413 (OPLL)", {
DIV_SYSTEM_OPLL, 64, 0, 0, DIV_SYSTEM_OPLL, 64, 0, 0,
@ -132,6 +144,18 @@ void FurnaceGUI::initSystemPresets() {
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3438 (OPN2C) with DualPCM", {
DIV_SYSTEM_YM2612_FRAC, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3438 (extended channel 3) with DualPCM", {
DIV_SYSTEM_YM2612_FRAC_EXT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3526 (OPL)", { "Yamaha YM3526 (OPL)", {
DIV_SYSTEM_OPL, 64, 0, 0, DIV_SYSTEM_OPL, 64, 0, 0,
@ -435,6 +459,20 @@ void FurnaceGUI::initSystemPresets() {
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Genesis (Fractal Sound template)", {
DIV_SYSTEM_YM2612_FRAC, 64, 0, 0,
DIV_SYSTEM_SMS, 24, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Genesis (Fractal Sound template, extended channel 3)", {
DIV_SYSTEM_YM2612_FRAC_EXT, 64, 0, 0,
DIV_SYSTEM_SMS, 24, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Sega Genesis (with Sega CD)", { "Sega Genesis (with Sega CD)", {
DIV_SYSTEM_YM2612, 64, 0, 0, DIV_SYSTEM_YM2612, 64, 0, 0,

View file

@ -1312,6 +1312,10 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::MenuItem("select all",BIND_FOR(GUI_ACTION_SAMPLE_SELECT_ALL))) { if (ImGui::MenuItem("select all",BIND_FOR(GUI_ACTION_SAMPLE_SELECT_ALL))) {
doAction(GUI_ACTION_SAMPLE_SELECT_ALL); doAction(GUI_ACTION_SAMPLE_SELECT_ALL);
} }
ImGui::Separator();
if (ImGui::MenuItem("set loop to selection",BIND_FOR(GUI_ACTION_SAMPLE_SET_LOOP))) {
doAction(GUI_ACTION_SAMPLE_SET_LOOP);
}
ImGui::EndPopup(); ImGui::EndPopup();
} }

View file

@ -1240,6 +1240,35 @@ void FurnaceGUI::drawSettings() {
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Pattern view spacing after:");
if (CWSliderInt("Note",&settings.noteCellSpacing,0,32)) {
if (settings.noteCellSpacing<0) settings.noteCellSpacing=0;
if (settings.noteCellSpacing>32) settings.noteCellSpacing=32;
}
if (CWSliderInt("Instrument",&settings.insCellSpacing,0,32)) {
if (settings.insCellSpacing<0) settings.insCellSpacing=0;
if (settings.insCellSpacing>32) settings.insCellSpacing=32;
}
if (CWSliderInt("Volume",&settings.volCellSpacing,0,32)) {
if (settings.volCellSpacing<0) settings.volCellSpacing=0;
if (settings.volCellSpacing>32) settings.volCellSpacing=32;
}
if (CWSliderInt("Effect",&settings.effectCellSpacing,0,32)) {
if (settings.effectCellSpacing<0) settings.effectCellSpacing=0;
if (settings.effectCellSpacing>32) settings.effectCellSpacing=32;
}
if (CWSliderInt("Effect value",&settings.effectValCellSpacing,0,32)) {
if (settings.effectValCellSpacing<0) settings.effectValCellSpacing=0;
if (settings.effectValCellSpacing>32) settings.effectValCellSpacing=32;
}
ImGui::Separator();
if (ImGui::TreeNode("Color scheme")) { if (ImGui::TreeNode("Color scheme")) {
if (ImGui::Button("Import")) { if (ImGui::Button("Import")) {
openFileDialog(GUI_FILE_IMPORT_COLORS); openFileDialog(GUI_FILE_IMPORT_COLORS);
@ -1806,6 +1835,7 @@ void FurnaceGUI::drawSettings() {
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_OUT); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_OUT);
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_AUTO); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_AUTO);
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_MAKE_INS); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_MAKE_INS);
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SET_LOOP);
KEYBIND_CONFIG_END; KEYBIND_CONFIG_END;
ImGui::TreePop(); ImGui::TreePop();
@ -1975,6 +2005,11 @@ void FurnaceGUI::syncSettings() {
settings.noMultiSystem=e->getConfInt("noMultiSystem",0); settings.noMultiSystem=e->getConfInt("noMultiSystem",0);
settings.oldMacroVSlider=e->getConfInt("oldMacroVSlider",0); settings.oldMacroVSlider=e->getConfInt("oldMacroVSlider",0);
settings.displayAllInsTypes=e->getConfInt("displayAllInsTypes",0); settings.displayAllInsTypes=e->getConfInt("displayAllInsTypes",0);
settings.noteCellSpacing=e->getConfInt("noteCellSpacing",0);
settings.insCellSpacing=e->getConfInt("insCellSpacing",0);
settings.volCellSpacing=e->getConfInt("volCellSpacing",0);
settings.effectCellSpacing=e->getConfInt("effectCellSpacing",0);
settings.effectValCellSpacing=e->getConfInt("effectValCellSpacing",0);
clampSetting(settings.mainFontSize,2,96); clampSetting(settings.mainFontSize,2,96);
clampSetting(settings.patFontSize,2,96); clampSetting(settings.patFontSize,2,96);
@ -2052,6 +2087,11 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.noMultiSystem,0,1); clampSetting(settings.noMultiSystem,0,1);
clampSetting(settings.oldMacroVSlider,0,1); clampSetting(settings.oldMacroVSlider,0,1);
clampSetting(settings.displayAllInsTypes,0,1); clampSetting(settings.displayAllInsTypes,0,1);
clampSetting(settings.noteCellSpacing,0,32);
clampSetting(settings.insCellSpacing,0,32);
clampSetting(settings.volCellSpacing,0,32);
clampSetting(settings.effectCellSpacing,0,32);
clampSetting(settings.effectValCellSpacing,0,32);
settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys","")); settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys",""));
if (settings.initialSys.size()<4) { if (settings.initialSys.size()<4) {
@ -2178,6 +2218,11 @@ void FurnaceGUI::commitSettings() {
e->setConf("noMultiSystem",settings.noMultiSystem); e->setConf("noMultiSystem",settings.noMultiSystem);
e->setConf("oldMacroVSlider",settings.oldMacroVSlider); e->setConf("oldMacroVSlider",settings.oldMacroVSlider);
e->setConf("displayAllInsTypes",settings.displayAllInsTypes); e->setConf("displayAllInsTypes",settings.displayAllInsTypes);
e->setConf("noteCellSpacing",settings.noteCellSpacing);
e->setConf("insCellSpacing",settings.insCellSpacing);
e->setConf("volCellSpacing",settings.volCellSpacing);
e->setConf("effectCellSpacing",settings.effectCellSpacing);
e->setConf("effectValCellSpacing",settings.effectValCellSpacing);
// colors // colors
for (int i=0; i<GUI_COLOR_MAX; i++) { for (int i=0; i<GUI_COLOR_MAX; i++) {