mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-16 01:35:07 +00:00
Merge branch 'master' of https://github.com/tildearrow/furnace into mod-import
This commit is contained in:
commit
c7fb5df206
83 changed files with 7113 additions and 372 deletions
|
@ -234,6 +234,8 @@ src/engine/platform/sound/gb/apu.c
|
||||||
src/engine/platform/sound/gb/timing.c
|
src/engine/platform/sound/gb/timing.c
|
||||||
src/engine/platform/sound/pce_psg.cpp
|
src/engine/platform/sound/pce_psg.cpp
|
||||||
src/engine/platform/sound/nes/apu.c
|
src/engine/platform/sound/nes/apu.c
|
||||||
|
src/engine/platform/sound/vera_psg.c
|
||||||
|
src/engine/platform/sound/vera_pcm.c
|
||||||
|
|
||||||
src/engine/platform/sound/c64/sid.cc
|
src/engine/platform/sound/c64/sid.cc
|
||||||
src/engine/platform/sound/c64/voice.cc
|
src/engine/platform/sound/c64/voice.cc
|
||||||
|
@ -265,6 +267,8 @@ src/engine/platform/sound/lynx/Mikey.cpp
|
||||||
|
|
||||||
src/engine/platform/sound/qsound.c
|
src/engine/platform/sound/qsound.c
|
||||||
|
|
||||||
|
src/engine/platform/sound/x1_010/x1_010.cpp
|
||||||
|
|
||||||
src/engine/platform/sound/swan.cpp
|
src/engine/platform/sound/swan.cpp
|
||||||
|
|
||||||
src/engine/platform/ym2610Interface.cpp
|
src/engine/platform/ym2610Interface.cpp
|
||||||
|
@ -308,8 +312,10 @@ src/engine/platform/amiga.cpp
|
||||||
src/engine/platform/pcspkr.cpp
|
src/engine/platform/pcspkr.cpp
|
||||||
src/engine/platform/segapcm.cpp
|
src/engine/platform/segapcm.cpp
|
||||||
src/engine/platform/qsound.cpp
|
src/engine/platform/qsound.cpp
|
||||||
|
src/engine/platform/x1_010.cpp
|
||||||
src/engine/platform/lynx.cpp
|
src/engine/platform/lynx.cpp
|
||||||
src/engine/platform/swan.cpp
|
src/engine/platform/swan.cpp
|
||||||
|
src/engine/platform/vera.cpp
|
||||||
src/engine/platform/dummy.cpp
|
src/engine/platform/dummy.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -341,6 +347,7 @@ src/gui/font_unifont.cpp
|
||||||
src/gui/font_icon.cpp
|
src/gui/font_icon.cpp
|
||||||
src/gui/fonts.cpp
|
src/gui/fonts.cpp
|
||||||
src/gui/debug.cpp
|
src/gui/debug.cpp
|
||||||
|
src/gui/fileDialog.cpp
|
||||||
|
|
||||||
src/gui/intConst.cpp
|
src/gui/intConst.cpp
|
||||||
src/gui/guiConst.cpp
|
src/gui/guiConst.cpp
|
||||||
|
@ -392,6 +399,9 @@ endif()
|
||||||
|
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
set(WARNING_FLAGS -Wall -Wextra -Wno-unused-parameter)
|
set(WARNING_FLAGS -Wall -Wextra -Wno-unused-parameter)
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
|
list(APPEND WARNING_FLAGS -Wno-cast-function-type)
|
||||||
|
endif()
|
||||||
if (WARNINGS_ARE_ERRORS)
|
if (WARNINGS_ARE_ERRORS)
|
||||||
list(APPEND WARNING_FLAGS -Werror)
|
list(APPEND WARNING_FLAGS -Werror)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -68,6 +68,8 @@ some people have provided packages for Unix/Unix-like distributions. here's a li
|
||||||
|
|
||||||
# developer info
|
# developer info
|
||||||
|
|
||||||
|
[![Build furnace](https://github.com/tildearrow/furnace/actions/workflows/build.yml/badge.svg)](https://github.com/tildearrow/furnace/actions/workflows/build.yml)
|
||||||
|
|
||||||
**NOTE: do not download the project's source as a .zip or .tar.gz as these do not include the project's submodules which are necessary to proceed with building.**
|
**NOTE: do not download the project's source as a .zip or .tar.gz as these do not include the project's submodules which are necessary to proceed with building.**
|
||||||
|
|
||||||
## dependencies
|
## dependencies
|
||||||
|
|
2
extern/igfd/ImGuiFileDialog.cpp
vendored
2
extern/igfd/ImGuiFileDialog.cpp
vendored
|
@ -3877,6 +3877,7 @@ namespace IGFD
|
||||||
static ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_AllowDoubleClick |
|
static ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_AllowDoubleClick |
|
||||||
ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SpanAvailWidth;
|
ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SpanAvailWidth;
|
||||||
|
|
||||||
|
// TODO BUG?!
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, vFmt);
|
va_start(args, vFmt);
|
||||||
vsnprintf(fdi.puVariadicBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFmt, args);
|
vsnprintf(fdi.puVariadicBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFmt, args);
|
||||||
|
@ -4074,6 +4075,7 @@ namespace IGFD
|
||||||
|
|
||||||
if (ImGui::TableNextColumn()) // file name
|
if (ImGui::TableNextColumn()) // file name
|
||||||
{
|
{
|
||||||
|
// TODO BUG?!?!?!
|
||||||
needToBreakTheloop = prSelectableItem(i, infos, selected, _str.c_str());
|
needToBreakTheloop = prSelectableItem(i, infos, selected, _str.c_str());
|
||||||
if (needToBreakTheloop==2) escape=true;
|
if (needToBreakTheloop==2) escape=true;
|
||||||
}
|
}
|
||||||
|
|
4
extern/pfd-fixed/.gitignore
vendored
Normal file
4
extern/pfd-fixed/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
Makefile
|
||||||
|
cmake_install.cmake
|
5
extern/pfd-fixed/.lgtm.yml
vendored
Normal file
5
extern/pfd-fixed/.lgtm.yml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
extraction:
|
||||||
|
cpp:
|
||||||
|
index:
|
||||||
|
build_command:
|
||||||
|
- make -C examples
|
6
extern/pfd-fixed/CMakeLists.txt
vendored
Normal file
6
extern/pfd-fixed/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
cmake_minimum_required(VERSION 3.1.0)
|
||||||
|
|
||||||
|
project(portable_file_dialogs VERSION 1.00 LANGUAGES CXX)
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} INTERFACE)
|
||||||
|
target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
14
extern/pfd-fixed/COPYING
vendored
Normal file
14
extern/pfd-fixed/COPYING
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
Version 2, December 2004
|
||||||
|
|
||||||
|
Copyright (C) 2004 Sam Hocevar
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
copies of this license document, and changing it is allowed as long
|
||||||
|
as the name is changed.
|
||||||
|
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
64
extern/pfd-fixed/README.md
vendored
Normal file
64
extern/pfd-fixed/README.md
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
# Portable File Dialogs
|
||||||
|
|
||||||
|
A free C++11 file dialog library.
|
||||||
|
|
||||||
|
- works on Windows, Mac OS X, Linux
|
||||||
|
- **single-header**, no extra library dependencies
|
||||||
|
- **synchronous *or* asynchronous** (does not block the rest of your program!)
|
||||||
|
- **cancelable** (kill asynchronous dialogues without user interaction)
|
||||||
|
- **secure** (immune to shell-quote vulnerabilities)
|
||||||
|
|
||||||
|
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/a25d3fd6959a4333871f630ac70b6e09)](https://www.codacy.com/manual/samhocevar/portable-file-dialogs?utm_source=github.com&utm_medium=referral&utm_content=samhocevar/portable-file-dialogs&utm_campaign=Badge_Grade)
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
The library is now pretty robust. It is not as feature-complete as
|
||||||
|
[Tiny File Dialogs](https://sourceforge.net/projects/tinyfiledialogs/),
|
||||||
|
but has asynchonous dialogs, more maintainable code, and fewer potential
|
||||||
|
security issues.
|
||||||
|
|
||||||
|
The currently available backends are:
|
||||||
|
|
||||||
|
- Win32 API (all known versions of Windows)
|
||||||
|
- Mac OS X (using AppleScript)
|
||||||
|
- GNOME desktop (using [Zenity](https://en.wikipedia.org/wiki/Zenity) or its clones Matedialog and Qarma)
|
||||||
|
- KDE desktop (using [KDialog](https://github.com/KDE/kdialog))
|
||||||
|
|
||||||
|
Experimental support for Emscripten is on its way.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [`pfd`](doc/pfd.md) general documentation
|
||||||
|
- [`pfd::message`](doc/message.md) message box
|
||||||
|
- [`pfd::notify`](doc/notify.md) notification
|
||||||
|
- [`pfd::open_file`](doc/open_file.md) file open
|
||||||
|
- [`pfd::save_file`](doc/save_file.md) file save
|
||||||
|
- [`pfd::select_folder`](doc/select_folder.md) folder selection
|
||||||
|
|
||||||
|
## History
|
||||||
|
|
||||||
|
- 0.1.0 (July 16, 2020): first public release
|
||||||
|
|
||||||
|
## Screenshots (Windows 10)
|
||||||
|
|
||||||
|
![warning-win32](https://user-images.githubusercontent.com/245089/47136607-76919a00-d2b4-11e8-8f42-e2d62c4f9570.png)
|
||||||
|
![notify-win32](https://user-images.githubusercontent.com/245089/47142453-2ff76c00-d2c3-11e8-871a-1a110ac91eb2.png)
|
||||||
|
![open-win32](https://user-images.githubusercontent.com/245089/47155865-0f8cd900-d2e6-11e8-8041-1e20b6f77dee.png)
|
||||||
|
|
||||||
|
## Screenshots (Mac OS X, dark theme)
|
||||||
|
|
||||||
|
![warning-osxdark](https://user-images.githubusercontent.com/245089/56053001-22dba700-5d53-11e9-8233-ca7a2c58188d.png)
|
||||||
|
![notify-osxdark](https://user-images.githubusercontent.com/245089/56053188-bc0abd80-5d53-11e9-8298-68aa96315c6c.png)
|
||||||
|
![open-osxdark](https://user-images.githubusercontent.com/245089/56053378-39363280-5d54-11e9-9583-9f1c978fa0db.png)
|
||||||
|
|
||||||
|
## Screenshots (Linux, GNOME desktop)
|
||||||
|
|
||||||
|
![warning-gnome](https://user-images.githubusercontent.com/245089/47136608-772a3080-d2b4-11e8-9e1d-60a7e743e908.png)
|
||||||
|
![notify-gnome](https://user-images.githubusercontent.com/245089/47142455-30900280-d2c3-11e8-8b76-ea16c7e502d4.png)
|
||||||
|
![open-gnome](https://user-images.githubusercontent.com/245089/47155867-0f8cd900-d2e6-11e8-93af-275636491ec4.png)
|
||||||
|
|
||||||
|
## Screenshots (Linux, KDE Plasma desktop)
|
||||||
|
|
||||||
|
![warning-kde](https://user-images.githubusercontent.com/245089/47149255-4dcccd00-d2d3-11e8-84c9-f85612784680.png)
|
||||||
|
![notify-kde](https://user-images.githubusercontent.com/245089/47149206-27a72d00-d2d3-11e8-8f1b-96e462f08c2b.png)
|
||||||
|
![open-kde](https://user-images.githubusercontent.com/245089/47155866-0f8cd900-d2e6-11e8-8006-f14b948afc55.png)
|
97
extern/pfd-fixed/doc/message.md
vendored
Normal file
97
extern/pfd-fixed/doc/message.md
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
## Message Box API
|
||||||
|
|
||||||
|
Displaying a message box is done using the `pfd::message` class. It can be provided a title, a
|
||||||
|
message text, a `choice` representing which buttons need to be rendered, and an `icon` for the
|
||||||
|
message:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
pfd::message::message(std::string const &title,
|
||||||
|
std::string const &text,
|
||||||
|
pfd::choice choice = pfd::choice::ok_cancel,
|
||||||
|
pfd::icon icon = pfd::icon::info);
|
||||||
|
|
||||||
|
enum class pfd::choice { ok, ok_cancel, yes_no, yes_no_cancel };
|
||||||
|
|
||||||
|
enum class pfd::icon { info, warning, error, question };
|
||||||
|
```
|
||||||
|
|
||||||
|
The pressed button is queried using `pfd::message::result()`. If the dialog box is closed by any
|
||||||
|
other means, the `pfd::button::cancel` is assumed:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
pfd::button pfd::message::result();
|
||||||
|
|
||||||
|
enum class pfd::button { ok, cancel, yes, no };
|
||||||
|
```
|
||||||
|
|
||||||
|
It is possible to ask the dialog box whether the user took action using the `pfd::message::ready()`
|
||||||
|
method, with an optional `timeout` argument. If the user did not press a button within `timeout`
|
||||||
|
milliseconds, the function will return `false`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool pfd::message::ready(int timeout = pfd::default_wait_timeout);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 1: simple notification
|
||||||
|
|
||||||
|
The `pfd::message` destructor waits for user action, so this operation will block until the user
|
||||||
|
closes the message box:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
pfd::message("Problem", "An error occurred while doing things",
|
||||||
|
pfd::choice::ok, pfd::icon::error);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 2: retrieving the pressed button
|
||||||
|
|
||||||
|
Using `pfd::message::result()` will also wait for user action before returning. This operation will block and return the user choice:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Ask for user opinion
|
||||||
|
auto button = pfd::message("Action requested", "Do you want to proceed with things?",
|
||||||
|
pfd::choice::yes_no, pfd::icon::question).result();
|
||||||
|
// Do something with button…
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 3: asynchronous message box
|
||||||
|
|
||||||
|
Using `pfd::message::ready()` allows the application to perform other tasks while waiting for
|
||||||
|
user input:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Message box with nice message
|
||||||
|
auto box = pfd::message("Unsaved Files", "Do you want to save the current "
|
||||||
|
"document before closing the application?",
|
||||||
|
pfd::choice::yes_no_cancel,
|
||||||
|
pfd::icon::warning);
|
||||||
|
|
||||||
|
// Do something while waiting for user input
|
||||||
|
while (!box.ready(1000))
|
||||||
|
std::cout << "Waited 1 second for user input...\n";
|
||||||
|
|
||||||
|
// Act depending on the selected button
|
||||||
|
switch (box.result())
|
||||||
|
{
|
||||||
|
case pfd::button::yes: std::cout << "User agreed.\n"; break;
|
||||||
|
case pfd::button::no: std::cout << "User disagreed.\n"; break;
|
||||||
|
case pfd::button::cancel: std::cout << "User freaked out.\n"; break;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
#### Windows 10
|
||||||
|
|
||||||
|
![warning-win32](https://user-images.githubusercontent.com/245089/47136607-76919a00-d2b4-11e8-8f42-e2d62c4f9570.png)
|
||||||
|
|
||||||
|
#### Mac OS X
|
||||||
|
|
||||||
|
![warning-osx-dark](https://user-images.githubusercontent.com/245089/56053001-22dba700-5d53-11e9-8233-ca7a2c58188d.png) ![warning-osx-light](https://user-images.githubusercontent.com/245089/56053055-49014700-5d53-11e9-8306-e9a03a25e044.png)
|
||||||
|
|
||||||
|
#### Linux (GNOME desktop)
|
||||||
|
|
||||||
|
![warning-gnome](https://user-images.githubusercontent.com/245089/47140824-8662ab80-d2bf-11e8-9c87-2742dd5b58af.png)
|
||||||
|
|
||||||
|
#### Linux (KDE desktop)
|
||||||
|
|
||||||
|
![warning-kde](https://user-images.githubusercontent.com/245089/47149255-4dcccd00-d2d3-11e8-84c9-f85612784680.png)
|
40
extern/pfd-fixed/doc/notify.md
vendored
Normal file
40
extern/pfd-fixed/doc/notify.md
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
## Notification API
|
||||||
|
|
||||||
|
Displaying a desktop notification is done using the `pfd::notify` class. It can be provided a
|
||||||
|
title, a message text, and an `icon` for the notification style:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
pfd::notify::notify(std::string const &title,
|
||||||
|
std::string const &text,
|
||||||
|
pfd::icon icon = pfd::icon::info);
|
||||||
|
|
||||||
|
enum class pfd::icon { info, warning, error };
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Displaying a notification is straightforward. Emoji are supported:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
pfd::notify("System event", "Something might be on fire 🔥",
|
||||||
|
pfd::icon::warning);
|
||||||
|
```
|
||||||
|
|
||||||
|
The `pfd::notify` object needs not be kept around, letting the object clean up itself is enough.
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
Windows 10:
|
||||||
|
![notify-win32](https://user-images.githubusercontent.com/245089/47142453-2ff76c00-d2c3-11e8-871a-1a110ac91eb2.png)
|
||||||
|
|
||||||
|
Mac OS X (dark theme):
|
||||||
|
![image](https://user-images.githubusercontent.com/245089/56053188-bc0abd80-5d53-11e9-8298-68aa96315c6c.png)
|
||||||
|
|
||||||
|
Mac OS X (light theme):
|
||||||
|
![image](https://user-images.githubusercontent.com/245089/56053137-92ea2d00-5d53-11e9-8cf2-049486c45713.png)
|
||||||
|
|
||||||
|
Linux (GNOME desktop):
|
||||||
|
![notify-gnome](https://user-images.githubusercontent.com/245089/47142455-30900280-d2c3-11e8-8b76-ea16c7e502d4.png)
|
||||||
|
|
||||||
|
Linux (KDE desktop):
|
||||||
|
![notify-kde](https://user-images.githubusercontent.com/245089/47149206-27a72d00-d2d3-11e8-8f1b-96e462f08c2b.png)
|
90
extern/pfd-fixed/doc/open_file.md
vendored
Normal file
90
extern/pfd-fixed/doc/open_file.md
vendored
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
## File Open API
|
||||||
|
|
||||||
|
The `pfd::open_file` class handles file opening dialogs. It can be provided a title, a starting
|
||||||
|
directory and/or pre-selected file, an optional filter for recognised file types, and an optional
|
||||||
|
flag to allow multiple selection:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
pfd::open_file::open_file(std::string const &title,
|
||||||
|
std::string const &initial_path,
|
||||||
|
std::vector<std::string> filters = { "All Files", "*" },
|
||||||
|
pfd::opt option = pfd::opt::none);
|
||||||
|
```
|
||||||
|
|
||||||
|
The `option` parameter can be `pfd::opt::multiselect` to allow selecting multiple files.
|
||||||
|
|
||||||
|
The selected files are queried using `pfd::open_file::result()`. If the user canceled the
|
||||||
|
operation, the returned list is empty:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::vector<std::string> pfd::open_file::result();
|
||||||
|
```
|
||||||
|
|
||||||
|
It is possible to ask the file open dialog whether the user took action using the
|
||||||
|
`pfd::message::ready()` method, with an optional `timeout` argument. If the user did not validate
|
||||||
|
the dialog within `timeout` milliseconds, the function will return `false`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool pfd::open_file::ready(int timeout = pfd::default_wait_timeout);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 1: simple file selection
|
||||||
|
|
||||||
|
Using `pfd::open_file::result()` will wait for user action before returning. This operation will
|
||||||
|
block and return the user choice:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto selection = pfd::open_file("Select a file").result();
|
||||||
|
if (!selection.empty())
|
||||||
|
std::cout << "User selected file " << selection[0] << "\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 2: filters
|
||||||
|
|
||||||
|
The filter list enumerates filter names and corresponded space-separated wildcard lists. It
|
||||||
|
defaults to `{ "All Files", "*" }`, but here is how to use other options:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto selection = pfd::open_file("Select a file", ".",
|
||||||
|
{ "Image Files", "*.png *.jpg *.jpeg *.bmp",
|
||||||
|
"Audio Files", "*.wav *.mp3",
|
||||||
|
"All Files", "*" },
|
||||||
|
pfd::opt::multiselect).result();
|
||||||
|
// Do something with selection
|
||||||
|
for (auto const &filename : dialog.result())
|
||||||
|
std::cout << "Selected file: " << filename << "\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 3: asynchronous file open
|
||||||
|
|
||||||
|
Using `pfd::open_file::ready()` allows the application to perform other tasks while waiting for
|
||||||
|
user input:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// File open dialog
|
||||||
|
auto dialog = pfd::open_file("Select file to open");
|
||||||
|
|
||||||
|
// Do something while waiting for user input
|
||||||
|
while (!dialog.ready(1000))
|
||||||
|
std::cout << "Waited 1 second for user input...\n";
|
||||||
|
|
||||||
|
// Act depending on the user choice
|
||||||
|
std::cout << "Number of selected files: " << dialog.result().size() << "\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
Windows 10:
|
||||||
|
![open-win32](https://user-images.githubusercontent.com/245089/47155865-0f8cd900-d2e6-11e8-8041-1e20b6f77dee.png)
|
||||||
|
|
||||||
|
Mac OS X (dark theme):
|
||||||
|
![image](https://user-images.githubusercontent.com/245089/56053378-39363280-5d54-11e9-9583-9f1c978fa0db.png)
|
||||||
|
|
||||||
|
Mac OS X (light theme):
|
||||||
|
![image](https://user-images.githubusercontent.com/245089/56053413-4fdc8980-5d54-11e9-85e3-e9e5d0e10772.png)
|
||||||
|
|
||||||
|
Linux (GNOME desktop):
|
||||||
|
![open-gnome](https://user-images.githubusercontent.com/245089/47155867-0f8cd900-d2e6-11e8-93af-275636491ec4.png)
|
||||||
|
|
||||||
|
Linux (KDE desktop):
|
||||||
|
![open-kde](https://user-images.githubusercontent.com/245089/47155866-0f8cd900-d2e6-11e8-8006-f14b948afc55.png)
|
120
extern/pfd-fixed/doc/pfd.md
vendored
Normal file
120
extern/pfd-fixed/doc/pfd.md
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
## Portable File Dialogs documentation
|
||||||
|
|
||||||
|
The library can be used either as a [header-only library](https://en.wikipedia.org/wiki/Header-only),
|
||||||
|
or as a [single file library](https://github.com/nothings/single_file_libs).
|
||||||
|
|
||||||
|
### Use as header-only library
|
||||||
|
|
||||||
|
Just include the main header file wherever needed:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include "portable-file-dialogs.h"
|
||||||
|
|
||||||
|
/* ... */
|
||||||
|
|
||||||
|
pfd::message::message("Hello", "This is a test");
|
||||||
|
|
||||||
|
/* ... */
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use as a single-file library
|
||||||
|
|
||||||
|
Defining the `PFD_SKIP_IMPLEMENTATION` macro before including `portable-file-dialogs.h` will
|
||||||
|
skip all the implementation code and reduce compilation times. You still need to include the
|
||||||
|
header without the macro at least once, typically in a `pfd-impl.cpp` file.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// In pfd-impl.cpp
|
||||||
|
#include "portable-file-dialogs.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// In all other files
|
||||||
|
#define PFD_SKIP_IMPLEMENTATION 1
|
||||||
|
#include "portable-file-dialogs.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
### General concepts
|
||||||
|
|
||||||
|
Dialogs inherit from `pfd::dialog` and are created by calling their class constructor. Their
|
||||||
|
destructor will block until the window is closed by user interaction. So for instance this
|
||||||
|
will block until the end of the line:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
pfd::message::message("Hi", "there");
|
||||||
|
```
|
||||||
|
|
||||||
|
Whereas this will only block until the end of the scope, allowing the program to perform
|
||||||
|
additional operations while the dialog is open:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
{
|
||||||
|
auto m = pfd::message::message("Hi", "there");
|
||||||
|
|
||||||
|
// ... perform asynchronous operations here
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It is possible to call `bool pfd::dialog::ready(timeout)` on the dialog in order to query its
|
||||||
|
status and perform asynchronous operations as long as the user has not interacted:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
{
|
||||||
|
auto m = pfd::message::message("Hi", "there");
|
||||||
|
|
||||||
|
while (!m.ready())
|
||||||
|
{
|
||||||
|
// ... perform asynchronous operations here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If necessary, a dialog can be forcibly closed using `bool pfd::dialog::kill()`. Note that this
|
||||||
|
may be confusing to the user and should only be used in very specific situations. It is also not
|
||||||
|
possible to close a Windows message box that provides no _Cancel_ button.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
{
|
||||||
|
auto m = pfd::message::message("Hi", "there");
|
||||||
|
|
||||||
|
while (!m.ready())
|
||||||
|
{
|
||||||
|
// ... perform asynchronous operations here
|
||||||
|
|
||||||
|
if (too_much_time_has_passed())
|
||||||
|
m.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, the user response can be retrieved using `pfd::dialog::result()`. The return value of
|
||||||
|
this function depends on which dialog is being used. See their respective documentation for more
|
||||||
|
information:
|
||||||
|
|
||||||
|
* [`pfd::message`](message.md) (message box)
|
||||||
|
* [`pfd::notify`](notify.md) (notification)
|
||||||
|
* [`pfd::open_file`](open_file.md) (file open)
|
||||||
|
* [`pfd::save_file`](save_file.md) (file save)
|
||||||
|
* [`pfd::select_folder`](select_folder.md) (folder selection)
|
||||||
|
|
||||||
|
### Settings
|
||||||
|
|
||||||
|
The library can be queried and configured through the `pfd::settings` class.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool pfd::settings::available();
|
||||||
|
void pfd::settings::verbose(bool value);
|
||||||
|
void pfd::settings::rescan();
|
||||||
|
```
|
||||||
|
|
||||||
|
The return value of `pfd::settings::available()` indicates whether a suitable dialog backend (such
|
||||||
|
as Zenity or KDialog on Linux) has been found. If not, the library will not work and all dialog
|
||||||
|
invocations will be no-ops. The program will not crash but you should account for this situation
|
||||||
|
and add a fallback mechanism or exit gracefully.
|
||||||
|
|
||||||
|
Calling `pfd::settings::rescan()` will force a rescan of available backends. This may change the
|
||||||
|
result of `pfd::settings::available()` if a backend was installed on the system in the meantime.
|
||||||
|
This is probably only useful for debugging purposes.
|
||||||
|
|
||||||
|
Calling `pfd::settings::verbose(true)` may help debug the library. It will output debug information
|
||||||
|
to `std::cout` about some operations being performed.
|
73
extern/pfd-fixed/doc/save_file.md
vendored
Normal file
73
extern/pfd-fixed/doc/save_file.md
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
## File Open API
|
||||||
|
|
||||||
|
The `pfd::save_file` class handles file saving dialogs. It can be provided a title, a starting
|
||||||
|
directory and/or pre-selected file, an optional filter for recognised file types, and an optional
|
||||||
|
flag to allow multiple selection:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
pfd::save_file::save_file(std::string const &title,
|
||||||
|
std::string const &initial_path,
|
||||||
|
std::vector<std::string> filters = { "All Files", "*" },
|
||||||
|
pfd::opt option = pfd::opt::none);
|
||||||
|
```
|
||||||
|
|
||||||
|
The `option` parameter can be `pfd::opt::force_overwrite` to disable a potential warning when
|
||||||
|
saving to an existing file.
|
||||||
|
|
||||||
|
The selected file is queried using `pfd::save_file::result()`. If the user canceled the
|
||||||
|
operation, the returned file name is empty:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::string pfd::save_file::result();
|
||||||
|
```
|
||||||
|
|
||||||
|
It is possible to ask the file save dialog whether the user took action using the
|
||||||
|
`pfd::message::ready()` method, with an optional `timeout` argument. If the user did not validate
|
||||||
|
the dialog within `timeout` milliseconds, the function will return `false`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool pfd::save_file::ready(int timeout = pfd::default_wait_timeout);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 1: simple file selection
|
||||||
|
|
||||||
|
Using `pfd::save_file::result()` will wait for user action before returning. This operation will
|
||||||
|
block and return the user choice:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto destination = pfd::save_file("Select a file").result();
|
||||||
|
if (!destination.empty())
|
||||||
|
std::cout << "User selected file " << destination << "\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 2: filters
|
||||||
|
|
||||||
|
The filter list enumerates filter names and corresponded space-separated wildcard lists. It
|
||||||
|
defaults to `{ "All Files", "*" }`, but here is how to use other options:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto destination = pfd::save_file("Select a file", ".",
|
||||||
|
{ "Image Files", "*.png *.jpg *.jpeg *.bmp",
|
||||||
|
"Audio Files", "*.wav *.mp3",
|
||||||
|
"All Files", "*" },
|
||||||
|
pfd::opt::force_overwrite).result();
|
||||||
|
// Do something with destination
|
||||||
|
std::cout << "Selected file: " << destination << "\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 3: asynchronous file save
|
||||||
|
|
||||||
|
Using `pfd::save_file::ready()` allows the application to perform other tasks while waiting for
|
||||||
|
user input:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// File save dialog
|
||||||
|
auto dialog = pfd::save_file("Select file to save");
|
||||||
|
|
||||||
|
// Do something while waiting for user input
|
||||||
|
while (!dialog.ready(1000))
|
||||||
|
std::cout << "Waited 1 second for user input...\n";
|
||||||
|
|
||||||
|
// Act depending on the user choice
|
||||||
|
std::cout << "User selected file: " << dialog.result() << "\n";
|
||||||
|
```
|
55
extern/pfd-fixed/doc/select_folder.md
vendored
Normal file
55
extern/pfd-fixed/doc/select_folder.md
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
## Folder Selection API
|
||||||
|
|
||||||
|
The `pfd::select_folder` class handles folder opening dialogs. It can be provided a title, and an
|
||||||
|
optional starting directory:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
pfd::select_folder::select_folder(std::string const &title,
|
||||||
|
std::string const &default_path = "",
|
||||||
|
pfd::opt option = pfd::opt::none);
|
||||||
|
```
|
||||||
|
|
||||||
|
The `option` parameter can be `pfd::opt::force_path` to force the operating system to use the
|
||||||
|
provided path. Some systems default to the most recently used path, if applicable.
|
||||||
|
|
||||||
|
The selected folder is queried using `pfd::select_folder::result()`. If the user canceled the
|
||||||
|
operation, the returned string is empty:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::string pfd::select_folder::result();
|
||||||
|
```
|
||||||
|
|
||||||
|
It is possible to ask the folder selection dialog whether the user took action using the
|
||||||
|
`pfd::message::ready()` method, with an optional `timeout` argument. If the user did not validate
|
||||||
|
the dialog within `timeout` milliseconds, the function will return `false`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool pfd::select_folder::ready(int timeout = pfd::default_wait_timeout);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 1: simple folder selection
|
||||||
|
|
||||||
|
Using `pfd::select_folder::result()` will wait for user action before returning. This operation
|
||||||
|
will block and return the user choice:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto selection = pfd::select_folder("Select a folder").result();
|
||||||
|
if (!selection.empty())
|
||||||
|
std::cout << "User selected folder " << selection << "\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 2: asynchronous folder open
|
||||||
|
|
||||||
|
Using `pfd::select_folder::ready()` allows the application to perform other tasks while waiting for user input:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Folder selection dialog
|
||||||
|
auto dialog = pfd::select_folder("Select folder to open");
|
||||||
|
|
||||||
|
// Do something while waiting for user input
|
||||||
|
while (!dialog.ready(1000))
|
||||||
|
std::cout << "Waited 1 second for user input...\n";
|
||||||
|
|
||||||
|
// Act depending on the user choice
|
||||||
|
std::cout << "Selected folder: " << dialog.result() << "\n";
|
||||||
|
```
|
11
extern/pfd-fixed/examples/.gitignore
vendored
Normal file
11
extern/pfd-fixed/examples/.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
example
|
||||||
|
example.exe
|
||||||
|
kill
|
||||||
|
kill.exe
|
||||||
|
|
||||||
|
Debug
|
||||||
|
Release
|
||||||
|
*.vcxproj.user
|
||||||
|
|
||||||
|
.idea
|
||||||
|
cmake-build-*
|
110
extern/pfd-fixed/examples/example.cpp
vendored
Normal file
110
extern/pfd-fixed/examples/example.cpp
vendored
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
//
|
||||||
|
// Portable File Dialogs
|
||||||
|
//
|
||||||
|
// Copyright © 2018—2020 Sam Hocevar <sam@hocevar.net>
|
||||||
|
//
|
||||||
|
// This program is free software. It comes without any warranty, to
|
||||||
|
// the extent permitted by applicable law. You can redistribute it
|
||||||
|
// and/or modify it under the terms of the Do What the Fuck You Want
|
||||||
|
// to Public License, Version 2, as published by the WTFPL Task Force.
|
||||||
|
// See http://www.wtfpl.net/ for more details.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "portable-file-dialogs.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#define DEFAULT_PATH "C:\\"
|
||||||
|
#else
|
||||||
|
#define DEFAULT_PATH "/tmp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Check that a backend is available
|
||||||
|
if (!pfd::settings::available())
|
||||||
|
{
|
||||||
|
std::cout << "Portable File Dialogs are not available on this platform.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set verbosity to true
|
||||||
|
pfd::settings::verbose(true);
|
||||||
|
|
||||||
|
// Notification
|
||||||
|
pfd::notify("Important Notification",
|
||||||
|
"This is ' a message, pay \" attention \\ to it!",
|
||||||
|
pfd::icon::info);
|
||||||
|
|
||||||
|
// Message box with nice message
|
||||||
|
auto m = pfd::message("Personal Message",
|
||||||
|
"You are an amazing person, don’t let anyone make you think otherwise.",
|
||||||
|
pfd::choice::yes_no_cancel,
|
||||||
|
pfd::icon::warning);
|
||||||
|
|
||||||
|
// Optional: do something while waiting for user action
|
||||||
|
for (int i = 0; i < 10 && !m.ready(1000); ++i)
|
||||||
|
std::cout << "Waited 1 second for user input...\n";
|
||||||
|
|
||||||
|
// Do something according to the selected button
|
||||||
|
switch (m.result())
|
||||||
|
{
|
||||||
|
case pfd::button::yes: std::cout << "User agreed.\n"; break;
|
||||||
|
case pfd::button::no: std::cout << "User disagreed.\n"; break;
|
||||||
|
case pfd::button::cancel: std::cout << "User freaked out.\n"; break;
|
||||||
|
default: break; // Should not happen
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directory selection
|
||||||
|
auto dir = pfd::select_folder("Select any directory", DEFAULT_PATH).result();
|
||||||
|
std::cout << "Selected dir: " << dir << "\n";
|
||||||
|
|
||||||
|
// File open
|
||||||
|
auto f = pfd::open_file("Choose files to read", DEFAULT_PATH,
|
||||||
|
{ "Text Files (.txt .text)", "*.txt *.text",
|
||||||
|
"All Files", "*" },
|
||||||
|
pfd::opt::multiselect);
|
||||||
|
std::cout << "Selected files:";
|
||||||
|
for (auto const &name : f.result())
|
||||||
|
std::cout << " " + name;
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unused function that just tests the whole API
|
||||||
|
void api()
|
||||||
|
{
|
||||||
|
// pfd::settings
|
||||||
|
pfd::settings::verbose(true);
|
||||||
|
pfd::settings::rescan();
|
||||||
|
|
||||||
|
// pfd::notify
|
||||||
|
pfd::notify("", "");
|
||||||
|
pfd::notify("", "", pfd::icon::info);
|
||||||
|
pfd::notify("", "", pfd::icon::warning);
|
||||||
|
pfd::notify("", "", pfd::icon::error);
|
||||||
|
pfd::notify("", "", pfd::icon::question);
|
||||||
|
|
||||||
|
pfd::notify a("", "");
|
||||||
|
(void)a.ready();
|
||||||
|
(void)a.ready(42);
|
||||||
|
|
||||||
|
// pfd::message
|
||||||
|
pfd::message("", "");
|
||||||
|
pfd::message("", "", pfd::choice::ok);
|
||||||
|
pfd::message("", "", pfd::choice::ok_cancel);
|
||||||
|
pfd::message("", "", pfd::choice::yes_no);
|
||||||
|
pfd::message("", "", pfd::choice::yes_no_cancel);
|
||||||
|
pfd::message("", "", pfd::choice::retry_cancel);
|
||||||
|
pfd::message("", "", pfd::choice::abort_retry_ignore);
|
||||||
|
pfd::message("", "", pfd::choice::ok, pfd::icon::info);
|
||||||
|
pfd::message("", "", pfd::choice::ok, pfd::icon::warning);
|
||||||
|
pfd::message("", "", pfd::choice::ok, pfd::icon::error);
|
||||||
|
pfd::message("", "", pfd::choice::ok, pfd::icon::question);
|
||||||
|
|
||||||
|
pfd::message b("", "");
|
||||||
|
(void)b.ready();
|
||||||
|
(void)b.ready(42);
|
||||||
|
(void)b.result();
|
||||||
|
}
|
||||||
|
|
96
extern/pfd-fixed/examples/example.vcxproj
vendored
Normal file
96
extern/pfd-fixed/examples/example.vcxproj
vendored
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="example.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="../portable-file-dialogs.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>15.0</VCProjectVersion>
|
||||||
|
<ProjectGuid>{10F4364D-27C4-4C74-8079-7C42971E81E7}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>example</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
|
||||||
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
42
extern/pfd-fixed/examples/kill.cpp
vendored
Normal file
42
extern/pfd-fixed/examples/kill.cpp
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
//
|
||||||
|
// Portable File Dialogs
|
||||||
|
//
|
||||||
|
// Copyright © 2018—2020 Sam Hocevar <sam@hocevar.net>
|
||||||
|
//
|
||||||
|
// This program is free software. It comes without any warranty, to
|
||||||
|
// the extent permitted by applicable law. You can redistribute it
|
||||||
|
// and/or modify it under the terms of the Do What the Fuck You Want
|
||||||
|
// to Public License, Version 2, as published by the WTFPL Task Force.
|
||||||
|
// See http://www.wtfpl.net/ for more details.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "portable-file-dialogs.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Set verbosity to true
|
||||||
|
pfd::settings::verbose(true);
|
||||||
|
|
||||||
|
// Message box with nice message
|
||||||
|
auto m = pfd::message("Upgrade software?",
|
||||||
|
"Press OK to upgrade this software.\n"
|
||||||
|
"\n"
|
||||||
|
"By default, the software will update itself\n"
|
||||||
|
"automatically in 10 seconds.",
|
||||||
|
pfd::choice::ok_cancel,
|
||||||
|
pfd::icon::warning);
|
||||||
|
|
||||||
|
// Wait for an answer for up to 10 seconds
|
||||||
|
for (int i = 0; i < 10 && !m.ready(1000); ++i)
|
||||||
|
;
|
||||||
|
|
||||||
|
// Upgrade software if user clicked OK, or if user didn’t interact
|
||||||
|
bool upgrade = m.ready() ? m.result() == pfd::button::ok : m.kill();
|
||||||
|
if (upgrade)
|
||||||
|
std::cout << "Upgrading software!\n";
|
||||||
|
else
|
||||||
|
std::cout << "Not upgrading software.\n";
|
||||||
|
}
|
||||||
|
|
96
extern/pfd-fixed/examples/kill.vcxproj
vendored
Normal file
96
extern/pfd-fixed/examples/kill.vcxproj
vendored
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="kill.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="../portable-file-dialogs.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>15.0</VCProjectVersion>
|
||||||
|
<ProjectGuid>{B94D26B1-7EF7-43A2-A973-9A96A08E2E17}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>kill</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
|
||||||
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
1731
extern/pfd-fixed/portable-file-dialogs.h
vendored
Normal file
1731
extern/pfd-fixed/portable-file-dialogs.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -10,7 +10,7 @@ double-click to open the instrument editor.
|
||||||
|
|
||||||
every instrument can be renamed and have its type changed.
|
every instrument can be renamed and have its type changed.
|
||||||
|
|
||||||
depending on the instrument type, there are currently 12 different types of an instrument editor:
|
depending on the instrument type, there are currently 13 different types of an instrument editor:
|
||||||
|
|
||||||
- [FM synthesis](fm.md) - for use with YM2612, YM2151 and FM block portion of YM2610.
|
- [FM synthesis](fm.md) - for use with YM2612, YM2151 and FM block portion of YM2610.
|
||||||
- [Standard](standard.md) - for use with NES and Sega Master System's PSG sound source and its derivatives.
|
- [Standard](standard.md) - for use with NES and Sega Master System's PSG sound source and its derivatives.
|
||||||
|
@ -22,8 +22,10 @@ depending on the instrument type, there are currently 12 different types of an i
|
||||||
- [SAA1099](saa.md) - for use with Philips SAA1099 PSG sound source.
|
- [SAA1099](saa.md) - for use with Philips SAA1099 PSG sound source.
|
||||||
- [TIA](tia.md) - for use with Atari 2600 system.
|
- [TIA](tia.md) - for use with Atari 2600 system.
|
||||||
- [AY-3-8910](ay8910.md) - for use with AY-3-8910 PSG sound source and SSG portion in YM2610.
|
- [AY-3-8910](ay8910.md) - for use with AY-3-8910 PSG sound source and SSG portion in YM2610.
|
||||||
- [Amiga/sample](amiga.md) for controlling Amiga and other sample based synthsizers like YM2612's Channel 6 PCM mode, NES channel 5, Sega PCM and PC Engine's sample playback mode.
|
- [Amiga/sample](amiga.md) for controlling Amiga and other sample based synthsizers like YM2612's Channel 6 PCM mode, NES channel 5, Sega PCM, X1-010 and PC Engine's sample playback mode.
|
||||||
- [Atari Lynx](lynx.md) - for use with Atari Lynx handheld console.
|
- [Atari Lynx](lynx.md) - for use with Atari Lynx handheld console.
|
||||||
|
- [VERA](vera.md) - for use with Commander X16 VERA.
|
||||||
|
- [Seta/Allumer X1-010](x1_010.md) - for use with Wavetable portion in Seta/Allumer X1-010.
|
||||||
|
|
||||||
# macros
|
# macros
|
||||||
|
|
||||||
|
|
8
papers/doc/4-instrument/vera.md
Normal file
8
papers/doc/4-instrument/vera.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# VERA instrument editor
|
||||||
|
|
||||||
|
VERA instrument editor consists of only four macros:
|
||||||
|
|
||||||
|
- [Volume] - volume sequence
|
||||||
|
- [Arpeggio] - pitch sequence
|
||||||
|
- [Duty cycle] - pulse duty cycle sequence
|
||||||
|
- [Waveform] - select the waveform used by instrument
|
11
papers/doc/4-instrument/x1_010.md
Normal file
11
papers/doc/4-instrument/x1_010.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# X1-010 instrument editor
|
||||||
|
|
||||||
|
X1-010 instrument editor consists of 7 macros.
|
||||||
|
|
||||||
|
- [Volume] - volume levels sequence
|
||||||
|
- [Arpeggio]- pitch sequence
|
||||||
|
- [Waveform] - spicifies wavetables sequence
|
||||||
|
- [Envelope Mode] - allows shaping an envelope
|
||||||
|
- [Envelope] - spicifies envelope shape sequence, it's also wavetable.
|
||||||
|
- [Auto envelope numerator] - sets the envelope to the channel's frequency multiplied by numerator
|
||||||
|
- [Auto envelope denominator] - ets the envelope to the channel's frequency multiplied by denominator
|
|
@ -2,4 +2,4 @@
|
||||||
|
|
||||||
Wavetable synthizers, in context of Furnace, are sound sources that operate on extremely short n-bit PCM streams. By extremely short, no more than 256 bytes. This amount of space is nowhere near enough to store an actual sampled sound, it allows certain amount of freedom to define a waveform shape. As of Furnace 0.5.8, wavetable editor affects PC Engine, WonderSwan and channel 3 of Game Boy.
|
Wavetable synthizers, in context of Furnace, are sound sources that operate on extremely short n-bit PCM streams. By extremely short, no more than 256 bytes. This amount of space is nowhere near enough to store an actual sampled sound, it allows certain amount of freedom to define a waveform shape. As of Furnace 0.5.8, wavetable editor affects PC Engine, WonderSwan and channel 3 of Game Boy.
|
||||||
|
|
||||||
Furnace's wavetable editor is rather simple, you can draw the waveform using mouse or by pasting an MML bit stream in the input field. Maximum wave width (length) is 256 bytes, and maximum wave height (depth) is 256. NOTE: Game Boy, PCE and WonderSwan can handle max 32 byte waveforms as of now, with 16-level height for GB and WS, and 32-level height for PCE. If larger wave will be defined for these two systems, it will be squashed to fit within the constraints of the system.
|
Furnace's wavetable editor is rather simple, you can draw the waveform using mouse or by pasting an MML bit stream in the input field. Maximum wave width (length) is 256 bytes, and maximum wave height (depth) is 256. NOTE: Game Boy, PCE and WonderSwan can handle max 32 byte waveforms, X1-010 can handle max 128 byte waveforms as of now, with 16-level height for GB, X1-010 Envelope and WS, and 32-level height for PCE. If larger wave will be defined for these systems, it will be squashed to fit within the constraints of the system.
|
||||||
|
|
|
@ -12,7 +12,8 @@ As of Furnace 0.5.5, the following sound chips have sample support:
|
||||||
- PC Engine/TurboGrafx 16/Huc6280 (same conditions as above)
|
- PC Engine/TurboGrafx 16/Huc6280 (same conditions as above)
|
||||||
- Amiga/Paula (on all channels AND resamplable, but you need to make an instrument with the Amiga format and tie it to a sample first)
|
- Amiga/Paula (on all channels AND resamplable, but you need to make an instrument with the Amiga format and tie it to a sample first)
|
||||||
- Arcade/SEGA PCM (same as above but you don't need to make an instrument for it and you have to use the `20xx` effect command to resample your samples)
|
- Arcade/SEGA PCM (same as above but you don't need to make an instrument for it and you have to use the `20xx` effect command to resample your samples)
|
||||||
- Neo Geo/Neo Geo EXT-Ch2 (on the last 5 channels only and can be resampled the same way as above)
|
- Neo Geo/Neo Geo EXT-Ch2 (on the last 7 channels only and can be resampled the same way as above)
|
||||||
|
- Seta/Allumer X1-010 (same as above, and both `1701` and `20xx` effect commands are affected on all 16 channels)
|
||||||
|
|
||||||
Furnace also has a feature where you can make an Amiga formarted instrument on the YM2612 and Huc6280 to resample a sample you have in the module.
|
Furnace also has a feature where you can make an Amiga formarted instrument on the YM2612 and Huc6280 to resample a sample you have in the module.
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ this is a list of systems that Furnace supports, including each system's effects
|
||||||
- [Atari 2600](tia.md)
|
- [Atari 2600](tia.md)
|
||||||
- [Philips SAA1099](saa1099.md)
|
- [Philips SAA1099](saa1099.md)
|
||||||
- [Microchip AY8930](ay8930.md)
|
- [Microchip AY8930](ay8930.md)
|
||||||
|
- [Seta/Allumer X1-010](x1_010.md)
|
||||||
- [WonderSwan](wonderswan.md)
|
- [WonderSwan](wonderswan.md)
|
||||||
|
|
||||||
Furnace also reads .dmf files with the [Yamaha YMU759](ymu759.md) system, but does not emulate the chip at all.
|
Furnace also reads .dmf files with the [Yamaha YMU759](ymu759.md) system, but does not emulate the chip at all.
|
||||||
|
|
|
@ -4,6 +4,8 @@ this chip was used in several home computers (ZX Spectrum, MSX, Amstrad CPC, Ata
|
||||||
|
|
||||||
the chip's powerful sound comes from the envelope...
|
the chip's powerful sound comes from the envelope...
|
||||||
|
|
||||||
|
AY-3-8914 variant was used in Intellivision, it's basically original AY with 4 level envelope volume per channel and different register format.
|
||||||
|
|
||||||
# effects
|
# effects
|
||||||
|
|
||||||
- `20xx`: set channel mode. `xx` may be one of the following:
|
- `20xx`: set channel mode. `xx` may be one of the following:
|
||||||
|
|
20
papers/doc/7-systems/lynx.md
Normal file
20
papers/doc/7-systems/lynx.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Atari Lynx/MIKEY
|
||||||
|
|
||||||
|
The Atari Lynx is a 16 bit handheld console developed by (obviously) Atari Corporation, and initially released in September of 1989, with the worldwide release being in 1990.
|
||||||
|
|
||||||
|
The Lynx, while being an incredible handheld for the time (and a lot more powerful than a Game Boy), unfortunately meant nothing in the end due to the Lynx being a market failiure, and ending up as one of the things that contributed to the downfall of Atari.
|
||||||
|
|
||||||
|
Although the Lynx is still getting (rather impressive) homebrew developed for it, it does not mean that the Lynx is a popular system at all.
|
||||||
|
|
||||||
|
The Atari Lynx's custom sound chip and CPU (MIKEY) is a 6502-based 8 bit CPU running at 16MHz, however this information is generally not useful in the context of Furnace.
|
||||||
|
|
||||||
|
## Sound capabilities
|
||||||
|
|
||||||
|
- The MIKEY has 4 channels of square wave-based sound, which can be modulated with different frequencies (×0, ×1, ×2, ×3, ×4, ×5, ×7, ×10, and ×11) to create wavetable-like results.
|
||||||
|
- Likewise, when a lot of the modulators are activated, this can provide a "pseudo-white noise"-like effect, whoch can be useful for drums and sound effects.
|
||||||
|
- The MIKEY also has hard stereo panning capabilities via the `08xx` effect command.
|
||||||
|
- The MIKEY has four 8-bit DACs (Digital to Analog Converter) — one for each voice — that essentially mean you can play samples on the MIKEY (at the cost of CPU time and memory).
|
||||||
|
- The MIKEY also has a variety of pitches to choose from, and they go from 32Hz to "above the range of human hearing", according to Atari.
|
||||||
|
|
||||||
|
## Effect commands
|
||||||
|
- `3xxx`: Load LFSR (0 to FFF). For it to work, duty macro in instrument editor must be set to some value, without it LFSR will not be fed with any bits.
|
47
papers/doc/7-systems/x1_010.md
Normal file
47
papers/doc/7-systems/x1_010.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# Seta/Allumer X1-010
|
||||||
|
|
||||||
|
One of sound chip originally designed by Seta, mainly used at their arcade hardwares at late-80s to early-2000s.
|
||||||
|
It has 2 output channels, but no known hardware using this feature for stereo sound.
|
||||||
|
Later hardware paired this with external bankswitching logic, but its logic is not emulated now in current furnace revision.
|
||||||
|
Allumer one is just rebadged Seta's thing for use in their arcade hardwares.
|
||||||
|
|
||||||
|
It has 16 channels, and all channels can be switchable to PCM sample or wavetable playback mode.
|
||||||
|
Wavetable needs to paired with envelope, this feature is similar as AY PSG, but its shape are stored at RAM: it means it is user-definable.
|
||||||
|
|
||||||
|
In furnace, this chip is can be configurable for original arcade mono output or stereo output - it simulates early 'incorrect' emulation on some mono hardware, but it is also based on the assumption that each channel is connected to each output.
|
||||||
|
|
||||||
|
# waveform type
|
||||||
|
|
||||||
|
This chip supports 2 type waveforms, needs to paired external 8 KB RAM for use these features:
|
||||||
|
|
||||||
|
One is signed 8 bit mono waveform, it's operated like other wavetable based sound systems.
|
||||||
|
These are stored at the bottom half of RAM at common case.
|
||||||
|
|
||||||
|
Another one ("Envelope") is 4 bit stereo waveform, it's multiplied with above and calculates final output, Each nibble is used for each output channels.
|
||||||
|
These are stored at the upper half of RAM at common case.
|
||||||
|
|
||||||
|
Both waveforms are 128 byte fixed size, it's freely allocated at each half of RAM except channel register area: each half can be stored total 32/31 waveforms at once.
|
||||||
|
In furnace, You can set envelope shape split mode. When it sets, its waveform will be split to left half and right half for each outputs. each max size are 128 bytes, total 256 bytes.
|
||||||
|
|
||||||
|
# effects
|
||||||
|
|
||||||
|
- `10xx`: change wave.
|
||||||
|
- `11xx`: change envelope shape. (also wavetable)
|
||||||
|
- `17xx`: toggle PCM mode.
|
||||||
|
- `20xx`: set PCM frequency. (1 to FF)*
|
||||||
|
- `22xx`: set envelope mode.
|
||||||
|
- bit 0 sets whether envelope will affect this channel.
|
||||||
|
- bit 1 sets whether envelope one-shot mode. when it sets, channel is halted after envelope cycle is ended.
|
||||||
|
- bit 2 sets whether envelope shape split mode. when it sets, envelope shape will splitted to left half and right half.
|
||||||
|
- bit 3/5 sets whether the right/left shape will mirror the original one.
|
||||||
|
- bit 4/6 sets whether the right/left output will mirror the original one.
|
||||||
|
- `23xx`: set envelope period.
|
||||||
|
- `25xx`: slide envelope period up.
|
||||||
|
- `26xx`: slide envelope period down.
|
||||||
|
- `29xy`: enable auto-envelope mode.
|
||||||
|
- in this mode the envelope period is set to the channel's notes, multiplied by a fraction.
|
||||||
|
- `x` is the numerator.
|
||||||
|
- `y` is the denominator.
|
||||||
|
- if `x` or `y` are 0 this will disable auto-envelope mode.
|
||||||
|
|
||||||
|
* PCM frequency: 255 step, fomula: `step * (Chip clock / 8192)`; 1.95KHz to 498KHz if Chip clock is 16MHz.
|
|
@ -29,6 +29,9 @@ furthermore, an `or reserved` indicates this field is always present, but is res
|
||||||
|
|
||||||
the format versions are:
|
the format versions are:
|
||||||
|
|
||||||
|
- 66: Furnace dev66
|
||||||
|
- 65: Furnace dev65
|
||||||
|
- 64: Furnace dev64
|
||||||
- 63: Furnace dev63
|
- 63: Furnace dev63
|
||||||
- 62: Furnace dev62
|
- 62: Furnace dev62
|
||||||
- 61: Furnace dev61
|
- 61: Furnace dev61
|
||||||
|
@ -203,7 +206,10 @@ size | description
|
||||||
1 | ignore duplicate slides (>=50) or reserved
|
1 | ignore duplicate slides (>=50) or reserved
|
||||||
1 | stop portamento on note off (>=62) or reserved
|
1 | stop portamento on note off (>=62) or reserved
|
||||||
1 | continuous vibrato (>=62) or reserved
|
1 | continuous vibrato (>=62) or reserved
|
||||||
4 | reserved
|
1 | broken DAC mode (>=64) or reserved
|
||||||
|
1 | one tick cut (>=65) or reserved
|
||||||
|
1 | instrument change allowed during porta (>=66) or reserved
|
||||||
|
1 | reserved
|
||||||
4?? | pointers to instruments
|
4?? | pointers to instruments
|
||||||
4?? | pointers to wavetables
|
4?? | pointers to wavetables
|
||||||
4?? | pointers to samples
|
4?? | pointers to samples
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# make Windows release
|
# make Windows release
|
||||||
# this script shall be run from Linux with MinGW installed!
|
# this script shall be run from Arch Linux with MinGW installed!
|
||||||
|
|
||||||
if [ ! -e /tmp/furnace ]; then
|
if [ ! -e /tmp/furnace ]; then
|
||||||
ln -s "$PWD" /tmp/furnace || exit 1
|
ln -s "$PWD" /tmp/furnace || exit 1
|
||||||
|
@ -14,7 +14,8 @@ fi
|
||||||
|
|
||||||
cd win32build
|
cd win32build
|
||||||
|
|
||||||
i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Werror" -DBUILD_SHARED_LIBS=OFF .. || exit 1
|
# TODO: potential Arch-ism?
|
||||||
|
i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Werror" -DBUILD_SHARED_LIBS=OFF .. || exit 1
|
||||||
make -j8 || exit 1
|
make -j8 || exit 1
|
||||||
i686-w64-mingw32-strip -s furnace.exe || exit 1
|
i686-w64-mingw32-strip -s furnace.exe || exit 1
|
||||||
|
|
||||||
|
@ -30,3 +31,7 @@ cp -r ../../papers papers || exit 1
|
||||||
cp -r ../../demos demos || exit 1
|
cp -r ../../demos demos || exit 1
|
||||||
|
|
||||||
zip -r furnace.zip LICENSE.txt furnace.exe README.txt papers demos
|
zip -r furnace.zip LICENSE.txt furnace.exe README.txt papers demos
|
||||||
|
|
||||||
|
furName=$(git describe --tags | sed "s/v0/0/")
|
||||||
|
|
||||||
|
mv furnace.zip furnace-"$furName"-win32.zip
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# make Windows release
|
# make Windows release
|
||||||
# this script shall be run from Linux with MinGW installed!
|
# this script shall be run from Arch Linux with MinGW installed!
|
||||||
|
|
||||||
if [ ! -e /tmp/furnace ]; then
|
if [ ! -e /tmp/furnace ]; then
|
||||||
ln -s "$PWD" /tmp/furnace || exit 1
|
ln -s "$PWD" /tmp/furnace || exit 1
|
||||||
|
@ -14,7 +14,8 @@ fi
|
||||||
|
|
||||||
cd winbuild
|
cd winbuild
|
||||||
|
|
||||||
x86_64-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Werror" .. || exit 1
|
# TODO: potential Arch-ism?
|
||||||
|
x86_64-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Werror" .. || exit 1
|
||||||
make -j8 || exit 1
|
make -j8 || exit 1
|
||||||
x86_64-w64-mingw32-strip -s furnace.exe || exit 1
|
x86_64-w64-mingw32-strip -s furnace.exe || exit 1
|
||||||
|
|
||||||
|
@ -30,3 +31,7 @@ cp -r ../../papers papers || exit 1
|
||||||
cp -r ../../demos demos || exit 1
|
cp -r ../../demos demos || exit 1
|
||||||
|
|
||||||
zip -r furnace.zip LICENSE.txt furnace.exe README.txt papers demos
|
zip -r furnace.zip LICENSE.txt furnace.exe README.txt papers demos
|
||||||
|
|
||||||
|
furName=$(git describe --tags | sed "s/v0/0/")
|
||||||
|
|
||||||
|
mv furnace.zip furnace-"$furName"-win64.zip
|
||||||
|
|
|
@ -110,6 +110,13 @@ enum DivDispatchCmds {
|
||||||
DIV_CMD_QSOUND_ECHO_DELAY,
|
DIV_CMD_QSOUND_ECHO_DELAY,
|
||||||
DIV_CMD_QSOUND_ECHO_LEVEL,
|
DIV_CMD_QSOUND_ECHO_LEVEL,
|
||||||
|
|
||||||
|
DIV_CMD_X1_010_ENVELOPE_SHAPE,
|
||||||
|
DIV_CMD_X1_010_ENVELOPE_ENABLE,
|
||||||
|
DIV_CMD_X1_010_ENVELOPE_MODE,
|
||||||
|
DIV_CMD_X1_010_ENVELOPE_PERIOD,
|
||||||
|
DIV_CMD_X1_010_ENVELOPE_SLIDE,
|
||||||
|
DIV_CMD_X1_010_AUTO_ENVELOPE,
|
||||||
|
|
||||||
DIV_CMD_WS_SWEEP_TIME,
|
DIV_CMD_WS_SWEEP_TIME,
|
||||||
DIV_CMD_WS_SWEEP_AMOUNT,
|
DIV_CMD_WS_SWEEP_AMOUNT,
|
||||||
|
|
||||||
|
|
|
@ -41,9 +41,11 @@
|
||||||
#include "platform/pcspkr.h"
|
#include "platform/pcspkr.h"
|
||||||
#include "platform/segapcm.h"
|
#include "platform/segapcm.h"
|
||||||
#include "platform/qsound.h"
|
#include "platform/qsound.h"
|
||||||
|
#include "platform/vera.h"
|
||||||
|
#include "platform/x1_010.h"
|
||||||
#include "platform/swan.h"
|
#include "platform/swan.h"
|
||||||
#include "platform/dummy.h"
|
|
||||||
#include "platform/lynx.h"
|
#include "platform/lynx.h"
|
||||||
|
#include "platform/dummy.h"
|
||||||
#include "../ta-log.h"
|
#include "../ta-log.h"
|
||||||
#include "song.h"
|
#include "song.h"
|
||||||
|
|
||||||
|
@ -237,7 +239,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
||||||
((DivPlatformOPL*)dispatch)->setOPLType(3,true);
|
((DivPlatformOPL*)dispatch)->setOPLType(3,true);
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_SAA1099: {
|
case DIV_SYSTEM_SAA1099: {
|
||||||
int saaCore=eng->getConfInt("saaCore",0);
|
int saaCore=eng->getConfInt("saaCore",1);
|
||||||
if (saaCore<0 || saaCore>2) saaCore=0;
|
if (saaCore<0 || saaCore>2) saaCore=0;
|
||||||
dispatch=new DivPlatformSAA1099;
|
dispatch=new DivPlatformSAA1099;
|
||||||
((DivPlatformSAA1099*)dispatch)->setCore((DivSAACores)saaCore);
|
((DivPlatformSAA1099*)dispatch)->setCore((DivSAACores)saaCore);
|
||||||
|
@ -256,9 +258,15 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
||||||
case DIV_SYSTEM_SEGAPCM_COMPAT:
|
case DIV_SYSTEM_SEGAPCM_COMPAT:
|
||||||
dispatch=new DivPlatformSegaPCM;
|
dispatch=new DivPlatformSegaPCM;
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
|
dispatch=new DivPlatformX1_010;
|
||||||
|
break;
|
||||||
case DIV_SYSTEM_SWAN:
|
case DIV_SYSTEM_SWAN:
|
||||||
dispatch=new DivPlatformSwan;
|
dispatch=new DivPlatformSwan;
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_VERA:
|
||||||
|
dispatch=new DivPlatformVERA;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
logW("this system is not supported yet! using dummy platform.\n");
|
logW("this system is not supported yet! using dummy platform.\n");
|
||||||
dispatch=new DivPlatformDummy;
|
dispatch=new DivPlatformDummy;
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
#include "dataErrors.h"
|
#include "dataErrors.h"
|
||||||
#include "song.h"
|
#include "song.h"
|
||||||
#include <cstddef>
|
|
||||||
#define _USE_MATH_DEFINES
|
#define _USE_MATH_DEFINES
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
#include "instrument.h"
|
#include "instrument.h"
|
||||||
|
@ -547,6 +546,36 @@ void DivEngine::renderSamples() {
|
||||||
memPos+=length+16;
|
memPos+=length+16;
|
||||||
}
|
}
|
||||||
qsoundMemLen=memPos+256;
|
qsoundMemLen=memPos+256;
|
||||||
|
|
||||||
|
// step 4: allocate x1-010 pcm samples
|
||||||
|
if (x1_010Mem==NULL) x1_010Mem=new unsigned char[1048576];
|
||||||
|
memset(x1_010Mem,0,1048576);
|
||||||
|
|
||||||
|
memPos=0;
|
||||||
|
for (int i=0; i<song.sampleLen; i++) {
|
||||||
|
DivSample* s=song.sample[i];
|
||||||
|
int paddedLen=(s->length8+4095)&(~0xfff);
|
||||||
|
// fit sample bank size to 128KB for Seta 2 external bankswitching logic (not emulated yet!)
|
||||||
|
if (paddedLen>131072) {
|
||||||
|
paddedLen=131072;
|
||||||
|
}
|
||||||
|
if ((memPos&0xfe0000)!=((memPos+paddedLen)&0xfe0000)) {
|
||||||
|
memPos=(memPos+0x1ffff)&0xfe0000;
|
||||||
|
}
|
||||||
|
if (memPos>=1048576) {
|
||||||
|
logW("out of X1-010 memory for sample %d!\n",i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (memPos+paddedLen>=1048576) {
|
||||||
|
memcpy(x1_010Mem+memPos,s->data8,1048576-memPos);
|
||||||
|
logW("out of X1-010 memory for sample %d!\n",i);
|
||||||
|
} else {
|
||||||
|
memcpy(x1_010Mem+memPos,s->data8,paddedLen);
|
||||||
|
}
|
||||||
|
s->offX1_010=memPos;
|
||||||
|
memPos+=paddedLen;
|
||||||
|
}
|
||||||
|
x1_010MemLen=memPos+256;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivEngine::createNew(const int* description) {
|
void DivEngine::createNew(const int* description) {
|
||||||
|
@ -969,6 +998,10 @@ int DivEngine::getEffectiveSampleRate(int rate) {
|
||||||
return (24038*MIN(65535,(rate*4096/24038)))/4096;
|
return (24038*MIN(65535,(rate*4096/24038)))/4096;
|
||||||
case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_FULL: case DIV_SYSTEM_YM2610_FULL_EXT: case DIV_SYSTEM_YM2610B: case DIV_SYSTEM_YM2610B_EXT:
|
case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_FULL: case DIV_SYSTEM_YM2610_FULL_EXT: case DIV_SYSTEM_YM2610B: case DIV_SYSTEM_YM2610B_EXT:
|
||||||
return 18518;
|
return 18518;
|
||||||
|
case DIV_SYSTEM_VERA:
|
||||||
|
return (48828*MIN(128,(rate*128/48828)))/128;
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
|
return (31250*MIN(255,(rate*16/31250)))/16; // TODO: support variable clock case
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,8 @@
|
||||||
warnings+=(String("\n")+x); \
|
warnings+=(String("\n")+x); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DIV_VERSION "dev63"
|
#define DIV_VERSION "dev66"
|
||||||
#define DIV_ENGINE_VERSION 63
|
#define DIV_ENGINE_VERSION 66
|
||||||
|
|
||||||
enum DivStatusView {
|
enum DivStatusView {
|
||||||
DIV_STATUS_NOTHING=0,
|
DIV_STATUS_NOTHING=0,
|
||||||
|
@ -69,7 +69,7 @@ enum DivHaltPositions {
|
||||||
|
|
||||||
struct DivChannelState {
|
struct DivChannelState {
|
||||||
std::vector<DivDelayedCommand> delayed;
|
std::vector<DivDelayedCommand> delayed;
|
||||||
int note, oldNote, pitch, portaSpeed, portaNote;
|
int note, oldNote, lastIns, pitch, portaSpeed, portaNote;
|
||||||
int volume, volSpeed, cut, rowDelay, volMax;
|
int volume, volSpeed, cut, rowDelay, volMax;
|
||||||
int delayOrder, delayRow, retrigSpeed, retrigTick;
|
int delayOrder, delayRow, retrigSpeed, retrigTick;
|
||||||
int vibratoDepth, vibratoRate, vibratoPos, vibratoDir, vibratoFine;
|
int vibratoDepth, vibratoRate, vibratoPos, vibratoDir, vibratoFine;
|
||||||
|
@ -80,6 +80,7 @@ struct DivChannelState {
|
||||||
DivChannelState():
|
DivChannelState():
|
||||||
note(-1),
|
note(-1),
|
||||||
oldNote(-1),
|
oldNote(-1),
|
||||||
|
lastIns(-1),
|
||||||
pitch(0),
|
pitch(0),
|
||||||
portaSpeed(-1),
|
portaSpeed(-1),
|
||||||
portaNote(-1),
|
portaNote(-1),
|
||||||
|
@ -633,6 +634,8 @@ class DivEngine {
|
||||||
size_t qsoundAMemLen;
|
size_t qsoundAMemLen;
|
||||||
unsigned char* dpcmMem;
|
unsigned char* dpcmMem;
|
||||||
size_t dpcmMemLen;
|
size_t dpcmMemLen;
|
||||||
|
unsigned char* x1_010Mem;
|
||||||
|
size_t x1_010MemLen;
|
||||||
|
|
||||||
DivEngine():
|
DivEngine():
|
||||||
output(NULL),
|
output(NULL),
|
||||||
|
|
|
@ -140,6 +140,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
ds.algMacroBehavior=false;
|
ds.algMacroBehavior=false;
|
||||||
ds.brokenShortcutSlides=false;
|
ds.brokenShortcutSlides=false;
|
||||||
ds.ignoreDuplicateSlides=true;
|
ds.ignoreDuplicateSlides=true;
|
||||||
|
ds.brokenDACMode=true;
|
||||||
|
ds.oneTickCut=false;
|
||||||
|
ds.newInsTriggersInPorta=true;
|
||||||
|
|
||||||
// 1.1 compat flags
|
// 1.1 compat flags
|
||||||
if (ds.version>24) {
|
if (ds.version>24) {
|
||||||
|
@ -799,6 +802,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
if (ds.version<62) {
|
if (ds.version<62) {
|
||||||
ds.stopPortaOnNoteOff=true;
|
ds.stopPortaOnNoteOff=true;
|
||||||
}
|
}
|
||||||
|
if (ds.version<64) {
|
||||||
|
ds.brokenDACMode=false;
|
||||||
|
}
|
||||||
|
if (ds.version<65) {
|
||||||
|
ds.oneTickCut=false;
|
||||||
|
}
|
||||||
|
if (ds.version<66) {
|
||||||
|
ds.newInsTriggersInPorta=false;
|
||||||
|
}
|
||||||
ds.isDMF=false;
|
ds.isDMF=false;
|
||||||
|
|
||||||
reader.readS(); // reserved
|
reader.readS(); // reserved
|
||||||
|
@ -975,7 +987,22 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
reader.readC();
|
reader.readC();
|
||||||
reader.readC();
|
reader.readC();
|
||||||
}
|
}
|
||||||
for (int i=0; i<4; i++) reader.readC();
|
if (ds.version>=64) {
|
||||||
|
ds.brokenDACMode=reader.readC();
|
||||||
|
} else {
|
||||||
|
reader.readC();
|
||||||
|
}
|
||||||
|
if (ds.version>=65) {
|
||||||
|
ds.oneTickCut=reader.readC();
|
||||||
|
} else {
|
||||||
|
reader.readC();
|
||||||
|
}
|
||||||
|
if (ds.version>=66) {
|
||||||
|
ds.newInsTriggersInPorta=reader.readC();
|
||||||
|
} else {
|
||||||
|
reader.readC();
|
||||||
|
}
|
||||||
|
for (int i=0; i<1; i++) reader.readC();
|
||||||
} else {
|
} else {
|
||||||
for (int i=0; i<20; i++) reader.readC();
|
for (int i=0; i<20; i++) reader.readC();
|
||||||
}
|
}
|
||||||
|
@ -1746,7 +1773,10 @@ SafeWriter* DivEngine::saveFur() {
|
||||||
w->writeC(song.ignoreDuplicateSlides);
|
w->writeC(song.ignoreDuplicateSlides);
|
||||||
w->writeC(song.stopPortaOnNoteOff);
|
w->writeC(song.stopPortaOnNoteOff);
|
||||||
w->writeC(song.continuousVibrato);
|
w->writeC(song.continuousVibrato);
|
||||||
for (int i=0; i<4; i++) {
|
w->writeC(song.brokenDACMode);
|
||||||
|
w->writeC(song.oneTickCut);
|
||||||
|
w->writeC(song.newInsTriggersInPorta);
|
||||||
|
for (int i=0; i<1; i++) {
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,9 @@ enum DivInstrumentType {
|
||||||
DIV_INS_BEEPER=21,
|
DIV_INS_BEEPER=21,
|
||||||
DIV_INS_SWAN=22,
|
DIV_INS_SWAN=22,
|
||||||
DIV_INS_MIKEY=23,
|
DIV_INS_MIKEY=23,
|
||||||
|
DIV_INS_VERA=24,
|
||||||
|
DIV_INS_X1_010=25,
|
||||||
|
DIV_INS_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
// FM operator structure:
|
// FM operator structure:
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(regRemap(a),v); if (dumpWrites) {addWrite(regRemap(a),v);} }
|
||||||
|
|
||||||
#define CHIP_DIVIDER 8
|
#define CHIP_DIVIDER 8
|
||||||
|
|
||||||
|
@ -48,8 +48,28 @@ const char* regCheatSheetAY[]={
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char* regCheatSheetAY8914[]={
|
||||||
|
"FreqL_A", "0",
|
||||||
|
"FreqL_B", "1",
|
||||||
|
"FreqL_C", "2",
|
||||||
|
"FreqL_Env", "3",
|
||||||
|
"FreqH_A", "4",
|
||||||
|
"FreqH_B", "5",
|
||||||
|
"FreqH_C", "6",
|
||||||
|
"FreqH_Env", "7",
|
||||||
|
"Enable", "8",
|
||||||
|
"FreqNoise", "9",
|
||||||
|
"Control_Env", "A",
|
||||||
|
"Volume_A", "B",
|
||||||
|
"Volume_B", "C",
|
||||||
|
"Volume_C", "D",
|
||||||
|
"PortA", "E",
|
||||||
|
"PortB", "F",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
const char** DivPlatformAY8910::getRegisterSheet() {
|
const char** DivPlatformAY8910::getRegisterSheet() {
|
||||||
return regCheatSheetAY;
|
return intellivision?regCheatSheetAY8914:regCheatSheetAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* DivPlatformAY8910::getEffectName(unsigned char effect) {
|
const char* DivPlatformAY8910::getEffectName(unsigned char effect) {
|
||||||
|
@ -92,8 +112,13 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l
|
||||||
}
|
}
|
||||||
while (!writes.empty()) {
|
while (!writes.empty()) {
|
||||||
QueuedWrite w=writes.front();
|
QueuedWrite w=writes.front();
|
||||||
|
if (intellivision) {
|
||||||
|
ay8914_device* ay8914=(ay8914_device*)ay;
|
||||||
|
ay8914->write(w.addr,w.val);
|
||||||
|
} else {
|
||||||
ay->address_w(w.addr);
|
ay->address_w(w.addr);
|
||||||
ay->data_w(w.val);
|
ay->data_w(w.val);
|
||||||
|
}
|
||||||
regPool[w.addr&0x0f]=w.val;
|
regPool[w.addr&0x0f]=w.val;
|
||||||
writes.pop();
|
writes.pop();
|
||||||
}
|
}
|
||||||
|
@ -125,6 +150,8 @@ void DivPlatformAY8910::tick() {
|
||||||
if (chan[i].outVol<0) chan[i].outVol=0;
|
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||||
if (isMuted[i]) {
|
if (isMuted[i]) {
|
||||||
rWrite(0x08+i,0);
|
rWrite(0x08+i,0);
|
||||||
|
} else if (intellivision && (chan[i].psgMode&4)) {
|
||||||
|
rWrite(0x08+i,(chan[i].outVol&0xc)<<2);
|
||||||
} else {
|
} else {
|
||||||
rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
|
rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
|
||||||
}
|
}
|
||||||
|
@ -151,6 +178,8 @@ void DivPlatformAY8910::tick() {
|
||||||
chan[i].psgMode=(chan[i].std.wave+1)&7;
|
chan[i].psgMode=(chan[i].std.wave+1)&7;
|
||||||
if (isMuted[i]) {
|
if (isMuted[i]) {
|
||||||
rWrite(0x08+i,0);
|
rWrite(0x08+i,0);
|
||||||
|
} else if (intellivision && (chan[i].psgMode&4)) {
|
||||||
|
rWrite(0x08+i,(chan[i].outVol&0xc)<<2);
|
||||||
} else {
|
} else {
|
||||||
rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
|
rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
|
||||||
}
|
}
|
||||||
|
@ -242,6 +271,8 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
||||||
chan[c.chan].std.init(ins);
|
chan[c.chan].std.init(ins);
|
||||||
if (isMuted[c.chan]) {
|
if (isMuted[c.chan]) {
|
||||||
rWrite(0x08+c.chan,0);
|
rWrite(0x08+c.chan,0);
|
||||||
|
} else if (intellivision && (chan[c.chan].psgMode&4)) {
|
||||||
|
rWrite(0x08+c.chan,(chan[c.chan].vol&0xc)<<2);
|
||||||
} else {
|
} else {
|
||||||
rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||||
}
|
}
|
||||||
|
@ -264,7 +295,13 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
||||||
if (isMuted[c.chan]) {
|
if (isMuted[c.chan]) {
|
||||||
rWrite(0x08+c.chan,0);
|
rWrite(0x08+c.chan,0);
|
||||||
} else {
|
} else {
|
||||||
if (chan[c.chan].active) rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
if (chan[c.chan].active) {
|
||||||
|
if (intellivision && (chan[c.chan].psgMode&4)) {
|
||||||
|
rWrite(0x08+c.chan,(chan[c.chan].vol&0xc)<<2);
|
||||||
|
} else {
|
||||||
|
rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -317,9 +354,13 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
||||||
if (isMuted[c.chan]) {
|
if (isMuted[c.chan]) {
|
||||||
rWrite(0x08+c.chan,0);
|
rWrite(0x08+c.chan,0);
|
||||||
} else if (chan[c.chan].active) {
|
} else if (chan[c.chan].active) {
|
||||||
|
if (intellivision && (chan[c.chan].psgMode&4)) {
|
||||||
|
rWrite(0x08+c.chan,(chan[c.chan].outVol&0xc)<<2);
|
||||||
|
} else {
|
||||||
rWrite(0x08+c.chan,(chan[c.chan].outVol&15)|((chan[c.chan].psgMode&4)<<2));
|
rWrite(0x08+c.chan,(chan[c.chan].outVol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_STD_NOISE_FREQ:
|
case DIV_CMD_STD_NOISE_FREQ:
|
||||||
rWrite(0x06,31-c.value);
|
rWrite(0x06,31-c.value);
|
||||||
|
@ -334,6 +375,8 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
if (isMuted[c.chan]) {
|
if (isMuted[c.chan]) {
|
||||||
rWrite(0x08+c.chan,0);
|
rWrite(0x08+c.chan,0);
|
||||||
|
} else if (intellivision && (chan[c.chan].psgMode&4)) {
|
||||||
|
rWrite(0x08+c.chan,(chan[c.chan].vol&0xc)<<2);
|
||||||
} else {
|
} else {
|
||||||
rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||||
}
|
}
|
||||||
|
@ -383,6 +426,8 @@ void DivPlatformAY8910::muteChannel(int ch, bool mute) {
|
||||||
isMuted[ch]=mute;
|
isMuted[ch]=mute;
|
||||||
if (isMuted[ch]) {
|
if (isMuted[ch]) {
|
||||||
rWrite(0x08+ch,0);
|
rWrite(0x08+ch,0);
|
||||||
|
} else if (intellivision && (chan[ch].psgMode&4)) {
|
||||||
|
rWrite(0x08+ch,(chan[ch].vol&0xc)<<2);
|
||||||
} else {
|
} else {
|
||||||
rWrite(0x08+ch,(chan[ch].outVol&15)|((chan[ch].psgMode&4)<<2));
|
rWrite(0x08+ch,(chan[ch].outVol&15)|((chan[ch].psgMode&4)<<2));
|
||||||
}
|
}
|
||||||
|
@ -508,14 +553,22 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
|
||||||
case 1:
|
case 1:
|
||||||
ay=new ym2149_device(rate);
|
ay=new ym2149_device(rate);
|
||||||
sunsoft=false;
|
sunsoft=false;
|
||||||
|
intellivision=false;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
ay=new sunsoft_5b_sound_device(rate);
|
ay=new sunsoft_5b_sound_device(rate);
|
||||||
sunsoft=true;
|
sunsoft=true;
|
||||||
|
intellivision=false;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
ay=new ay8914_device(rate);
|
||||||
|
sunsoft=false;
|
||||||
|
intellivision=true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ay=new ay8910_device(rate);
|
ay=new ay8910_device(rate);
|
||||||
sunsoft=false;
|
sunsoft=false;
|
||||||
|
intellivision=false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ay->device_start();
|
ay->device_start();
|
||||||
|
|
|
@ -26,6 +26,10 @@
|
||||||
|
|
||||||
class DivPlatformAY8910: public DivDispatch {
|
class DivPlatformAY8910: public DivDispatch {
|
||||||
protected:
|
protected:
|
||||||
|
const unsigned char AY8914RegRemap[16]={
|
||||||
|
0,4,1,5,2,6,9,8,11,12,13,3,7,10,14,15
|
||||||
|
};
|
||||||
|
inline unsigned char regRemap(unsigned char reg) { return intellivision?AY8914RegRemap[reg&0x0f]:reg&0x0f; }
|
||||||
struct Channel {
|
struct Channel {
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, note, pitch;
|
int freq, baseFreq, note, pitch;
|
||||||
|
@ -60,7 +64,7 @@ class DivPlatformAY8910: public DivDispatch {
|
||||||
int delay;
|
int delay;
|
||||||
|
|
||||||
bool extMode;
|
bool extMode;
|
||||||
bool stereo, sunsoft;
|
bool stereo, sunsoft, intellivision;
|
||||||
|
|
||||||
short oldWrites[16];
|
short oldWrites[16];
|
||||||
short pendingWrites[16];
|
short pendingWrites[16];
|
||||||
|
|
|
@ -509,10 +509,17 @@ void DivPlatformC64::setChipModel(bool is6581) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformC64::setFlags(unsigned int flags) {
|
void DivPlatformC64::setFlags(unsigned int flags) {
|
||||||
if (flags&1) {
|
switch (flags&0xf) {
|
||||||
rate=COLOR_PAL*2.0/9.0;
|
case 0x0: // NTSC C64
|
||||||
} else {
|
|
||||||
rate=COLOR_NTSC*2.0/7.0;
|
rate=COLOR_NTSC*2.0/7.0;
|
||||||
|
break;
|
||||||
|
case 0x1: // PAL C64
|
||||||
|
rate=COLOR_PAL*2.0/9.0;
|
||||||
|
break;
|
||||||
|
case 0x2: // SSI 2001
|
||||||
|
default:
|
||||||
|
rate=14318180.0/16.0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
chipClock=rate;
|
chipClock=rate;
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,8 +97,12 @@ void DivPlatformGB::updateWave() {
|
||||||
if (wt->max<1 || wt->len<1) {
|
if (wt->max<1 || wt->len<1) {
|
||||||
rWrite(0x30+i,0);
|
rWrite(0x30+i,0);
|
||||||
} else {
|
} else {
|
||||||
unsigned char nibble1=15-((wt->data[(i*2)*wt->len/32]*15)/wt->max);
|
int nibble1=15-((wt->data[(i*2)*wt->len/32]*15)/wt->max);
|
||||||
unsigned char nibble2=15-((wt->data[(1+i*2)*wt->len/32]*15)/wt->max);
|
int nibble2=15-((wt->data[(1+i*2)*wt->len/32]*15)/wt->max);
|
||||||
|
if (nibble1<0) nibble1=0;
|
||||||
|
if (nibble1>15) nibble1=15;
|
||||||
|
if (nibble2<0) nibble2=0;
|
||||||
|
if (nibble2>15) nibble2=15;
|
||||||
rWrite(0x30+i,(nibble1<<4)|nibble2);
|
rWrite(0x30+i,(nibble1<<4)|nibble2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,13 +92,16 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s
|
||||||
DivSample* s=parent->getSample(dacSample);
|
DivSample* s=parent->getSample(dacSample);
|
||||||
if (s->samples>0) {
|
if (s->samples>0) {
|
||||||
if (!isMuted[5]) {
|
if (!isMuted[5]) {
|
||||||
immWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
||||||
}
|
}
|
||||||
if (++dacPos>=s->samples) {
|
if (++dacPos>=s->samples) {
|
||||||
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
|
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
|
||||||
dacPos=s->loopStart;
|
dacPos=s->loopStart;
|
||||||
} else {
|
} else {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
|
if (parent->song.brokenDACMode) {
|
||||||
|
rWrite(0x2b,0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dacPeriod+=MAX(40,dacRate);
|
dacPeriod+=MAX(40,dacRate);
|
||||||
|
@ -118,7 +121,7 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s
|
||||||
//printf("write: %x = %.2x\n",w.addr,w.val);
|
//printf("write: %x = %.2x\n",w.addr,w.val);
|
||||||
lastBusy=0;
|
lastBusy=0;
|
||||||
regPool[w.addr&0x1ff]=w.val;
|
regPool[w.addr&0x1ff]=w.val;
|
||||||
writes.pop();
|
writes.pop_front();
|
||||||
} else {
|
} else {
|
||||||
lastBusy++;
|
lastBusy++;
|
||||||
if (fm.write_busy==0) {
|
if (fm.write_busy==0) {
|
||||||
|
@ -156,13 +159,16 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
|
||||||
DivSample* s=parent->getSample(dacSample);
|
DivSample* s=parent->getSample(dacSample);
|
||||||
if (s->samples>0) {
|
if (s->samples>0) {
|
||||||
if (!isMuted[5]) {
|
if (!isMuted[5]) {
|
||||||
immWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
||||||
}
|
}
|
||||||
if (++dacPos>=s->samples) {
|
if (++dacPos>=s->samples) {
|
||||||
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
|
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
|
||||||
dacPos=s->loopStart;
|
dacPos=s->loopStart;
|
||||||
} else {
|
} else {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
|
if (parent->song.brokenDACMode) {
|
||||||
|
rWrite(0x2b,0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dacPeriod+=MAX(40,dacRate);
|
dacPeriod+=MAX(40,dacRate);
|
||||||
|
@ -178,7 +184,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
|
||||||
fm_ymfm->write(0x0+((w.addr>>8)<<1),w.addr);
|
fm_ymfm->write(0x0+((w.addr>>8)<<1),w.addr);
|
||||||
fm_ymfm->write(0x1+((w.addr>>8)<<1),w.val);
|
fm_ymfm->write(0x1+((w.addr>>8)<<1),w.val);
|
||||||
regPool[w.addr&0x1ff]=w.val;
|
regPool[w.addr&0x1ff]=w.val;
|
||||||
writes.pop();
|
writes.pop_front();
|
||||||
lastBusy=1;
|
lastBusy=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,6 +466,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
if (dumpWrites) addWrite(0xffff0002,0);
|
if (dumpWrites) addWrite(0xffff0002,0);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
rWrite(0x2b,1<<7);
|
||||||
if (dumpWrites) addWrite(0xffff0000,dacSample);
|
if (dumpWrites) addWrite(0xffff0000,dacSample);
|
||||||
}
|
}
|
||||||
dacPos=0;
|
dacPos=0;
|
||||||
|
@ -477,6 +484,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
if (dumpWrites) addWrite(0xffff0002,0);
|
if (dumpWrites) addWrite(0xffff0002,0);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
rWrite(0x2b,1<<7);
|
||||||
if (dumpWrites) addWrite(0xffff0000,dacSample);
|
if (dumpWrites) addWrite(0xffff0000,dacSample);
|
||||||
}
|
}
|
||||||
dacPos=0;
|
dacPos=0;
|
||||||
|
@ -541,6 +549,10 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
if (c.chan==5) {
|
if (c.chan==5) {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
if (dumpWrites) addWrite(0xffff0002,0);
|
if (dumpWrites) addWrite(0xffff0002,0);
|
||||||
|
if (parent->song.brokenDACMode) {
|
||||||
|
rWrite(0x2b,0);
|
||||||
|
if (dacMode) break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
chan[c.chan].keyOff=true;
|
chan[c.chan].keyOff=true;
|
||||||
chan[c.chan].keyOn=false;
|
chan[c.chan].keyOn=false;
|
||||||
|
@ -770,7 +782,7 @@ int DivPlatformGenesis::getRegisterPoolSize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformGenesis::reset() {
|
void DivPlatformGenesis::reset() {
|
||||||
while (!writes.empty()) writes.pop();
|
while (!writes.empty()) writes.pop_front();
|
||||||
memset(regPool,0,512);
|
memset(regPool,0,512);
|
||||||
if (useYMFM) {
|
if (useYMFM) {
|
||||||
fm_ymfm->reset();
|
fm_ymfm->reset();
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#ifndef _GENESIS_H
|
#ifndef _GENESIS_H
|
||||||
#define _GENESIS_H
|
#define _GENESIS_H
|
||||||
#include "../dispatch.h"
|
#include "../dispatch.h"
|
||||||
#include <queue>
|
#include <deque>
|
||||||
#include "../../../extern/Nuked-OPN2/ym3438.h"
|
#include "../../../extern/Nuked-OPN2/ym3438.h"
|
||||||
#include "sound/ymfm/ymfm_opn.h"
|
#include "sound/ymfm/ymfm_opn.h"
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ class DivPlatformGenesis: public DivDispatch {
|
||||||
bool addrOrVal;
|
bool addrOrVal;
|
||||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||||
};
|
};
|
||||||
std::queue<QueuedWrite> writes;
|
std::deque<QueuedWrite> writes;
|
||||||
ym3438_t fm;
|
ym3438_t fm;
|
||||||
int delay;
|
int delay;
|
||||||
unsigned char lastBusy;
|
unsigned char lastBusy;
|
||||||
|
|
|
@ -43,6 +43,7 @@ static int orderedOps[4]={
|
||||||
};
|
};
|
||||||
|
|
||||||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
#define immWrite(a,v) if (!skipRegisterWrites) {writes.push_back(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
|
||||||
|
#define urgentWrite(a,v) if (!skipRegisterWrites) {if (writes.front().addrOrVal) {writes.push_back(QueuedWrite(a,v));} else {writes.push_front(QueuedWrite(a,v));}; if (dumpWrites) {addWrite(a,v);} }
|
||||||
|
|
||||||
#include "fmshared_OPN.h"
|
#include "fmshared_OPN.h"
|
||||||
|
|
|
@ -82,7 +82,44 @@ static int32_t clamp(int32_t v, int32_t lo, int32_t hi)
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* regCheatSheetLynx[]={
|
const char* regCheatSheetLynx[]={
|
||||||
"DATA", "0",
|
"AUDIO0_VOLCNTRL", "20",
|
||||||
|
"AUDIO0_FEEDBACK", "21",
|
||||||
|
"AUDIO0_OUTPUT", "22",
|
||||||
|
"AUDIO0_SHIFT", "23",
|
||||||
|
"AUDIO0_BACKUP", "24",
|
||||||
|
"AUDIO0_CONTROL", "25",
|
||||||
|
"AUDIO0_COUNTER", "26",
|
||||||
|
"AUDIO0_OTHER", "27",
|
||||||
|
"AUDIO1_VOLCNTRL", "28",
|
||||||
|
"AUDIO1_FEEDBACK", "29",
|
||||||
|
"AUDIO1_OUTPUT", "2a",
|
||||||
|
"AUDIO1_SHIFT", "2b",
|
||||||
|
"AUDIO1_BACKUP", "2c",
|
||||||
|
"AUDIO1_CONTROL", "2d",
|
||||||
|
"AUDIO1_COUNTER", "2e",
|
||||||
|
"AUDIO1_OTHER", "2f",
|
||||||
|
"AUDIO2_VOLCNTRL", "30",
|
||||||
|
"AUDIO2_FEEDBACK", "31",
|
||||||
|
"AUDIO2_OUTPUT", "32",
|
||||||
|
"AUDIO2_SHIFT", "33",
|
||||||
|
"AUDIO2_BACKUP", "34",
|
||||||
|
"AUDIO2_CONTROL", "35",
|
||||||
|
"AUDIO2_COUNTER", "36",
|
||||||
|
"AUDIO2_OTHER", "37",
|
||||||
|
"AUDIO3_VOLCNTRL", "38",
|
||||||
|
"AUDIO3_FEEDBACK", "39",
|
||||||
|
"AUDIO3_OUTPUT", "3a",
|
||||||
|
"AUDIO3_SHIFT", "3b",
|
||||||
|
"AUDIO3_BACKUP", "3c",
|
||||||
|
"AUDIO3_CONTROL", "3d",
|
||||||
|
"AUDIO3_COUNTER", "3e",
|
||||||
|
"AUDIO3_OTHER", "3f",
|
||||||
|
"ATTENREG0", "40",
|
||||||
|
"ATTENREG1", "41",
|
||||||
|
"ATTENREG2", "42",
|
||||||
|
"ATTENREG3", "43",
|
||||||
|
"MPAN", "44",
|
||||||
|
"MSTEREO", "50",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -198,7 +235,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_PANNING:
|
case DIV_CMD_PANNING:
|
||||||
chan[c.chan].pan=((c.value&0x0f)<<4)|((c.value&0xf0)>>4);
|
chan[c.chan].pan=c.value;
|
||||||
WRITE_ATTEN(c.chan,chan[c.chan].pan);
|
WRITE_ATTEN(c.chan,chan[c.chan].pan);
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_GET_VOLUME:
|
case DIV_CMD_GET_VOLUME:
|
||||||
|
|
|
@ -201,6 +201,7 @@ void DivPlatformNES::tick() {
|
||||||
} else {
|
} else {
|
||||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1;
|
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1;
|
||||||
if (chan[i].freq>2047) chan[i].freq=2047;
|
if (chan[i].freq>2047) chan[i].freq=2047;
|
||||||
|
if (chan[i].freq<0) chan[i].freq=0;
|
||||||
}
|
}
|
||||||
if (chan[i].keyOn) {
|
if (chan[i].keyOn) {
|
||||||
//rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63)));
|
//rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63)));
|
||||||
|
|
|
@ -222,7 +222,7 @@ void DivPlatformOPL::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformOPL::tick() {
|
void DivPlatformOPL::tick() {
|
||||||
for (int i=0; i<20; i++) {
|
for (int i=0; i<melodicChans; i++) {
|
||||||
chan[i].std.next();
|
chan[i].std.next();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -349,10 +349,22 @@ void DivPlatformOPL::tick() {
|
||||||
|
|
||||||
if (chan[i].keyOn || chan[i].keyOff) {
|
if (chan[i].keyOn || chan[i].keyOff) {
|
||||||
immWrite(chanMap[i]+ADDR_FREQH,0x00|(chan[i].freqH&31));
|
immWrite(chanMap[i]+ADDR_FREQH,0x00|(chan[i].freqH&31));
|
||||||
|
if (chan[i].state.ops==4 && i<6) {
|
||||||
|
immWrite(chanMap[i+1]+ADDR_FREQH,0x00|(chan[i].freqH&31));
|
||||||
|
}
|
||||||
chan[i].keyOff=false;
|
chan[i].keyOff=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (update4OpMask) {
|
||||||
|
update4OpMask=false;
|
||||||
|
if (oplType==3) {
|
||||||
|
unsigned char opMask=chan[0].fourOp|(chan[2].fourOp<<1)|(chan[4].fourOp<<2)|(chan[6].fourOp<<3)|(chan[8].fourOp<<4)|(chan[10].fourOp<<5);
|
||||||
|
immWrite(0x104,opMask);
|
||||||
|
//printf("updating opMask to %.2x\n",opMask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i=0; i<512; i++) {
|
for (int i=0; i<512; i++) {
|
||||||
if (pendingWrites[i]!=oldWrites[i]) {
|
if (pendingWrites[i]!=oldWrites[i]) {
|
||||||
immWrite(i,pendingWrites[i]&0xff);
|
immWrite(i,pendingWrites[i]&0xff);
|
||||||
|
@ -360,7 +372,7 @@ void DivPlatformOPL::tick() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<20; i++) {
|
for (int i=0; i<melodicChans; i++) {
|
||||||
if (chan[i].freqChanged) {
|
if (chan[i].freqChanged) {
|
||||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq));
|
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq));
|
||||||
if (chan[i].freq>131071) chan[i].freq=131071;
|
if (chan[i].freq>131071) chan[i].freq=131071;
|
||||||
|
@ -368,12 +380,21 @@ void DivPlatformOPL::tick() {
|
||||||
chan[i].freqH=freqt>>8;
|
chan[i].freqH=freqt>>8;
|
||||||
chan[i].freqL=freqt&0xff;
|
chan[i].freqL=freqt&0xff;
|
||||||
immWrite(chanMap[i]+ADDR_FREQ,chan[i].freqL);
|
immWrite(chanMap[i]+ADDR_FREQ,chan[i].freqL);
|
||||||
|
if (chan[i].state.ops==4 && i<6) {
|
||||||
|
immWrite(chanMap[i+1]+ADDR_FREQ,chan[i].freqL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (chan[i].keyOn) {
|
if (chan[i].keyOn) {
|
||||||
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(0x20));
|
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(0x20));
|
||||||
|
if (chan[i].state.ops==4 && i<6) {
|
||||||
|
immWrite(chanMap[i+1]+ADDR_FREQH,chan[i].freqH|(0x20));
|
||||||
|
}
|
||||||
chan[i].keyOn=false;
|
chan[i].keyOn=false;
|
||||||
} else if (chan[i].freqChanged) {
|
} else if (chan[i].freqChanged) {
|
||||||
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(chan[i].active<<5));
|
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(chan[i].active<<5));
|
||||||
|
if (chan[i].state.ops==4 && i<6) {
|
||||||
|
immWrite(chanMap[i+1]+ADDR_FREQH,chan[i].freqH|(chan[i].active<<5));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
chan[i].freqChanged=false;
|
chan[i].freqChanged=false;
|
||||||
}
|
}
|
||||||
|
@ -424,25 +445,42 @@ int DivPlatformOPL::toFreq(int freq) {
|
||||||
|
|
||||||
void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
||||||
isMuted[ch]=mute;
|
isMuted[ch]=mute;
|
||||||
/*
|
int ops=(slots[3][ch]!=255 && chan[ch].state.ops==4 && oplType==3)?4:2;
|
||||||
for (int j=0; j<4; j++) {
|
chan[ch].fourOp=(ops==4);
|
||||||
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
|
update4OpMask=true;
|
||||||
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
|
for (int i=0; i<ops; i++) {
|
||||||
|
unsigned char slot=slots[i][ch];
|
||||||
|
if (slot==255) continue;
|
||||||
|
unsigned short baseAddr=slotMap[slot];
|
||||||
|
DivInstrumentFM::Operator& op=chan[ch].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||||
|
|
||||||
if (isMuted[ch]) {
|
if (isMuted[ch]) {
|
||||||
rWrite(baseAddr+ADDR_TL,127);
|
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||||
} else {
|
} else {
|
||||||
if (isOutput[chan[ch].state.alg][j]) {
|
if (isOutputL[ops==4][chan[ch].state.alg][i]) {
|
||||||
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127));
|
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[ch].outVol&0x3f))/63))|(op.ksl<<6));
|
||||||
} else {
|
} else {
|
||||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
|
|
||||||
*/
|
if (isMuted[ch]) {
|
||||||
|
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
|
||||||
|
if (ops==4) {
|
||||||
|
rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&3)<<4));
|
||||||
|
if (ops==4) {
|
||||||
|
rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&3)<<4));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformOPL::dispatch(DivCommand c) {
|
int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
|
// TODO: drums mode!
|
||||||
|
if (c.chan>=melodicChans) return 0;
|
||||||
switch (c.cmd) {
|
switch (c.cmd) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||||
|
@ -456,7 +494,9 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
}
|
}
|
||||||
if (chan[c.chan].insChanged) {
|
if (chan[c.chan].insChanged) {
|
||||||
int ops=(slots[3][c.chan]!=255 && ins->fm.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
|
chan[c.chan].fourOp=(ops==4);
|
||||||
|
update4OpMask=true;
|
||||||
for (int i=0; i<ops; i++) {
|
for (int i=0; i<ops; i++) {
|
||||||
unsigned char slot=slots[i][c.chan];
|
unsigned char slot=slots[i][c.chan];
|
||||||
if (slot==255) continue;
|
if (slot==255) continue;
|
||||||
|
@ -524,21 +564,23 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
if (!chan[c.chan].std.hasVol) {
|
if (!chan[c.chan].std.hasVol) {
|
||||||
chan[c.chan].outVol=c.value;
|
chan[c.chan].outVol=c.value;
|
||||||
}
|
}
|
||||||
/*
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<ops; i++) {
|
||||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
unsigned char slot=slots[i][c.chan];
|
||||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
if (slot==255) continue;
|
||||||
|
unsigned short baseAddr=slotMap[slot];
|
||||||
|
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||||
|
|
||||||
if (isMuted[c.chan]) {
|
if (isMuted[c.chan]) {
|
||||||
rWrite(baseAddr+ADDR_TL,127);
|
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||||
} else {
|
} else {
|
||||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
if (isOutputL[ops==4][chan[c.chan].state.alg][i]) {
|
||||||
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
|
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
|
||||||
} else {
|
} else {
|
||||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_GET_VOLUME: {
|
case DIV_CMD_GET_VOLUME: {
|
||||||
|
@ -552,16 +594,22 @@ int DivPlatformOPL::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: {
|
||||||
switch (c.value) {
|
if (c.value==0) {
|
||||||
case 0x01:
|
|
||||||
chan[c.chan].pan=1;
|
|
||||||
break;
|
|
||||||
case 0x10:
|
|
||||||
chan[c.chan].pan=2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
chan[c.chan].pan=3;
|
chan[c.chan].pan=3;
|
||||||
break;
|
} else {
|
||||||
|
chan[c.chan].pan=(((c.value&15)>0)<<1)|((c.value>>4)>0);
|
||||||
|
}
|
||||||
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
|
if (isMuted[c.chan]) {
|
||||||
|
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1));
|
||||||
|
if (ops==4) {
|
||||||
|
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&3)<<4));
|
||||||
|
if (ops==4) {
|
||||||
|
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&3)<<4));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
|
//rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
|
||||||
break;
|
break;
|
||||||
|
@ -683,39 +731,46 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformOPL::forceIns() {
|
void DivPlatformOPL::forceIns() {
|
||||||
/*
|
for (int i=0; i<melodicChans; i++) {
|
||||||
for (int i=0; i<20; i++) {
|
int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2;
|
||||||
for (int j=0; j<4; j++) {
|
chan[i].fourOp=(ops==4);
|
||||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
for (int j=0; j<ops; j++) {
|
||||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
unsigned char slot=slots[j][i];
|
||||||
|
if (slot==255) continue;
|
||||||
|
unsigned short baseAddr=slotMap[slot];
|
||||||
|
DivInstrumentFM::Operator& op=chan[i].state.op[(ops==4)?orderedOpsL[j]:j];
|
||||||
|
|
||||||
if (isMuted[i]) {
|
if (isMuted[i]) {
|
||||||
rWrite(baseAddr+ADDR_TL,127);
|
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||||
} else {
|
} else {
|
||||||
if (isOutput[chan[i].state.alg][j]) {
|
if (isOutputL[ops==4][chan[i].state.alg][j]) {
|
||||||
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
|
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
|
||||||
} else {
|
} else {
|
||||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
|
||||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
|
||||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
|
||||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
if (oplType>1) {
|
||||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
|
||||||
}
|
|
||||||
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
|
|
||||||
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
|
||||||
if (chan[i].active) {
|
|
||||||
chan[i].keyOn=true;
|
|
||||||
chan[i].freqChanged=true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dacMode) {
|
|
||||||
rWrite(0x2b,0x80);
|
if (isMuted[i]) {
|
||||||
|
rWrite(chanMap[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&1)|(chan[i].state.fb<<1));
|
||||||
|
if (ops==4) {
|
||||||
|
rWrite(chanMap[i+1]+ADDR_LR_FB_ALG,((chan[i].state.alg>>1)&1)|(chan[i].state.fb<<1));
|
||||||
}
|
}
|
||||||
immWrite(0x22,lfoValue);
|
} else {
|
||||||
*/
|
rWrite(chanMap[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&1)|(chan[i].state.fb<<1)|((chan[i].pan&3)<<4));
|
||||||
|
if (ops==4) {
|
||||||
|
rWrite(chanMap[i+1]+ADDR_LR_FB_ALG,((chan[i].state.alg>>1)&1)|(chan[i].state.fb<<1)|((chan[i].pan&3)<<4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update4OpMask=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformOPL::toggleRegisterDump(bool enable) {
|
void DivPlatformOPL::toggleRegisterDump(bool enable) {
|
||||||
|
@ -746,7 +801,7 @@ void DivPlatformOPL::reset() {
|
||||||
if (dumpWrites) {
|
if (dumpWrites) {
|
||||||
addWrite(0xffffffff,0);
|
addWrite(0xffffffff,0);
|
||||||
}
|
}
|
||||||
for (int i=0; i<20; i++) {
|
for (int i=0; i<totalChans; i++) {
|
||||||
chan[i]=DivPlatformOPL::Channel();
|
chan[i]=DivPlatformOPL::Channel();
|
||||||
chan[i].vol=0x3f;
|
chan[i].vol=0x3f;
|
||||||
chan[i].outVol=0x3f;
|
chan[i].outVol=0x3f;
|
||||||
|
@ -761,10 +816,15 @@ void DivPlatformOPL::reset() {
|
||||||
lfoValue=8;
|
lfoValue=8;
|
||||||
properDrums=properDrumsSys;
|
properDrums=properDrumsSys;
|
||||||
|
|
||||||
|
if (oplType==1) { // disable waveforms
|
||||||
|
immWrite(0x01,0x20);
|
||||||
|
}
|
||||||
|
|
||||||
if (oplType==3) { // enable OPL3 features
|
if (oplType==3) { // enable OPL3 features
|
||||||
immWrite(0x105,1);
|
immWrite(0x105,1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update4OpMask=true;
|
||||||
delay=0;
|
delay=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,7 +841,7 @@ bool DivPlatformOPL::keyOffAffectsPorta(int ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformOPL::notifyInsChange(int ins) {
|
void DivPlatformOPL::notifyInsChange(int ins) {
|
||||||
for (int i=0; i<20; i++) {
|
for (int i=0; i<totalChans; i++) {
|
||||||
if (chan[i].ins==ins) {
|
if (chan[i].ins==ins) {
|
||||||
chan[i].insChanged=true;
|
chan[i].insChanged=true;
|
||||||
}
|
}
|
||||||
|
@ -815,6 +875,9 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
|
||||||
slots=drums?slotsDrums:slotsNonDrums;
|
slots=drums?slotsDrums:slotsNonDrums;
|
||||||
chanMap=chanMapOPL2;
|
chanMap=chanMapOPL2;
|
||||||
chipFreqBase=9440540*0.25;
|
chipFreqBase=9440540*0.25;
|
||||||
|
chans=9;
|
||||||
|
melodicChans=drums?6:9;
|
||||||
|
totalChans=drums?11:9;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
slotsNonDrums=slotsOPL3;
|
slotsNonDrums=slotsOPL3;
|
||||||
|
@ -822,6 +885,9 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
|
||||||
slots=drums?slotsDrums:slotsNonDrums;
|
slots=drums?slotsDrums:slotsNonDrums;
|
||||||
chanMap=chanMapOPL3;
|
chanMap=chanMapOPL3;
|
||||||
chipFreqBase=9440540;
|
chipFreqBase=9440540;
|
||||||
|
chans=18;
|
||||||
|
melodicChans=drums?15:18;
|
||||||
|
totalChans=drums?20:18;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
oplType=type;
|
oplType=type;
|
||||||
|
|
|
@ -32,7 +32,7 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, pitch, note;
|
int freq, baseFreq, pitch, note;
|
||||||
unsigned char ins;
|
unsigned char ins;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, fourOp;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
unsigned char pan;
|
unsigned char pan;
|
||||||
Channel():
|
Channel():
|
||||||
|
@ -51,6 +51,7 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
portaPause(false),
|
portaPause(false),
|
||||||
furnaceDac(false),
|
furnaceDac(false),
|
||||||
inPorta(false),
|
inPorta(false),
|
||||||
|
fourOp(false),
|
||||||
vol(0),
|
vol(0),
|
||||||
pan(3) {}
|
pan(3) {}
|
||||||
};
|
};
|
||||||
|
@ -69,7 +70,7 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
const unsigned char** slots;
|
const unsigned char** slots;
|
||||||
const unsigned short* chanMap;
|
const unsigned short* chanMap;
|
||||||
double chipFreqBase;
|
double chipFreqBase;
|
||||||
int delay, oplType;
|
int delay, oplType, chans, melodicChans, totalChans;
|
||||||
unsigned char lastBusy;
|
unsigned char lastBusy;
|
||||||
|
|
||||||
unsigned char regPool[512];
|
unsigned char regPool[512];
|
||||||
|
@ -78,7 +79,7 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
|
|
||||||
unsigned char lfoValue;
|
unsigned char lfoValue;
|
||||||
|
|
||||||
bool useYMFM;
|
bool useYMFM, update4OpMask;
|
||||||
|
|
||||||
short oldWrites[512];
|
short oldWrites[512];
|
||||||
short pendingWrites[512];
|
short pendingWrites[512];
|
||||||
|
|
|
@ -138,7 +138,10 @@ void DivPlatformPCE::updateWave(int ch) {
|
||||||
if (wt->max<1 || wt->len<1) {
|
if (wt->max<1 || wt->len<1) {
|
||||||
chWrite(ch,0x06,0);
|
chWrite(ch,0x06,0);
|
||||||
} else {
|
} else {
|
||||||
chWrite(ch,0x06,wt->data[i*wt->len/32]*31/wt->max);
|
int data=wt->data[i*wt->len/32]*31/wt->max;
|
||||||
|
if (data<0) data=0;
|
||||||
|
if (data>31) data=31;
|
||||||
|
chWrite(ch,0x06,data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chan[ch].active) {
|
if (chan[ch].active) {
|
||||||
|
|
|
@ -435,8 +435,8 @@ public:
|
||||||
case ATTENREG2:
|
case ATTENREG2:
|
||||||
case ATTENREG3:
|
case ATTENREG3:
|
||||||
mRegisterPool[8*4+idx] = value;
|
mRegisterPool[8*4+idx] = value;
|
||||||
mAttenuationLeft[idx] = ( value & 0x0f ) << 2;
|
mAttenuationRight[idx] = ( value & 0x0f ) << 2;
|
||||||
mAttenuationRight[idx] = ( value & 0xf0 ) >> 2;
|
mAttenuationLeft[idx] = ( value & 0xf0 ) >> 2;
|
||||||
break;
|
break;
|
||||||
case MPAN:
|
case MPAN:
|
||||||
mPan = value;
|
mPan = value;
|
||||||
|
|
135
src/engine/platform/sound/vera_pcm.c
Normal file
135
src/engine/platform/sound/vera_pcm.c
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
// Commander X16 Emulator
|
||||||
|
// Copyright (c) 2020 Frank van den Hoef
|
||||||
|
// All rights reserved. License: 2-clause BSD
|
||||||
|
|
||||||
|
#include "vera_pcm.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static uint8_t volume_lut[16] = {0, 1, 2, 3, 4, 5, 6, 8, 11, 14, 18, 23, 30, 38, 49, 64};
|
||||||
|
|
||||||
|
static void
|
||||||
|
fifo_reset(struct VERA_PCM* pcm)
|
||||||
|
{
|
||||||
|
pcm->fifo_wridx = 0;
|
||||||
|
pcm->fifo_rdidx = 0;
|
||||||
|
pcm->fifo_cnt = 0;
|
||||||
|
memset(pcm->fifo,0,sizeof(pcm->fifo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pcm_reset(struct VERA_PCM* pcm)
|
||||||
|
{
|
||||||
|
fifo_reset(pcm);
|
||||||
|
pcm->ctrl = 0;
|
||||||
|
pcm->rate = 0;
|
||||||
|
pcm->cur_l = 0;
|
||||||
|
pcm->cur_r = 0;
|
||||||
|
pcm->phase = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pcm_write_ctrl(struct VERA_PCM* pcm, uint8_t val)
|
||||||
|
{
|
||||||
|
if (val & 0x80) {
|
||||||
|
fifo_reset(pcm);
|
||||||
|
}
|
||||||
|
|
||||||
|
pcm->ctrl = val & 0x3F;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
pcm_read_ctrl(struct VERA_PCM* pcm)
|
||||||
|
{
|
||||||
|
uint8_t result = pcm->ctrl;
|
||||||
|
if (pcm->fifo_cnt == sizeof(pcm->fifo)) {
|
||||||
|
result |= 0x80;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pcm_write_rate(struct VERA_PCM* pcm, uint8_t val)
|
||||||
|
{
|
||||||
|
pcm->rate = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
pcm_read_rate(struct VERA_PCM* pcm)
|
||||||
|
{
|
||||||
|
return pcm->rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pcm_write_fifo(struct VERA_PCM* pcm, uint8_t val)
|
||||||
|
{
|
||||||
|
if (pcm->fifo_cnt < sizeof(pcm->fifo)) {
|
||||||
|
pcm->fifo[pcm->fifo_wridx++] = val;
|
||||||
|
if (pcm->fifo_wridx == sizeof(pcm->fifo)) {
|
||||||
|
pcm->fifo_wridx = 0;
|
||||||
|
}
|
||||||
|
pcm->fifo_cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t
|
||||||
|
read_fifo(struct VERA_PCM* pcm)
|
||||||
|
{
|
||||||
|
if (pcm->fifo_cnt == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint8_t result = pcm->fifo[pcm->fifo_rdidx++];
|
||||||
|
if (pcm->fifo_rdidx == sizeof(pcm->fifo)) {
|
||||||
|
pcm->fifo_rdidx = 0;
|
||||||
|
}
|
||||||
|
pcm->fifo_cnt--;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
pcm_is_fifo_almost_empty(struct VERA_PCM* pcm)
|
||||||
|
{
|
||||||
|
return pcm->fifo_cnt < 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pcm_render(struct VERA_PCM* pcm, int16_t* buf_l, int16_t* buf_r, unsigned num_samples)
|
||||||
|
{
|
||||||
|
while (num_samples--) {
|
||||||
|
uint8_t old_phase = pcm->phase;
|
||||||
|
pcm->phase += pcm->rate;
|
||||||
|
if ((old_phase & 0x80) != (pcm->phase & 0x80)) {
|
||||||
|
switch ((pcm->ctrl >> 4) & 3) {
|
||||||
|
case 0: { // mono 8-bit
|
||||||
|
pcm->cur_l = (int16_t)read_fifo(pcm) << 8;
|
||||||
|
pcm->cur_r = pcm->cur_l;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: { // stereo 8-bit
|
||||||
|
pcm->cur_l = read_fifo(pcm) << 8;
|
||||||
|
pcm->cur_r = read_fifo(pcm) << 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: { // mono 16-bit
|
||||||
|
pcm->cur_l = read_fifo(pcm);
|
||||||
|
pcm->cur_l |= read_fifo(pcm) << 8;
|
||||||
|
pcm->cur_r = pcm->cur_l;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: { // stereo 16-bit
|
||||||
|
pcm->cur_l = read_fifo(pcm);
|
||||||
|
pcm->cur_l |= read_fifo(pcm) << 8;
|
||||||
|
pcm->cur_r = read_fifo(pcm);
|
||||||
|
pcm->cur_r |= read_fifo(pcm) << 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*(buf_l) += ((int)pcm->cur_l * (int)volume_lut[pcm->ctrl & 0xF]) >> 6;
|
||||||
|
*(buf_r) += ((int)pcm->cur_r * (int)volume_lut[pcm->ctrl & 0xF]) >> 6;
|
||||||
|
|
||||||
|
buf_l++;
|
||||||
|
buf_r++;
|
||||||
|
}
|
||||||
|
}
|
31
src/engine/platform/sound/vera_pcm.h
Normal file
31
src/engine/platform/sound/vera_pcm.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// Commander X16 Emulator
|
||||||
|
// Copyright (c) 2020 Frank van den Hoef
|
||||||
|
// All rights reserved. License: 2-clause BSD
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct VERA_PCM {
|
||||||
|
uint8_t fifo[4096 - 1]; // Actual hardware FIFO is 4kB, but you can only use 4095 bytes.
|
||||||
|
unsigned fifo_wridx;
|
||||||
|
unsigned fifo_rdidx;
|
||||||
|
unsigned fifo_cnt;
|
||||||
|
|
||||||
|
uint8_t ctrl;
|
||||||
|
uint8_t rate;
|
||||||
|
|
||||||
|
int16_t cur_l, cur_r;
|
||||||
|
uint8_t phase;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void pcm_reset(struct VERA_PCM* pcm);
|
||||||
|
void pcm_write_ctrl(struct VERA_PCM* pcm, uint8_t val);
|
||||||
|
uint8_t pcm_read_ctrl(struct VERA_PCM* pcm);
|
||||||
|
void pcm_write_rate(struct VERA_PCM* pcm, uint8_t val);
|
||||||
|
uint8_t pcm_read_rate(struct VERA_PCM* pcm);
|
||||||
|
void pcm_write_fifo(struct VERA_PCM* pcm, uint8_t val);
|
||||||
|
void pcm_render(struct VERA_PCM* pcm, int16_t* buf_l, int16_t* buf_r, unsigned num_samples);
|
||||||
|
bool pcm_is_fifo_almost_empty(struct VERA_PCM* pcm);
|
106
src/engine/platform/sound/vera_psg.c
Normal file
106
src/engine/platform/sound/vera_psg.c
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
// Commander X16 Emulator
|
||||||
|
// Copyright (c) 2020 Frank van den Hoef
|
||||||
|
// All rights reserved. License: 2-clause BSD
|
||||||
|
|
||||||
|
#include "vera_psg.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
enum waveform {
|
||||||
|
WF_PULSE = 0,
|
||||||
|
WF_SAWTOOTH,
|
||||||
|
WF_TRIANGLE,
|
||||||
|
WF_NOISE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t volume_lut[64] = {0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 11, 11, 12, 13, 14, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 28, 29, 31, 33, 35, 37, 39, 42, 44, 47, 50, 52, 56, 59, 63};
|
||||||
|
|
||||||
|
void
|
||||||
|
psg_reset(struct VERA_PSG* psg)
|
||||||
|
{
|
||||||
|
memset(psg->channels, 0, sizeof(psg->channels));
|
||||||
|
psg->noiseState=1;
|
||||||
|
psg->noiseOut=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
psg_writereg(struct VERA_PSG* psg, uint8_t reg, uint8_t val)
|
||||||
|
{
|
||||||
|
reg &= 0x3f;
|
||||||
|
|
||||||
|
int ch = reg / 4;
|
||||||
|
int idx = reg & 3;
|
||||||
|
|
||||||
|
switch (idx) {
|
||||||
|
case 0: psg->channels[ch].freq = (psg->channels[ch].freq & 0xFF00) | val; break;
|
||||||
|
case 1: psg->channels[ch].freq = (psg->channels[ch].freq & 0x00FF) | (val << 8); break;
|
||||||
|
case 2: {
|
||||||
|
psg->channels[ch].right = (val & 0x80) != 0;
|
||||||
|
psg->channels[ch].left = (val & 0x40) != 0;
|
||||||
|
psg->channels[ch].volume = volume_lut[val & 0x3F];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
psg->channels[ch].pw = val & 0x3F;
|
||||||
|
psg->channels[ch].waveform = val >> 6;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
render(struct VERA_PSG* psg, int16_t *left, int16_t *right)
|
||||||
|
{
|
||||||
|
int l = 0;
|
||||||
|
int r = 0;
|
||||||
|
// TODO this is a currently speculated noise generation
|
||||||
|
// as the hardware and sources for it are not out in the public
|
||||||
|
// and the official emulator just uses rand()
|
||||||
|
psg->noiseOut=((psg->noiseOut<<1)|(psg->noiseState&1))&63;
|
||||||
|
psg->noiseState=(psg->noiseState<<1)|(((psg->noiseState>>1)^(psg->noiseState>>2)^(psg->noiseState>>4)^(psg->noiseState>>15))&1);
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
struct VERAChannel *ch = &psg->channels[i];
|
||||||
|
|
||||||
|
unsigned new_phase = (ch->phase + ch->freq) & 0x1FFFF;
|
||||||
|
if ((ch->phase & 0x10000) != (new_phase & 0x10000)) {
|
||||||
|
ch->noiseval = psg->noiseOut;
|
||||||
|
}
|
||||||
|
ch->phase = new_phase;
|
||||||
|
|
||||||
|
uint8_t v = 0;
|
||||||
|
switch (ch->waveform) {
|
||||||
|
case WF_PULSE: v = (ch->phase >> 10) > ch->pw ? 0 : 63; break;
|
||||||
|
case WF_SAWTOOTH: v = ch->phase >> 11; break;
|
||||||
|
case WF_TRIANGLE: v = (ch->phase & 0x10000) ? (~(ch->phase >> 10) & 0x3F) : ((ch->phase >> 10) & 0x3F); break;
|
||||||
|
case WF_NOISE: v = ch->noiseval; break;
|
||||||
|
}
|
||||||
|
int8_t sv = (v ^ 0x20);
|
||||||
|
if (sv & 0x20) {
|
||||||
|
sv |= 0xC0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int val = (int)sv * (int)ch->volume;
|
||||||
|
|
||||||
|
if (ch->left) {
|
||||||
|
l += val;
|
||||||
|
}
|
||||||
|
if (ch->right) {
|
||||||
|
r += val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*left = l;
|
||||||
|
*right = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
psg_render(struct VERA_PSG* psg, int16_t *bufL, int16_t *bufR, unsigned num_samples)
|
||||||
|
{
|
||||||
|
while (num_samples--) {
|
||||||
|
render(psg, bufL, bufR);
|
||||||
|
bufL++;
|
||||||
|
bufR++;
|
||||||
|
}
|
||||||
|
}
|
28
src/engine/platform/sound/vera_psg.h
Normal file
28
src/engine/platform/sound/vera_psg.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// Commander X16 Emulator
|
||||||
|
// Copyright (c) 2020 Frank van den Hoef
|
||||||
|
// All rights reserved. License: 2-clause BSD
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct VERAChannel {
|
||||||
|
uint16_t freq;
|
||||||
|
uint8_t volume;
|
||||||
|
bool left, right;
|
||||||
|
uint8_t pw;
|
||||||
|
uint8_t waveform;
|
||||||
|
|
||||||
|
unsigned phase;
|
||||||
|
uint8_t noiseval;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VERA_PSG {
|
||||||
|
unsigned int noiseState, noiseOut;
|
||||||
|
struct VERAChannel channels[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
void psg_reset(struct VERA_PSG* psg);
|
||||||
|
void psg_writereg(struct VERA_PSG* psg, uint8_t reg, uint8_t val);
|
||||||
|
void psg_render(struct VERA_PSG* psg, int16_t *bufL, int16_t *bufR, unsigned num_samples);
|
224
src/engine/platform/sound/x1_010/x1_010.cpp
Normal file
224
src/engine/platform/sound/x1_010/x1_010.cpp
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
/*
|
||||||
|
License: BSD-3-Clause
|
||||||
|
see https://github.com/cam900/vgsound_emu/LICENSE for more details
|
||||||
|
|
||||||
|
Copyright holders: cam900
|
||||||
|
Seta/Allumer X1-010 Emulation core
|
||||||
|
|
||||||
|
the chip has 16 voices, all voices can be switchable to Wavetable or PCM sample playback mode.
|
||||||
|
It has also 2 output channels, but no known hardware using this feature for stereo sound.
|
||||||
|
|
||||||
|
Wavetable needs to paired with envelope, it's always enabled and similar as AY PSG's one
|
||||||
|
but its shape is stored at RAM.
|
||||||
|
|
||||||
|
PCM volume is stored by each register.
|
||||||
|
|
||||||
|
Both volume is 4bit per output.
|
||||||
|
|
||||||
|
Everything except PCM sample is stored at paired 8 bit RAM.
|
||||||
|
|
||||||
|
RAM layout (common case: Address bit 12 is swapped when RAM is shared with CPU)
|
||||||
|
|
||||||
|
-----------------------------
|
||||||
|
0000...007f Voice Registers
|
||||||
|
|
||||||
|
0000...0007 Voice 0 Register
|
||||||
|
|
||||||
|
Address Bits Description
|
||||||
|
7654 3210
|
||||||
|
0 x--- ---- Frequency divider*
|
||||||
|
---- -x-- Envelope one-shot mode
|
||||||
|
---- --x- Sound format
|
||||||
|
---- --0- PCM
|
||||||
|
---- --1- Wavetable
|
||||||
|
---- ---x Keyon/off
|
||||||
|
PCM case:
|
||||||
|
1 xxxx xxxx Volume (Each nibble is for each output)
|
||||||
|
|
||||||
|
2 xxxx xxxx Frequency*
|
||||||
|
|
||||||
|
4 xxxx xxxx Start address / 4096
|
||||||
|
|
||||||
|
5 xxxx xxxx 0x100 - (End address / 4096)
|
||||||
|
Wavetable case:
|
||||||
|
1 ---x xxxx Wavetable data select
|
||||||
|
|
||||||
|
2 xxxx xxxx Frequency LSB*
|
||||||
|
3 xxxx xxxx "" MSB
|
||||||
|
|
||||||
|
4 xxxx xxxx Envelope period (.10 fixed point, Low 8 bit)
|
||||||
|
|
||||||
|
5 ---x xxxx Envelope shape select (!= 0 : Reserved for Voice registers)
|
||||||
|
|
||||||
|
0008...000f Voice 1 Register
|
||||||
|
...
|
||||||
|
0078...007f Voice 15 Register
|
||||||
|
-----------------------------
|
||||||
|
0080...0fff Envelope shape data (Same as volume; Each nibble is for each output)
|
||||||
|
|
||||||
|
0080...00ff Envelope shape data 1
|
||||||
|
0100...017f Envelope shape data 2
|
||||||
|
...
|
||||||
|
0f80...0fff Envelope shape data 31
|
||||||
|
-----------------------------
|
||||||
|
1000...1fff Wavetable data
|
||||||
|
|
||||||
|
1000...107f Wavetable data 0
|
||||||
|
1080...10ff Wavetable data 1
|
||||||
|
...
|
||||||
|
1f80...1fff Wavetable data 31
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
* Frequency is 4.4 fixed point for PCM,
|
||||||
|
6.10 for Wavetable.
|
||||||
|
Frequency divider is higher precision or just right shift?
|
||||||
|
needs verification.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "x1_010.hpp"
|
||||||
|
|
||||||
|
void x1_010_core::tick()
|
||||||
|
{
|
||||||
|
// reset output
|
||||||
|
m_out[0] = m_out[1] = 0;
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
voice_t &v = m_voice[i];
|
||||||
|
v.tick();
|
||||||
|
m_out[0] += v.data * v.vol_out[0];
|
||||||
|
m_out[1] += v.data * v.vol_out[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void x1_010_core::voice_t::tick()
|
||||||
|
{
|
||||||
|
data = vol_out[0] = vol_out[1] = 0;
|
||||||
|
if (flag.keyon)
|
||||||
|
{
|
||||||
|
if (flag.wavetable) // Wavetable
|
||||||
|
{
|
||||||
|
// envelope, each nibble is for each output
|
||||||
|
u8 vol = m_host.m_envelope[(bitfield(end_envshape, 0, 5) << 7) | bitfield(env_acc, 10, 7)];
|
||||||
|
vol_out[0] = bitfield(vol, 4, 4);
|
||||||
|
vol_out[1] = bitfield(vol, 0, 4);
|
||||||
|
env_acc += start_envfreq;
|
||||||
|
if (flag.env_oneshot && bitfield(env_acc, 17))
|
||||||
|
flag.keyon = false;
|
||||||
|
else
|
||||||
|
env_acc = bitfield(env_acc, 0, 17);
|
||||||
|
// get wavetable data
|
||||||
|
data = m_host.m_wave[(bitfield(vol_wave, 0, 5) << 7) | bitfield(acc, 10, 7)];
|
||||||
|
acc = bitfield(acc + (freq >> flag.div), 0, 17);
|
||||||
|
}
|
||||||
|
else // PCM sample
|
||||||
|
{
|
||||||
|
// volume register, each nibble is for each output
|
||||||
|
vol_out[0] = bitfield(vol_wave, 4, 4);
|
||||||
|
vol_out[1] = bitfield(vol_wave, 0, 4);
|
||||||
|
// get PCM sample
|
||||||
|
data = m_host.m_intf.read_byte(bitfield(acc, 4, 20));
|
||||||
|
acc += bitfield(freq, 0, 8) >> flag.div;
|
||||||
|
if ((acc >> 16) > (0xff ^ end_envshape))
|
||||||
|
flag.keyon = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 x1_010_core::ram_r(u16 offset)
|
||||||
|
{
|
||||||
|
if (offset & 0x1000) // wavetable data
|
||||||
|
return m_wave[offset & 0xfff];
|
||||||
|
else if (offset & 0xf80) // envelope shape data
|
||||||
|
return m_envelope[offset & 0xfff];
|
||||||
|
else // channel register
|
||||||
|
return m_voice[bitfield(offset, 3, 4)].reg_r(offset & 0x7);
|
||||||
|
}
|
||||||
|
|
||||||
|
void x1_010_core::ram_w(u16 offset, u8 data)
|
||||||
|
{
|
||||||
|
if (offset & 0x1000) // wavetable data
|
||||||
|
m_wave[offset & 0xfff] = data;
|
||||||
|
else if (offset & 0xf80) // envelope shape data
|
||||||
|
m_envelope[offset & 0xfff] = data;
|
||||||
|
else // channel register
|
||||||
|
m_voice[bitfield(offset, 3, 4)].reg_w(offset & 0x7, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 x1_010_core::voice_t::reg_r(u8 offset)
|
||||||
|
{
|
||||||
|
switch (offset & 0x7)
|
||||||
|
{
|
||||||
|
case 0x00: return (flag.div << 7)
|
||||||
|
| (flag.env_oneshot << 2)
|
||||||
|
| (flag.wavetable << 1)
|
||||||
|
| (flag.keyon << 0);
|
||||||
|
case 0x01: return vol_wave;
|
||||||
|
case 0x02: return bitfield(freq, 0, 8);
|
||||||
|
case 0x03: return bitfield(freq, 8, 8);
|
||||||
|
case 0x04: return start_envfreq;
|
||||||
|
case 0x05: return end_envshape;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void x1_010_core::voice_t::reg_w(u8 offset, u8 data)
|
||||||
|
{
|
||||||
|
switch (offset & 0x7)
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
{
|
||||||
|
const bool prev_keyon = flag.keyon;
|
||||||
|
flag.div = bitfield(data, 7);
|
||||||
|
flag.env_oneshot = bitfield(data, 2);
|
||||||
|
flag.wavetable = bitfield(data, 1);
|
||||||
|
flag.keyon = bitfield(data, 0);
|
||||||
|
if (!prev_keyon && flag.keyon) // Key on
|
||||||
|
{
|
||||||
|
acc = flag.wavetable ? 0 : (u32(start_envfreq) << 16);
|
||||||
|
env_acc = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x01:
|
||||||
|
vol_wave = data;
|
||||||
|
break;
|
||||||
|
case 0x02:
|
||||||
|
freq = (freq & 0xff00) | data;
|
||||||
|
break;
|
||||||
|
case 0x03:
|
||||||
|
freq = (freq & 0x00ff) | (u16(data) << 8);
|
||||||
|
break;
|
||||||
|
case 0x04:
|
||||||
|
start_envfreq = data;
|
||||||
|
break;
|
||||||
|
case 0x05:
|
||||||
|
end_envshape = data;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void x1_010_core::voice_t::reset()
|
||||||
|
{
|
||||||
|
flag.reset();
|
||||||
|
vol_wave = 0;
|
||||||
|
freq = 0;
|
||||||
|
start_envfreq = 0;
|
||||||
|
end_envshape = 0;
|
||||||
|
acc = 0;
|
||||||
|
env_acc = 0;
|
||||||
|
data = 0;
|
||||||
|
vol_out[0] = vol_out[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void x1_010_core::reset()
|
||||||
|
{
|
||||||
|
for (auto & elem : m_voice)
|
||||||
|
elem.reset();
|
||||||
|
|
||||||
|
std::fill_n(&m_envelope[0], 0x1000, 0);
|
||||||
|
std::fill_n(&m_wave[0], 0x1000, 0);
|
||||||
|
m_out[0] = m_out[1] = 0;
|
||||||
|
}
|
127
src/engine/platform/sound/x1_010/x1_010.hpp
Normal file
127
src/engine/platform/sound/x1_010/x1_010.hpp
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
License: BSD-3-Clause
|
||||||
|
see https://github.com/cam900/vgsound_emu/LICENSE for more details
|
||||||
|
|
||||||
|
Copyright holders: cam900
|
||||||
|
Seta/Allumer X1-010 Emulation core
|
||||||
|
|
||||||
|
See x1_010.cpp for more info.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#ifndef _VGSOUND_EMU_X1_010_HPP
|
||||||
|
#define _VGSOUND_EMU_X1_010_HPP
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef unsigned char u8;
|
||||||
|
typedef unsigned short u16;
|
||||||
|
typedef unsigned int u32;
|
||||||
|
typedef signed char s8;
|
||||||
|
typedef signed int s32;
|
||||||
|
|
||||||
|
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||||
|
{
|
||||||
|
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
class x1_010_mem_intf
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual u8 read_byte(u32 address) { return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class x1_010_core
|
||||||
|
{
|
||||||
|
friend class x1_010_mem_intf;
|
||||||
|
public:
|
||||||
|
// constructor
|
||||||
|
x1_010_core(x1_010_mem_intf &intf)
|
||||||
|
: m_voice{*this,*this,*this,*this,
|
||||||
|
*this,*this,*this,*this,
|
||||||
|
*this,*this,*this,*this,
|
||||||
|
*this,*this,*this,*this}
|
||||||
|
, m_intf(intf)
|
||||||
|
{
|
||||||
|
m_envelope = std::make_unique<u8[]>(0x1000);
|
||||||
|
m_wave = std::make_unique<u8[]>(0x1000);
|
||||||
|
|
||||||
|
std::fill_n(&m_envelope[0], 0x1000, 0);
|
||||||
|
std::fill_n(&m_wave[0], 0x1000, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// register accessor
|
||||||
|
u8 ram_r(u16 offset);
|
||||||
|
void ram_w(u16 offset, u8 data);
|
||||||
|
|
||||||
|
// getters
|
||||||
|
s32 output(u8 channel) { return m_out[channel & 1]; }
|
||||||
|
|
||||||
|
// internal state
|
||||||
|
void reset();
|
||||||
|
void tick();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// 16 voices in chip
|
||||||
|
struct voice_t
|
||||||
|
{
|
||||||
|
// constructor
|
||||||
|
voice_t(x1_010_core &host) : m_host(host) {}
|
||||||
|
|
||||||
|
// internal state
|
||||||
|
void reset();
|
||||||
|
void tick();
|
||||||
|
|
||||||
|
// register accessor
|
||||||
|
u8 reg_r(u8 offset);
|
||||||
|
void reg_w(u8 offset, u8 data);
|
||||||
|
|
||||||
|
// registers
|
||||||
|
x1_010_core &m_host;
|
||||||
|
struct flag_t
|
||||||
|
{
|
||||||
|
u8 div : 1;
|
||||||
|
u8 env_oneshot : 1;
|
||||||
|
u8 wavetable : 1;
|
||||||
|
u8 keyon : 1;
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
div = 0;
|
||||||
|
env_oneshot = 0;
|
||||||
|
wavetable = 0;
|
||||||
|
keyon = 0;
|
||||||
|
}
|
||||||
|
flag_t()
|
||||||
|
: div(0)
|
||||||
|
, env_oneshot(0)
|
||||||
|
, wavetable(0)
|
||||||
|
, keyon(0)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
flag_t flag;
|
||||||
|
u8 vol_wave = 0;
|
||||||
|
u16 freq = 0;
|
||||||
|
u8 start_envfreq = 0;
|
||||||
|
u8 end_envshape = 0;
|
||||||
|
|
||||||
|
// internal registers
|
||||||
|
u32 acc = 0;
|
||||||
|
u32 env_acc = 0;
|
||||||
|
s8 data = 0;
|
||||||
|
u8 vol_out[2] = {0};
|
||||||
|
};
|
||||||
|
voice_t m_voice[16];
|
||||||
|
|
||||||
|
// RAM
|
||||||
|
std::unique_ptr<u8[]> m_envelope = nullptr;
|
||||||
|
std::unique_ptr<u8[]> m_wave = nullptr;
|
||||||
|
|
||||||
|
// output data
|
||||||
|
s32 m_out[2] = {0};
|
||||||
|
|
||||||
|
x1_010_mem_intf &m_intf;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -119,8 +119,12 @@ void DivPlatformSwan::updateWave(int ch) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i=0; i<16; i++) {
|
for (int i=0; i<16; i++) {
|
||||||
unsigned char nibble1=(wt->data[(i*2)*wt->len/32]*15)/wt->max;
|
int nibble1=(wt->data[(i*2)*wt->len/32]*15)/wt->max;
|
||||||
unsigned char nibble2=(wt->data[(1+i*2)*wt->len/32]*15)/wt->max;
|
int nibble2=(wt->data[(1+i*2)*wt->len/32]*15)/wt->max;
|
||||||
|
if (nibble1<0) nibble1=0;
|
||||||
|
if (nibble1>15) nibble1=15;
|
||||||
|
if (nibble2<0) nibble2=0;
|
||||||
|
if (nibble2>15) nibble2=15;
|
||||||
rWrite(addr+i,nibble1|(nibble2<<4));
|
rWrite(addr+i,nibble1|(nibble2<<4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
347
src/engine/platform/vera.cpp
Normal file
347
src/engine/platform/vera.cpp
Normal file
|
@ -0,0 +1,347 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "vera.h"
|
||||||
|
#include "../engine.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "sound/vera_psg.h"
|
||||||
|
#include "sound/vera_pcm.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
#define rWrite(c,a,d) {regPool[(c)*4+(a)]=(d); psg_writereg(psg,((c)*4+(a)),(d));}
|
||||||
|
#define rWriteLo(c,a,d) rWrite(c,a,(regPool[(c)*4+(a)]&(~0x3f))|((d)&0x3f))
|
||||||
|
#define rWriteHi(c,a,d) rWrite(c,a,(regPool[(c)*4+(a)]&(~0xc0))|(((d)<<6)&0xc0))
|
||||||
|
#define rWriteFIFOVol(d) rWrite(16,0,(regPool[64]&(~0x3f))|((d)&0x3f))
|
||||||
|
|
||||||
|
const char* regCheatSheetVERA[]={
|
||||||
|
"CHxFreq", "00+x*4",
|
||||||
|
"CHxVol", "02+x*4",
|
||||||
|
"CHxWave", "03+x*4",
|
||||||
|
|
||||||
|
"AUDIO_CTRL", "40",
|
||||||
|
"AUDIO_RATE", "41",
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
const char** DivPlatformVERA::getRegisterSheet() {
|
||||||
|
return regCheatSheetVERA;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* DivPlatformVERA::getEffectName(unsigned char effect) {
|
||||||
|
switch (effect) {
|
||||||
|
case 0x20:
|
||||||
|
return "20xx: Change waveform";
|
||||||
|
break;
|
||||||
|
case 0x22:
|
||||||
|
return "22xx: Set duty cycle (0 to 63)";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: wire up PCM.
|
||||||
|
void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||||
|
psg_render(psg,bufL+start,bufR+start,len);
|
||||||
|
pcm_render(pcm,bufL+start,bufR+start,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformVERA::reset() {
|
||||||
|
for (int i=0; i<17; i++) {
|
||||||
|
chan[i]=Channel();
|
||||||
|
}
|
||||||
|
psg_reset(psg);
|
||||||
|
pcm_reset(pcm);
|
||||||
|
memset(regPool,0,66);
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
chan[i].vol=63;
|
||||||
|
chan[i].pan=3;
|
||||||
|
rWriteHi(i,2,isMuted[i]?0:3);
|
||||||
|
}
|
||||||
|
chan[16].vol=15;
|
||||||
|
chan[16].pan=3;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DivPlatformVERA::calcNoteFreq(int ch, int note) {
|
||||||
|
if (ch<16) {
|
||||||
|
return parent->calcBaseFreq(chipClock,2097152,note,false);
|
||||||
|
} else {
|
||||||
|
double off=1.0;
|
||||||
|
if (chan[ch].pcm.sample>=0 && chan[ch].pcm.sample<parent->song.sampleLen) {
|
||||||
|
DivSample* s=parent->getSample(chan[ch].pcm.sample);
|
||||||
|
if (s->centerRate<1) {
|
||||||
|
off=1.0;
|
||||||
|
} else {
|
||||||
|
off=s->centerRate/8363.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (int)(off*parent->calcBaseFreq(chipClock,65536,note,false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformVERA::tick() {
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
chan[i].std.next();
|
||||||
|
if (chan[i].std.hadVol) {
|
||||||
|
chan[i].outVol=MAX(chan[i].vol+chan[i].std.vol-63,0);
|
||||||
|
rWriteLo(i,2,chan[i].outVol);
|
||||||
|
}
|
||||||
|
if (chan[i].std.hadArp) {
|
||||||
|
if (!chan[i].inPorta) {
|
||||||
|
if (chan[i].std.arpMode) {
|
||||||
|
chan[i].baseFreq=calcNoteFreq(0,chan[i].std.arp);
|
||||||
|
} else {
|
||||||
|
chan[i].baseFreq=calcNoteFreq(0,chan[i].note+chan[i].std.arp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chan[i].freqChanged=true;
|
||||||
|
} else {
|
||||||
|
if (chan[i].std.arpMode && chan[i].std.finishedArp) {
|
||||||
|
chan[i].baseFreq=calcNoteFreq(0,chan[i].note);
|
||||||
|
chan[i].freqChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chan[i].std.hadDuty) {
|
||||||
|
rWriteLo(i,3,chan[i].std.duty);
|
||||||
|
}
|
||||||
|
if (chan[i].std.hadWave) {
|
||||||
|
rWriteHi(i,3,chan[i].std.wave);
|
||||||
|
}
|
||||||
|
if (chan[i].freqChanged) {
|
||||||
|
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8);
|
||||||
|
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||||
|
rWrite(i,0,chan[i].freq&0xff);
|
||||||
|
rWrite(i,1,(chan[i].freq>>8)&0xff);
|
||||||
|
chan[i].freqChanged=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// PCM
|
||||||
|
chan[16].std.next();
|
||||||
|
if (chan[16].std.hadVol) {
|
||||||
|
chan[16].outVol=MAX(chan[16].vol+MIN(chan[16].std.vol/4,15)-15,0);
|
||||||
|
rWriteFIFOVol(chan[16].outVol&15);
|
||||||
|
}
|
||||||
|
if (chan[16].std.hadArp) {
|
||||||
|
if (!chan[16].inPorta) {
|
||||||
|
if (chan[16].std.arpMode) {
|
||||||
|
chan[16].baseFreq=calcNoteFreq(16,chan[16].std.arp);
|
||||||
|
} else {
|
||||||
|
chan[16].baseFreq=calcNoteFreq(16,chan[16].note+chan[16].std.arp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chan[16].freqChanged=true;
|
||||||
|
} else {
|
||||||
|
if (chan[16].std.arpMode && chan[16].std.finishedArp) {
|
||||||
|
chan[16].baseFreq=calcNoteFreq(16,chan[16].note);
|
||||||
|
chan[16].freqChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chan[16].freqChanged) {
|
||||||
|
chan[16].freq=parent->calcFreq(chan[16].baseFreq,chan[16].pitch,false,8);
|
||||||
|
if (chan[16].freq>128) chan[16].freq=128;
|
||||||
|
rWrite(16,1,chan[16].freq&0xff);
|
||||||
|
chan[16].freqChanged=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int DivPlatformVERA::dispatch(DivCommand c) {
|
||||||
|
int tmp;
|
||||||
|
switch (c.cmd) {
|
||||||
|
case DIV_CMD_NOTE_ON:
|
||||||
|
if(c.chan<16) {
|
||||||
|
rWriteLo(c.chan,2,chan[c.chan].vol)
|
||||||
|
} else {
|
||||||
|
chan[c.chan].pcm.sample=parent->getIns(chan[16].ins)->amiga.initSample;
|
||||||
|
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
|
||||||
|
chan[c.chan].pcm.sample=-1;
|
||||||
|
}
|
||||||
|
chan[16].pcm.pos=0;
|
||||||
|
rWriteFIFOVol(chan[c.chan].vol);
|
||||||
|
}
|
||||||
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].baseFreq=calcNoteFreq(c.chan,c.value);
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
chan[c.chan].note=c.value;
|
||||||
|
}
|
||||||
|
chan[c.chan].active=true;
|
||||||
|
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
|
||||||
|
break;
|
||||||
|
case DIV_CMD_NOTE_OFF:
|
||||||
|
chan[c.chan].active=false;
|
||||||
|
if(c.chan<16) {
|
||||||
|
rWriteLo(c.chan,2,0)
|
||||||
|
} else {
|
||||||
|
chan[16].pcm.sample=-1;
|
||||||
|
rWriteFIFOVol(0);
|
||||||
|
rWrite(16,1,0);
|
||||||
|
}
|
||||||
|
chan[c.chan].std.init(NULL);
|
||||||
|
break;
|
||||||
|
case DIV_CMD_NOTE_OFF_ENV:
|
||||||
|
case DIV_CMD_ENV_RELEASE:
|
||||||
|
chan[c.chan].std.release();
|
||||||
|
break;
|
||||||
|
case DIV_CMD_INSTRUMENT:
|
||||||
|
chan[c.chan].ins=(unsigned char)c.value;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_VOLUME:
|
||||||
|
if (c.chan<16) {
|
||||||
|
tmp=c.value&0x3f;
|
||||||
|
chan[c.chan].vol=tmp;
|
||||||
|
rWriteLo(c.chan,2,tmp);
|
||||||
|
} else {
|
||||||
|
tmp=c.value&0x0f;
|
||||||
|
chan[c.chan].vol=tmp;
|
||||||
|
rWriteFIFOVol(tmp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DIV_CMD_GET_VOLUME:
|
||||||
|
return chan[c.chan].vol;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_PITCH:
|
||||||
|
chan[c.chan].pitch=c.value;
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_NOTE_PORTA: {
|
||||||
|
int destFreq=calcNoteFreq(c.chan,c.value2);
|
||||||
|
bool return2=false;
|
||||||
|
if (destFreq>chan[c.chan].baseFreq) {
|
||||||
|
chan[c.chan].baseFreq+=c.value;
|
||||||
|
if (chan[c.chan].baseFreq>=destFreq) {
|
||||||
|
chan[c.chan].baseFreq=destFreq;
|
||||||
|
return2=true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chan[c.chan].baseFreq-=c.value;
|
||||||
|
if (chan[c.chan].baseFreq<=destFreq) {
|
||||||
|
chan[c.chan].baseFreq=destFreq;
|
||||||
|
return2=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
if (return2) {
|
||||||
|
chan[c.chan].inPorta=false;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DIV_CMD_LEGATO:
|
||||||
|
chan[c.chan].baseFreq=calcNoteFreq(c.chan,c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0)));
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
chan[c.chan].note=c.value;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_PRE_PORTA:
|
||||||
|
if (chan[c.chan].active && c.value2) {
|
||||||
|
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
|
||||||
|
}
|
||||||
|
chan[c.chan].inPorta=c.value;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_STD_NOISE_MODE:
|
||||||
|
if (c.chan<16) rWriteLo(c.chan,3,c.value);
|
||||||
|
break;
|
||||||
|
case DIV_CMD_WAVE:
|
||||||
|
if (c.chan<16) rWriteHi(c.chan,3,c.value);
|
||||||
|
break;
|
||||||
|
case DIV_CMD_PANNING: {
|
||||||
|
tmp=0;
|
||||||
|
tmp|=(c.value&0x10)?1:0;
|
||||||
|
tmp|=(c.value&0x01)?2:0;
|
||||||
|
chan[c.chan].pan=tmp&3;
|
||||||
|
if (c.chan<16) {
|
||||||
|
rWriteHi(c.chan,2,isMuted[c.chan]?0:chan[c.chan].pan);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DIV_CMD_GET_VOLMAX:
|
||||||
|
if(c.chan<16) {
|
||||||
|
return 63;
|
||||||
|
} else {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DIV_ALWAYS_SET_VOLUME:
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* DivPlatformVERA::getChanState(int ch) {
|
||||||
|
return &chan[ch];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* DivPlatformVERA::getRegisterPool() {
|
||||||
|
return regPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DivPlatformVERA::getRegisterPoolSize() {
|
||||||
|
return 66;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformVERA::muteChannel(int ch, bool mute) {
|
||||||
|
isMuted[ch]=mute;
|
||||||
|
if (ch<16) {
|
||||||
|
rWriteHi(ch,2,mute?0:chan[ch].pan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivPlatformVERA::isStereo() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformVERA::notifyInsDeletion(void* ins) {
|
||||||
|
for (int i=0; i<2; i++) {
|
||||||
|
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformVERA::poke(unsigned int addr, unsigned short val) {
|
||||||
|
regPool[addr] = (unsigned char)val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformVERA::poke(std::vector<DivRegWrite>& wlist) {
|
||||||
|
for (auto &i: wlist) regPool[i.addr] = (unsigned char)i.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DivPlatformVERA::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||||
|
for (int i=0; i<17; i++) {
|
||||||
|
isMuted[i]=false;
|
||||||
|
}
|
||||||
|
parent=p;
|
||||||
|
psg=new struct VERA_PSG;
|
||||||
|
pcm=new struct VERA_PCM;
|
||||||
|
dumpWrites=false;
|
||||||
|
skipRegisterWrites=false;
|
||||||
|
chipClock=25000000;
|
||||||
|
rate=chipClock/512;
|
||||||
|
reset();
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformVERA::quit() {
|
||||||
|
delete psg;
|
||||||
|
delete pcm;
|
||||||
|
}
|
||||||
|
DivPlatformVERA::~DivPlatformVERA() {
|
||||||
|
}
|
78
src/engine/platform/vera.h
Normal file
78
src/engine/platform/vera.h
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _VERA_H
|
||||||
|
#define _VERA_H
|
||||||
|
#include "../dispatch.h"
|
||||||
|
#include "../instrument.h"
|
||||||
|
#include "../macroInt.h"
|
||||||
|
|
||||||
|
struct VERA_PSG;
|
||||||
|
struct VERA_PCM;
|
||||||
|
|
||||||
|
class DivPlatformVERA: public DivDispatch {
|
||||||
|
protected:
|
||||||
|
struct Channel {
|
||||||
|
int freq, baseFreq, pitch, note;
|
||||||
|
unsigned char ins, pan;
|
||||||
|
bool active, freqChanged, inPorta;
|
||||||
|
int vol, outVol;
|
||||||
|
unsigned accum;
|
||||||
|
int noiseval;
|
||||||
|
DivMacroInt std;
|
||||||
|
|
||||||
|
struct PCMChannel {
|
||||||
|
int sample;
|
||||||
|
int out_l, out_r;
|
||||||
|
unsigned pos;
|
||||||
|
unsigned len;
|
||||||
|
unsigned char freq;
|
||||||
|
PCMChannel(): sample(-1), out_l(0), out_r(0), pos(0), len(0), freq(0) {}
|
||||||
|
} pcm;
|
||||||
|
Channel(): freq(0), baseFreq(0), pitch(0), note(0), ins(-1), pan(0), active(false), freqChanged(false), inPorta(false), vol(0), outVol(0), accum(0), noiseval(0) {}
|
||||||
|
};
|
||||||
|
Channel chan[17];
|
||||||
|
bool isMuted[17];
|
||||||
|
unsigned char regPool[66];
|
||||||
|
struct VERA_PSG* psg;
|
||||||
|
struct VERA_PCM* pcm;
|
||||||
|
|
||||||
|
int calcNoteFreq(int ch, int note);
|
||||||
|
friend void putDispatchChan(void*,int,int);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||||
|
int dispatch(DivCommand c);
|
||||||
|
void* getChanState(int chan);
|
||||||
|
unsigned char* getRegisterPool();
|
||||||
|
int getRegisterPoolSize();
|
||||||
|
void reset();
|
||||||
|
void tick();
|
||||||
|
void muteChannel(int ch, bool mute);
|
||||||
|
void notifyInsDeletion(void* ins);
|
||||||
|
bool isStereo();
|
||||||
|
void poke(unsigned int addr, unsigned short val);
|
||||||
|
void poke(std::vector<DivRegWrite>& wlist);
|
||||||
|
const char** getRegisterSheet();
|
||||||
|
const char* getEffectName(unsigned char effect);
|
||||||
|
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||||
|
void quit();
|
||||||
|
~DivPlatformVERA();
|
||||||
|
};
|
||||||
|
#endif
|
893
src/engine/platform/x1_010.cpp
Normal file
893
src/engine/platform/x1_010.cpp
Normal file
|
@ -0,0 +1,893 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "x1_010.h"
|
||||||
|
#include "../engine.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
//#define rWrite(a,v) pendingWrites[a]=v;
|
||||||
|
#define rWrite(a,v) if (!skipRegisterWrites) { x1_010->ram_w(a,v); if (dumpWrites) { addWrite(a,v); } }
|
||||||
|
|
||||||
|
#define chRead(c,a) x1_010->ram_r((c<<3)|(a&7))
|
||||||
|
#define chWrite(c,a,v) rWrite((c<<3)|(a&7),v)
|
||||||
|
#define waveWrite(c,a,v) rWrite(0x1000|(chan[c].waveBank<<11)|(c<<7)|(a&0x7f),(v-128)&0xff)
|
||||||
|
#define envFill(c,a) rWrite(0x800|(c<<7)|(a&0x7f),(chan[c].lvol<<4)|chan[c].rvol)
|
||||||
|
#define envWrite(c,a,l,r) rWrite(0x800|(c<<7)|(a&0x7f),(((chan[c].lvol*(l))/15)<<4)|((chan[c].rvol*(r))/15))
|
||||||
|
|
||||||
|
#define refreshControl(c) chWrite(c,0,chan[c].active?(chan[c].pcm?1:((chan[c].env.flag.envEnable && chan[c].env.flag.envOneshot)?7:3)):0);
|
||||||
|
|
||||||
|
#define CHIP_FREQBASE 4194304
|
||||||
|
|
||||||
|
const char* regCheatSheetX1_010[]={
|
||||||
|
// Channel registers
|
||||||
|
"Ch00_Control", "0000",
|
||||||
|
"Ch00_PCMVol_WavSel", "0001",
|
||||||
|
"Ch00_FreqL", "0002",
|
||||||
|
"Ch00_FreqH", "0003",
|
||||||
|
"Ch00_Start_EnvFrq", "0004",
|
||||||
|
"Ch00_End_EnvSel", "0005",
|
||||||
|
"Ch01_Control", "0008",
|
||||||
|
"Ch01_PCMVol_WavSel", "0009",
|
||||||
|
"Ch01_FreqL", "000A",
|
||||||
|
"Ch01_FreqH", "000B",
|
||||||
|
"Ch01_Start_EnvFrq", "000C",
|
||||||
|
"Ch01_End_EnvSel", "000D",
|
||||||
|
"Ch02_Control", "0010",
|
||||||
|
"Ch02_PCMVol_WavSel", "0011",
|
||||||
|
"Ch02_FreqL", "0012",
|
||||||
|
"Ch02_FreqH", "0013",
|
||||||
|
"Ch02_Start_EnvFrq", "0014",
|
||||||
|
"Ch02_End_EnvSel", "0015",
|
||||||
|
"Ch03_Control", "0018",
|
||||||
|
"Ch03_PCMVol_WavSel", "0019",
|
||||||
|
"Ch03_FreqL", "001A",
|
||||||
|
"Ch03_FreqH", "001B",
|
||||||
|
"Ch03_Start_EnvFrq", "001C",
|
||||||
|
"Ch03_End_EnvSel", "001D",
|
||||||
|
"Ch04_Control", "0020",
|
||||||
|
"Ch04_PCMVol_WavSel", "0021",
|
||||||
|
"Ch04_FreqL", "0022",
|
||||||
|
"Ch04_FreqH", "0023",
|
||||||
|
"Ch04_Start_EnvFrq", "0024",
|
||||||
|
"Ch04_End_EnvSel", "0025",
|
||||||
|
"Ch05_Control", "0028",
|
||||||
|
"Ch05_PCMVol_WavSel", "0029",
|
||||||
|
"Ch05_FreqL", "002A",
|
||||||
|
"Ch05_FreqH", "002B",
|
||||||
|
"Ch05_Start_EnvFrq", "002C",
|
||||||
|
"Ch05_End_EnvSel", "002D",
|
||||||
|
"Ch06_Control", "0030",
|
||||||
|
"Ch06_PCMVol_WavSel", "0031",
|
||||||
|
"Ch06_FreqL", "0032",
|
||||||
|
"Ch06_FreqH", "0033",
|
||||||
|
"Ch06_Start_EnvFrq", "0034",
|
||||||
|
"Ch06_End_EnvSel", "0035",
|
||||||
|
"Ch07_Control", "0038",
|
||||||
|
"Ch07_PCMVol_WavSel", "0039",
|
||||||
|
"Ch07_FreqL", "003A",
|
||||||
|
"Ch07_FreqH", "003B",
|
||||||
|
"Ch07_Start_EnvFrq", "003C",
|
||||||
|
"Ch07_End_EnvSel", "003D",
|
||||||
|
"Ch08_Control", "0040",
|
||||||
|
"Ch08_PCMVol_WavSel", "0041",
|
||||||
|
"Ch08_FreqL", "0042",
|
||||||
|
"Ch08_FreqH", "0043",
|
||||||
|
"Ch08_Start_EnvFrq", "0044",
|
||||||
|
"Ch08_End_EnvSel", "0045",
|
||||||
|
"Ch09_Control", "0048",
|
||||||
|
"Ch09_PCMVol_WavSel", "0049",
|
||||||
|
"Ch09_FreqL", "004A",
|
||||||
|
"Ch09_FreqH", "004B",
|
||||||
|
"Ch09_Start_EnvFrq", "004C",
|
||||||
|
"Ch09_End_EnvSel", "004D",
|
||||||
|
"Ch10_Control", "0050",
|
||||||
|
"Ch10_PCMVol_WavSel", "0051",
|
||||||
|
"Ch10_FreqL", "0052",
|
||||||
|
"Ch10_FreqH", "0053",
|
||||||
|
"Ch10_Start_EnvFrq", "0054",
|
||||||
|
"Ch10_End_EnvSel", "0055",
|
||||||
|
"Ch11_Control", "0058",
|
||||||
|
"Ch11_PCMVol_WavSel", "0059",
|
||||||
|
"Ch11_FreqL", "005A",
|
||||||
|
"Ch11_FreqH", "005B",
|
||||||
|
"Ch11_Start_EnvFrq", "005C",
|
||||||
|
"Ch11_End_EnvSel", "005D",
|
||||||
|
"Ch12_Control", "0060",
|
||||||
|
"Ch12_PCMVol_WavSel", "0061",
|
||||||
|
"Ch12_FreqL", "0062",
|
||||||
|
"Ch12_FreqH", "0063",
|
||||||
|
"Ch12_Start_EnvFrq", "0064",
|
||||||
|
"Ch12_End_EnvSel", "0065",
|
||||||
|
"Ch13_Control", "0068",
|
||||||
|
"Ch13_PCMVol_WavSel", "0069",
|
||||||
|
"Ch13_FreqL", "006A",
|
||||||
|
"Ch13_FreqH", "006B",
|
||||||
|
"Ch13_Start_EnvFrq", "006C",
|
||||||
|
"Ch13_End_EnvSel", "006D",
|
||||||
|
"Ch14_Control", "0070",
|
||||||
|
"Ch14_PCMVol_WavSel", "0071",
|
||||||
|
"Ch14_FreqL", "0072",
|
||||||
|
"Ch14_FreqH", "0073",
|
||||||
|
"Ch14_Start_EnvFrq", "0074",
|
||||||
|
"Ch14_End_EnvSel", "0075",
|
||||||
|
"Ch15_Control", "0078",
|
||||||
|
"Ch15_PCMVol_WavSel", "0079",
|
||||||
|
"Ch15_FreqL", "007A",
|
||||||
|
"Ch15_FreqH", "007B",
|
||||||
|
"Ch15_Start_EnvFrq", "007C",
|
||||||
|
"Ch15_End_EnvSel", "007D",
|
||||||
|
// Envelope data
|
||||||
|
"Env01Data", "0080",
|
||||||
|
"Env02Data", "0100",
|
||||||
|
"Env03Data", "0180",
|
||||||
|
"Env04Data", "0200",
|
||||||
|
"Env05Data", "0280",
|
||||||
|
"Env06Data", "0300",
|
||||||
|
"Env07Data", "0380",
|
||||||
|
"Env08Data", "0400",
|
||||||
|
"Env09Data", "0480",
|
||||||
|
"Env10Data", "0500",
|
||||||
|
"Env11Data", "0580",
|
||||||
|
"Env12Data", "0600",
|
||||||
|
"Env13Data", "0680",
|
||||||
|
"Env14Data", "0700",
|
||||||
|
"Env15Data", "0780",
|
||||||
|
"Env16Data", "0800",
|
||||||
|
"Env17Data", "0880",
|
||||||
|
"Env18Data", "0900",
|
||||||
|
"Env19Data", "0980",
|
||||||
|
"Env20Data", "0A00",
|
||||||
|
"Env21Data", "0A80",
|
||||||
|
"Env22Data", "0B00",
|
||||||
|
"Env23Data", "0B80",
|
||||||
|
"Env24Data", "0C00",
|
||||||
|
"Env25Data", "0C80",
|
||||||
|
"Env26Data", "0D00",
|
||||||
|
"Env27Data", "0D80",
|
||||||
|
"Env28Data", "0E00",
|
||||||
|
"Env29Data", "0E80",
|
||||||
|
"Env30Data", "0F00",
|
||||||
|
"Env31Data", "0F80",
|
||||||
|
// Wavetable data
|
||||||
|
"Wave00Data", "1000",
|
||||||
|
"Wave01Data", "1080",
|
||||||
|
"Wave02Data", "1100",
|
||||||
|
"Wave03Data", "1180",
|
||||||
|
"Wave04Data", "1200",
|
||||||
|
"Wave05Data", "1280",
|
||||||
|
"Wave06Data", "1300",
|
||||||
|
"Wave07Data", "1380",
|
||||||
|
"Wave08Data", "1400",
|
||||||
|
"Wave09Data", "1480",
|
||||||
|
"Wave10Data", "1500",
|
||||||
|
"Wave11Data", "1580",
|
||||||
|
"Wave12Data", "1600",
|
||||||
|
"Wave13Data", "1680",
|
||||||
|
"Wave14Data", "1700",
|
||||||
|
"Wave15Data", "1780",
|
||||||
|
"Wave16Data", "1800",
|
||||||
|
"Wave17Data", "1880",
|
||||||
|
"Wave18Data", "1900",
|
||||||
|
"Wave19Data", "1980",
|
||||||
|
"Wave20Data", "1A00",
|
||||||
|
"Wave21Data", "1A80",
|
||||||
|
"Wave22Data", "1B00",
|
||||||
|
"Wave23Data", "1B80",
|
||||||
|
"Wave24Data", "1C00",
|
||||||
|
"Wave25Data", "1C80",
|
||||||
|
"Wave26Data", "1D00",
|
||||||
|
"Wave27Data", "1D80",
|
||||||
|
"Wave28Data", "1E00",
|
||||||
|
"Wave29Data", "1E80",
|
||||||
|
"Wave30Data", "1F00",
|
||||||
|
"Wave31Data", "1F80",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
const char** DivPlatformX1_010::getRegisterSheet() {
|
||||||
|
return regCheatSheetX1_010;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* DivPlatformX1_010::getEffectName(unsigned char effect) {
|
||||||
|
switch (effect) {
|
||||||
|
case 0x10:
|
||||||
|
return "10xx: Change waveform";
|
||||||
|
break;
|
||||||
|
case 0x11:
|
||||||
|
return "11xx: Change envelope shape";
|
||||||
|
break;
|
||||||
|
case 0x17:
|
||||||
|
return "17xx: Toggle PCM mode";
|
||||||
|
break;
|
||||||
|
case 0x20:
|
||||||
|
return "20xx: Set PCM frequency (1 to FF)";
|
||||||
|
break;
|
||||||
|
case 0x22:
|
||||||
|
return "22xx: Set envelope mode (bit 0: enable, bit 1: one-shot, bit 2: split shape to L/R, bit 3/5: H.invert right/left, bit 4/6: V.invert right/left)";
|
||||||
|
break;
|
||||||
|
case 0x23:
|
||||||
|
return "23xx: Set envelope period";
|
||||||
|
break;
|
||||||
|
case 0x25:
|
||||||
|
return "25xx: Envelope slide up";
|
||||||
|
break;
|
||||||
|
case 0x26:
|
||||||
|
return "26xx: Envelope slide down";
|
||||||
|
break;
|
||||||
|
case 0x29:
|
||||||
|
return "29xy: Set auto-envelope (x: numerator; y: denominator)";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||||
|
for (size_t h=start; h<start+len; h++) {
|
||||||
|
x1_010->tick();
|
||||||
|
|
||||||
|
signed int tempL=x1_010->output(0);
|
||||||
|
signed int tempR=x1_010->output(1);
|
||||||
|
|
||||||
|
if (tempL<-32768) tempL=-32768;
|
||||||
|
if (tempL>32767) tempL=32767;
|
||||||
|
if (tempR<-32768) tempR=-32768;
|
||||||
|
if (tempR>32767) tempR=32767;
|
||||||
|
|
||||||
|
//printf("tempL: %d tempR: %d\n",tempL,tempR);
|
||||||
|
bufL[h]=stereo?tempL:((tempL+tempR)>>1);
|
||||||
|
bufR[h]=stereo?tempR:bufL[h];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double DivPlatformX1_010::NoteX1_010(int ch, int note) {
|
||||||
|
if (chan[ch].pcm) { // PCM note
|
||||||
|
double off=1.0;
|
||||||
|
int sample=chan[ch].sample;
|
||||||
|
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||||
|
DivSample* s=parent->getSample(sample);
|
||||||
|
if (s->centerRate<1) {
|
||||||
|
off=1.0;
|
||||||
|
} else {
|
||||||
|
off=s->centerRate/8363.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return off*parent->calcBaseFreq(chipClock,8192,note,false);
|
||||||
|
}
|
||||||
|
// Wavetable note
|
||||||
|
return NOTE_FREQUENCY(note);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::updateWave(int ch) {
|
||||||
|
DivWavetable* wt=parent->getWave(chan[ch].wave);
|
||||||
|
if (chan[ch].active) {
|
||||||
|
chan[ch].waveBank ^= 1;
|
||||||
|
}
|
||||||
|
for (int i=0; i<128; i++) {
|
||||||
|
if (wt->max<1 || wt->len<1) {
|
||||||
|
waveWrite(ch,i,0);
|
||||||
|
} else {
|
||||||
|
int data=wt->data[i*wt->len/128]*255/wt->max;
|
||||||
|
if (data<0) data=0;
|
||||||
|
if (data>255) data=255;
|
||||||
|
waveWrite(ch,i,data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!chan[ch].pcm) {
|
||||||
|
chWrite(ch,1,(chan[ch].waveBank<<4)|(ch&0xf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::updateEnvelope(int ch) {
|
||||||
|
if (!chan[ch].pcm) {
|
||||||
|
if (isMuted[ch]) {
|
||||||
|
for (int i=0; i<128; i++) {
|
||||||
|
rWrite(0x800|(ch<<7)|(i&0x7f),0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!chan[ch].env.flag.envEnable) {
|
||||||
|
for (int i=0; i<128; i++) {
|
||||||
|
envFill(ch,i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DivWavetable* wt=parent->getWave(chan[ch].env.shape);
|
||||||
|
for (int i=0; i<128; i++) {
|
||||||
|
if (wt->max<1 || wt->len<1) {
|
||||||
|
envFill(ch,i);
|
||||||
|
} else if (chan[ch].env.flag.envSplit || chan[ch].env.flag.envHinvR || chan[ch].env.flag.envVinvR || chan[ch].env.flag.envHinvL || chan[ch].env.flag.envVinvL) { // Stereo config
|
||||||
|
int la=i,ra=i;
|
||||||
|
int lo,ro;
|
||||||
|
if (chan[ch].env.flag.envHinvR) { ra=127-i; } // horizontal invert right envelope
|
||||||
|
if (chan[ch].env.flag.envHinvL) { la=127-i; } // horizontal invert left envelope
|
||||||
|
if (chan[ch].env.flag.envSplit) { // Split shape to left and right half
|
||||||
|
lo=wt->data[la*(wt->len/128/2)]*15/wt->max;
|
||||||
|
ro=wt->data[(ra+128)*(wt->len/128/2)]*15/wt->max;
|
||||||
|
} else {
|
||||||
|
lo=wt->data[la*wt->len/128]*15/wt->max;
|
||||||
|
ro=wt->data[ra*wt->len/128]*15/wt->max;
|
||||||
|
}
|
||||||
|
if (chan[ch].env.flag.envVinvR) { ro=15-ro; } // vertical invert right envelope
|
||||||
|
if (chan[ch].env.flag.envVinvL) { lo=15-lo; } // vertical invert left envelope
|
||||||
|
if (lo<0) lo=0;
|
||||||
|
if (lo>15) lo=15;
|
||||||
|
if (ro<0) ro=0;
|
||||||
|
if (ro>15) ro=15;
|
||||||
|
envWrite(ch,i,lo,ro);
|
||||||
|
} else {
|
||||||
|
int out=wt->data[i*wt->len/128]*15/wt->max;
|
||||||
|
if (out<0) out=0;
|
||||||
|
if (out>15) out=15;
|
||||||
|
envWrite(ch,i,out,out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chWrite(ch,5,0x10|(ch&0xf));
|
||||||
|
} else {
|
||||||
|
chWrite(ch,1,(chan[ch].lvol<<4)|chan[ch].rvol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::tick() {
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
chan[i].std.next();
|
||||||
|
if (chan[i].std.hadVol) {
|
||||||
|
signed char macroVol=((chan[i].vol&15)*MIN(chan[i].furnacePCM?64:15,chan[i].std.vol))/(chan[i].furnacePCM?64:15);
|
||||||
|
if ((!isMuted[i]) && (macroVol!=chan[i].outVol)) {
|
||||||
|
chan[i].outVol=macroVol;
|
||||||
|
chan[i].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((!chan[i].pcm) || chan[i].furnacePCM) {
|
||||||
|
if (chan[i].std.hadArp) {
|
||||||
|
if (!chan[i].inPorta) {
|
||||||
|
if (chan[i].std.arpMode) {
|
||||||
|
chan[i].baseFreq=NoteX1_010(i,chan[i].std.arp);
|
||||||
|
} else {
|
||||||
|
chan[i].baseFreq=NoteX1_010(i,chan[i].note+chan[i].std.arp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chan[i].freqChanged=true;
|
||||||
|
} else {
|
||||||
|
if (chan[i].std.arpMode && chan[i].std.finishedArp) {
|
||||||
|
chan[i].baseFreq=NoteX1_010(i,chan[i].note);
|
||||||
|
chan[i].freqChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chan[i].std.hadWave && !chan[i].pcm) {
|
||||||
|
if (chan[i].wave!=chan[i].std.wave) {
|
||||||
|
chan[i].wave=chan[i].std.wave;
|
||||||
|
if (!chan[i].pcm) {
|
||||||
|
updateWave(i);
|
||||||
|
if (!chan[i].keyOff) chan[i].keyOn=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chan[i].std.hadEx1) {
|
||||||
|
bool nextEnable=(chan[i].std.ex1&1);
|
||||||
|
if (nextEnable!=(chan[i].env.flag.envEnable)) {
|
||||||
|
chan[i].env.flag.envEnable=nextEnable;
|
||||||
|
if (!chan[i].pcm) {
|
||||||
|
if (!isMuted[i]) {
|
||||||
|
chan[i].envChanged=true;
|
||||||
|
}
|
||||||
|
refreshControl(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool nextOneshot=(chan[i].std.ex1&2);
|
||||||
|
if (nextOneshot!=(chan[i].env.flag.envOneshot)) {
|
||||||
|
chan[i].env.flag.envOneshot=nextOneshot;
|
||||||
|
if (!chan[i].pcm) {
|
||||||
|
refreshControl(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool nextSplit=(chan[i].std.ex1&4);
|
||||||
|
if (nextSplit!=(chan[i].env.flag.envSplit)) {
|
||||||
|
chan[i].env.flag.envSplit=nextSplit;
|
||||||
|
if (!isMuted[i] && !chan[i].pcm) {
|
||||||
|
chan[i].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool nextHinvR=(chan[i].std.ex1&8);
|
||||||
|
if (nextHinvR!=(chan[i].env.flag.envHinvR)) {
|
||||||
|
chan[i].env.flag.envHinvR=nextHinvR;
|
||||||
|
if (!isMuted[i] && !chan[i].pcm) {
|
||||||
|
chan[i].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool nextVinvR=(chan[i].std.ex1&16);
|
||||||
|
if (nextVinvR!=(chan[i].env.flag.envVinvR)) {
|
||||||
|
chan[i].env.flag.envVinvR=nextVinvR;
|
||||||
|
if (!isMuted[i] && !chan[i].pcm) {
|
||||||
|
chan[i].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool nextHinvL=(chan[i].std.ex1&32);
|
||||||
|
if (nextHinvL!=(chan[i].env.flag.envHinvL)) {
|
||||||
|
chan[i].env.flag.envHinvL=nextHinvL;
|
||||||
|
if (!isMuted[i] && !chan[i].pcm) {
|
||||||
|
chan[i].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool nextVinvL=(chan[i].std.ex1&64);
|
||||||
|
if (nextVinvL!=(chan[i].env.flag.envVinvL)) {
|
||||||
|
chan[i].env.flag.envVinvL=nextVinvL;
|
||||||
|
if (!isMuted[i] && !chan[i].pcm) {
|
||||||
|
chan[i].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chan[i].std.hadEx2) {
|
||||||
|
if (chan[i].env.shape!=chan[i].std.ex2) {
|
||||||
|
chan[i].env.shape=chan[i].std.ex2;
|
||||||
|
if (!chan[i].pcm) {
|
||||||
|
if (chan[i].env.flag.envEnable && (!isMuted[i])) {
|
||||||
|
chan[i].envChanged=true;
|
||||||
|
}
|
||||||
|
if (!chan[i].keyOff) chan[i].keyOn=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chan[i].std.hadEx3) {
|
||||||
|
chan[i].autoEnvNum=chan[i].std.ex3;
|
||||||
|
if (!chan[i].pcm) {
|
||||||
|
chan[i].freqChanged=true;
|
||||||
|
if (!chan[i].std.willAlg) chan[i].autoEnvDen=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chan[i].std.hadAlg) {
|
||||||
|
chan[i].autoEnvDen=chan[i].std.alg;
|
||||||
|
if (!chan[i].pcm) {
|
||||||
|
chan[i].freqChanged=true;
|
||||||
|
if (!chan[i].std.willEx3) chan[i].autoEnvNum=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chan[i].envChanged) {
|
||||||
|
if (!isMuted[i]) {
|
||||||
|
chan[i].lvol=((chan[i].outVol&0xf)*((chan[i].pan>>4)&0xf))/15;
|
||||||
|
chan[i].rvol=((chan[i].outVol&0xf)*((chan[i].pan>>0)&0xf))/15;
|
||||||
|
}
|
||||||
|
updateEnvelope(i);
|
||||||
|
chan[i].envChanged=false;
|
||||||
|
}
|
||||||
|
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||||
|
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false);
|
||||||
|
if (chan[i].pcm) {
|
||||||
|
if (chan[i].freq<1) chan[i].freq=1;
|
||||||
|
if (chan[i].freq>255) chan[i].freq=255;
|
||||||
|
chWrite(i,2,chan[i].freq&0xff);
|
||||||
|
} else {
|
||||||
|
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||||
|
chWrite(i,2,chan[i].freq&0xff);
|
||||||
|
chWrite(i,3,(chan[i].freq>>8)&0xff);
|
||||||
|
if (chan[i].freqChanged && chan[i].autoEnvNum>0 && chan[i].autoEnvDen>0) {
|
||||||
|
chan[i].env.period=(chan[i].freq*chan[i].autoEnvDen/chan[i].autoEnvNum)>>12;
|
||||||
|
chWrite(i,4,chan[i].env.period);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chan[i].keyOn || chan[i].keyOff || (chRead(i,0)&1)) {
|
||||||
|
refreshControl(i);
|
||||||
|
}
|
||||||
|
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||||
|
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||||
|
chan[i].freqChanged=false;
|
||||||
|
}
|
||||||
|
if (chan[i].env.slide!=0) {
|
||||||
|
chan[i].env.slidefrac+=chan[i].env.slide;
|
||||||
|
while (chan[i].env.slidefrac>0xf) {
|
||||||
|
chan[i].env.slidefrac-=0x10;
|
||||||
|
if (chan[i].env.period<0xff) {
|
||||||
|
chan[i].env.period++;
|
||||||
|
if (!chan[i].pcm) {
|
||||||
|
chWrite(i,4,chan[i].env.period);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (chan[i].env.slidefrac<-0xf) {
|
||||||
|
chan[i].env.slidefrac+=0x10;
|
||||||
|
if (chan[i].env.period>0) {
|
||||||
|
chan[i].env.period--;
|
||||||
|
if (!chan[i].pcm) {
|
||||||
|
chWrite(i,4,chan[i].env.period);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int DivPlatformX1_010::dispatch(DivCommand c) {
|
||||||
|
switch (c.cmd) {
|
||||||
|
case DIV_CMD_NOTE_ON: {
|
||||||
|
chWrite(c.chan,0,0); // reset previous note
|
||||||
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||||
|
if ((ins->type==DIV_INS_AMIGA) || chan[c.chan].pcm) {
|
||||||
|
if (ins->type==DIV_INS_AMIGA) {
|
||||||
|
chan[c.chan].furnacePCM=true;
|
||||||
|
} else {
|
||||||
|
chan[c.chan].furnacePCM=false;
|
||||||
|
}
|
||||||
|
if (skipRegisterWrites) break;
|
||||||
|
if (chan[c.chan].furnacePCM) {
|
||||||
|
chan[c.chan].pcm=true;
|
||||||
|
chan[c.chan].std.init(ins);
|
||||||
|
chan[c.chan].sample=ins->amiga.initSample;
|
||||||
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
|
chWrite(c.chan,4,(s->offX1_010>>12)&0xff);
|
||||||
|
int end=(s->offX1_010+s->length8+0xfff)&~0xfff; // padded
|
||||||
|
chWrite(c.chan,5,(0x100-(end>>12))&0xff);
|
||||||
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].note=c.value;
|
||||||
|
chan[c.chan].baseFreq=NoteX1_010(c.chan,chan[c.chan].note);
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chan[c.chan].std.init(NULL);
|
||||||
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
|
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
|
||||||
|
chWrite(c.chan,0,0); // reset
|
||||||
|
chWrite(c.chan,1,0);
|
||||||
|
chWrite(c.chan,2,0);
|
||||||
|
chWrite(c.chan,4,0);
|
||||||
|
chWrite(c.chan,5,0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chan[c.chan].std.init(NULL);
|
||||||
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
|
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
|
||||||
|
chWrite(c.chan,0,0); // reset
|
||||||
|
chWrite(c.chan,1,0);
|
||||||
|
chWrite(c.chan,2,0);
|
||||||
|
chWrite(c.chan,4,0);
|
||||||
|
chWrite(c.chan,5,0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DivSample* s=parent->getSample(12*sampleBank+c.value%12);
|
||||||
|
chWrite(c.chan,4,(s->offX1_010>>12)&0xff);
|
||||||
|
int end=(s->offX1_010+s->length8+0xfff)&~0xfff; // padded
|
||||||
|
chWrite(c.chan,5,(0x100-(end>>12))&0xff);
|
||||||
|
chan[c.chan].baseFreq=(((unsigned int)s->rate)<<4)/(chipClock/512);
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
}
|
||||||
|
} else if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].note=c.value;
|
||||||
|
chan[c.chan].baseFreq=NoteX1_010(c.chan,chan[c.chan].note);
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
}
|
||||||
|
chan[c.chan].active=true;
|
||||||
|
chan[c.chan].keyOn=true;
|
||||||
|
chan[c.chan].envChanged=true;
|
||||||
|
chan[c.chan].std.init(ins);
|
||||||
|
refreshControl(c.chan);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DIV_CMD_NOTE_OFF:
|
||||||
|
chan[c.chan].pcm=false;
|
||||||
|
chan[c.chan].active=false;
|
||||||
|
chan[c.chan].keyOff=true;
|
||||||
|
chan[c.chan].std.init(NULL);
|
||||||
|
break;
|
||||||
|
case DIV_CMD_NOTE_OFF_ENV:
|
||||||
|
case DIV_CMD_ENV_RELEASE:
|
||||||
|
chan[c.chan].std.release();
|
||||||
|
break;
|
||||||
|
case DIV_CMD_INSTRUMENT:
|
||||||
|
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||||
|
chan[c.chan].ins=c.value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DIV_CMD_VOLUME:
|
||||||
|
if (chan[c.chan].vol!=c.value) {
|
||||||
|
chan[c.chan].vol=c.value;
|
||||||
|
if (!chan[c.chan].std.hasVol) {
|
||||||
|
if (chan[c.chan].outVol!=c.value) {
|
||||||
|
chan[c.chan].outVol=c.value;
|
||||||
|
if (!isMuted[c.chan]) {
|
||||||
|
chan[c.chan].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DIV_CMD_GET_VOLUME:
|
||||||
|
if (chan[c.chan].std.hasVol) {
|
||||||
|
return chan[c.chan].vol;
|
||||||
|
}
|
||||||
|
return chan[c.chan].outVol;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_PITCH:
|
||||||
|
chan[c.chan].pitch=c.value;
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_WAVE:
|
||||||
|
chan[c.chan].wave=c.value;
|
||||||
|
updateWave(c.chan);
|
||||||
|
chan[c.chan].keyOn=true;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_X1_010_ENVELOPE_SHAPE:
|
||||||
|
if (chan[c.chan].env.shape!=c.value) {
|
||||||
|
chan[c.chan].env.shape=c.value;
|
||||||
|
if (!chan[c.chan].pcm) {
|
||||||
|
if (chan[c.chan].env.flag.envEnable && (!isMuted[c.chan])) {
|
||||||
|
chan[c.chan].envChanged=true;
|
||||||
|
}
|
||||||
|
chan[c.chan].keyOn=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DIV_CMD_NOTE_PORTA: {
|
||||||
|
int destFreq=NoteX1_010(c.chan,c.value2);
|
||||||
|
bool return2=false;
|
||||||
|
if (destFreq>chan[c.chan].baseFreq) {
|
||||||
|
chan[c.chan].baseFreq+=c.value;
|
||||||
|
if (chan[c.chan].baseFreq>=destFreq) {
|
||||||
|
chan[c.chan].baseFreq=destFreq;
|
||||||
|
return2=true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chan[c.chan].baseFreq-=c.value;
|
||||||
|
if (chan[c.chan].baseFreq<=destFreq) {
|
||||||
|
chan[c.chan].baseFreq=destFreq;
|
||||||
|
return2=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
if (return2) {
|
||||||
|
chan[c.chan].inPorta=false;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DIV_CMD_SAMPLE_MODE:
|
||||||
|
if (chan[c.chan].pcm!=(c.value&1)) {
|
||||||
|
chan[c.chan].pcm=c.value&1;
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
if (!isMuted[c.chan]) {
|
||||||
|
chan[c.chan].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DIV_CMD_SAMPLE_BANK:
|
||||||
|
sampleBank=c.value;
|
||||||
|
if (sampleBank>(parent->song.sample.size()/12)) {
|
||||||
|
sampleBank=parent->song.sample.size()/12;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DIV_CMD_PANNING: {
|
||||||
|
if (chan[c.chan].pan!=c.value) {
|
||||||
|
chan[c.chan].pan=c.value;
|
||||||
|
if (!isMuted[c.chan]) {
|
||||||
|
chan[c.chan].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DIV_CMD_LEGATO:
|
||||||
|
chan[c.chan].note=c.value;
|
||||||
|
chan[c.chan].baseFreq=NoteX1_010(c.chan,chan[c.chan].note+((chan[c.chan].std.willArp&&!chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0)));
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_PRE_PORTA:
|
||||||
|
if (chan[c.chan].active && c.value2) {
|
||||||
|
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
|
||||||
|
}
|
||||||
|
chan[c.chan].inPorta=c.value;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_SAMPLE_FREQ:
|
||||||
|
if (chan[c.chan].pcm) {
|
||||||
|
chan[c.chan].freq=MAX(1,c.value&0xff);
|
||||||
|
chWrite(c.chan,2,chan[c.chan].freq&0xff);
|
||||||
|
if (chRead(c.chan,0)&1) {
|
||||||
|
refreshControl(c.chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DIV_CMD_X1_010_ENVELOPE_MODE: {
|
||||||
|
bool nextEnable=c.value&1;
|
||||||
|
if (nextEnable!=(chan[c.chan].env.flag.envEnable)) {
|
||||||
|
chan[c.chan].env.flag.envEnable=nextEnable;
|
||||||
|
if (!chan[c.chan].pcm) {
|
||||||
|
if (!isMuted[c.chan]) {
|
||||||
|
chan[c.chan].envChanged=true;
|
||||||
|
}
|
||||||
|
refreshControl(c.chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool nextOneshot=c.value&2;
|
||||||
|
if (nextOneshot!=(chan[c.chan].env.flag.envOneshot)) {
|
||||||
|
chan[c.chan].env.flag.envOneshot=nextOneshot;
|
||||||
|
if (!chan[c.chan].pcm) {
|
||||||
|
refreshControl(c.chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool nextSplit=c.value&4;
|
||||||
|
if (nextSplit!=(chan[c.chan].env.flag.envSplit)) {
|
||||||
|
chan[c.chan].env.flag.envSplit=nextSplit;
|
||||||
|
if (!isMuted[c.chan] && !chan[c.chan].pcm) {
|
||||||
|
chan[c.chan].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool nextHinvR=c.value&8;
|
||||||
|
if (nextHinvR!=(chan[c.chan].env.flag.envHinvR)) {
|
||||||
|
chan[c.chan].env.flag.envHinvR=nextHinvR;
|
||||||
|
if (!isMuted[c.chan] && !chan[c.chan].pcm) {
|
||||||
|
chan[c.chan].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool nextVinvR=c.value&16;
|
||||||
|
if (nextVinvR!=(chan[c.chan].env.flag.envVinvR)) {
|
||||||
|
chan[c.chan].env.flag.envVinvR=nextVinvR;
|
||||||
|
if (!isMuted[c.chan] && !chan[c.chan].pcm) {
|
||||||
|
chan[c.chan].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool nextHinvL=c.value&32;
|
||||||
|
if (nextHinvL!=(chan[c.chan].env.flag.envHinvL)) {
|
||||||
|
chan[c.chan].env.flag.envHinvL=nextHinvL;
|
||||||
|
if (!isMuted[c.chan] && !chan[c.chan].pcm) {
|
||||||
|
chan[c.chan].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool nextVinvL=c.value&64;
|
||||||
|
if (nextVinvL!=(chan[c.chan].env.flag.envVinvL)) {
|
||||||
|
chan[c.chan].env.flag.envVinvL=nextVinvL;
|
||||||
|
if (!isMuted[c.chan] && !chan[c.chan].pcm) {
|
||||||
|
chan[c.chan].envChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DIV_CMD_X1_010_ENVELOPE_PERIOD:
|
||||||
|
chan[c.chan].env.period=c.value;
|
||||||
|
if (!chan[c.chan].pcm) {
|
||||||
|
chWrite(c.chan,4,chan[c.chan].env.period);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DIV_CMD_X1_010_ENVELOPE_SLIDE:
|
||||||
|
chan[c.chan].env.slide=c.value;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_X1_010_AUTO_ENVELOPE:
|
||||||
|
chan[c.chan].autoEnvNum=c.value>>4;
|
||||||
|
chan[c.chan].autoEnvDen=c.value&15;
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
break;
|
||||||
|
case DIV_CMD_GET_VOLMAX:
|
||||||
|
return 15;
|
||||||
|
break;
|
||||||
|
case DIV_ALWAYS_SET_VOLUME:
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::muteChannel(int ch, bool mute) {
|
||||||
|
isMuted[ch]=mute;
|
||||||
|
chan[ch].envChanged=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::forceIns() {
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
chan[i].insChanged=true;
|
||||||
|
chan[i].envChanged=true;
|
||||||
|
chan[i].freqChanged=true;
|
||||||
|
updateWave(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* DivPlatformX1_010::getChanState(int ch) {
|
||||||
|
return &chan[ch];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* DivPlatformX1_010::getRegisterPool() {
|
||||||
|
for (int i=0; i<0x2000; i++) {
|
||||||
|
regPool[i]=x1_010->ram_r(i);
|
||||||
|
}
|
||||||
|
return regPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DivPlatformX1_010::getRegisterPoolSize() {
|
||||||
|
return 0x2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::reset() {
|
||||||
|
memset(regPool,0,0x2000);
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
chan[i]=DivPlatformX1_010::Channel();
|
||||||
|
chan[i].reset();
|
||||||
|
}
|
||||||
|
x1_010->reset();
|
||||||
|
sampleBank=0;
|
||||||
|
// set per-channel initial panning
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
chWrite(i,0,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivPlatformX1_010::isStereo() {
|
||||||
|
return stereo;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivPlatformX1_010::keyOffAffectsArp(int ch) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::notifyWaveChange(int wave) {
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
if (chan[i].wave==wave) {
|
||||||
|
updateWave(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::notifyInsDeletion(void* ins) {
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::setFlags(unsigned int flags) {
|
||||||
|
switch (flags&15) {
|
||||||
|
case 0: // 16MHz (earlier hardwares)
|
||||||
|
chipClock=16000000;
|
||||||
|
break;
|
||||||
|
case 1: // 16.67MHz (later hardwares)
|
||||||
|
chipClock=50000000.0/3.0;
|
||||||
|
break;
|
||||||
|
// Other clock is used?
|
||||||
|
}
|
||||||
|
rate=chipClock/512;
|
||||||
|
stereo=flags&16;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::poke(unsigned int addr, unsigned short val) {
|
||||||
|
rWrite(addr,val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::poke(std::vector<DivRegWrite>& wlist) {
|
||||||
|
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DivPlatformX1_010::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||||
|
parent=p;
|
||||||
|
dumpWrites=false;
|
||||||
|
skipRegisterWrites=false;
|
||||||
|
stereo=false;
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
isMuted[i]=false;
|
||||||
|
}
|
||||||
|
setFlags(flags);
|
||||||
|
intf.parent=parent;
|
||||||
|
x1_010=new x1_010_core(intf);
|
||||||
|
x1_010->reset();
|
||||||
|
reset();
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformX1_010::quit() {
|
||||||
|
delete x1_010;
|
||||||
|
}
|
||||||
|
|
||||||
|
DivPlatformX1_010::~DivPlatformX1_010() {
|
||||||
|
}
|
144
src/engine/platform/x1_010.h
Normal file
144
src/engine/platform/x1_010.h
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _X1_010_H
|
||||||
|
#define _X1_010_H
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include "../dispatch.h"
|
||||||
|
#include "../engine.h"
|
||||||
|
#include "../macroInt.h"
|
||||||
|
#include "sound/x1_010/x1_010.hpp"
|
||||||
|
|
||||||
|
class DivX1_010Interface: public x1_010_mem_intf {
|
||||||
|
public:
|
||||||
|
DivEngine* parent;
|
||||||
|
int sampleBank;
|
||||||
|
virtual u8 read_byte(u32 address) override {
|
||||||
|
if (parent->x1_010Mem==NULL) return 0;
|
||||||
|
return parent->x1_010Mem[address & 0xfffff];
|
||||||
|
}
|
||||||
|
DivX1_010Interface(): parent(NULL), sampleBank(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DivPlatformX1_010: public DivDispatch {
|
||||||
|
struct Channel {
|
||||||
|
struct Envelope {
|
||||||
|
struct EnvFlag {
|
||||||
|
unsigned char envEnable : 1;
|
||||||
|
unsigned char envOneshot : 1;
|
||||||
|
unsigned char envSplit : 1;
|
||||||
|
unsigned char envHinvR : 1;
|
||||||
|
unsigned char envVinvR : 1;
|
||||||
|
unsigned char envHinvL : 1;
|
||||||
|
unsigned char envVinvL : 1;
|
||||||
|
void reset() {
|
||||||
|
envEnable=0;
|
||||||
|
envOneshot=0;
|
||||||
|
envSplit=0;
|
||||||
|
envHinvR=0;
|
||||||
|
envVinvR=0;
|
||||||
|
envHinvL=0;
|
||||||
|
envVinvL=0;
|
||||||
|
}
|
||||||
|
EnvFlag():
|
||||||
|
envEnable(0),
|
||||||
|
envOneshot(0),
|
||||||
|
envSplit(0),
|
||||||
|
envHinvR(0),
|
||||||
|
envVinvR(0),
|
||||||
|
envHinvL(0),
|
||||||
|
envVinvL(0) {}
|
||||||
|
};
|
||||||
|
int shape, period, slide, slidefrac;
|
||||||
|
EnvFlag flag;
|
||||||
|
void reset() {
|
||||||
|
shape=-1;
|
||||||
|
period=0;
|
||||||
|
flag.reset();
|
||||||
|
}
|
||||||
|
Envelope():
|
||||||
|
shape(-1),
|
||||||
|
period(0),
|
||||||
|
slide(0),
|
||||||
|
slidefrac(0) {}
|
||||||
|
};
|
||||||
|
int freq, baseFreq, pitch, note;
|
||||||
|
int wave, sample, ins;
|
||||||
|
unsigned char pan, autoEnvNum, autoEnvDen;
|
||||||
|
bool active, insChanged, envChanged, freqChanged, keyOn, keyOff, inPorta, furnacePCM, pcm;
|
||||||
|
int vol, outVol, lvol, rvol;
|
||||||
|
unsigned char waveBank;
|
||||||
|
Envelope env;
|
||||||
|
DivMacroInt std;
|
||||||
|
void reset() {
|
||||||
|
freq = baseFreq = pitch = note = 0;
|
||||||
|
wave = sample = ins = -1;
|
||||||
|
pan = 255;
|
||||||
|
autoEnvNum = autoEnvDen = 0;
|
||||||
|
active = false;
|
||||||
|
insChanged = envChanged = freqChanged = true;
|
||||||
|
keyOn = keyOff = inPorta = furnacePCM = pcm = false;
|
||||||
|
vol = outVol = lvol = rvol = 15;
|
||||||
|
waveBank = 0;
|
||||||
|
}
|
||||||
|
Channel():
|
||||||
|
freq(0), baseFreq(0), pitch(0), note(0),
|
||||||
|
wave(-1), sample(-1), ins(-1),
|
||||||
|
pan(255), autoEnvNum(0), autoEnvDen(0),
|
||||||
|
active(false), insChanged(true), envChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), furnacePCM(false), pcm(false),
|
||||||
|
vol(15), outVol(15), lvol(15), rvol(15),
|
||||||
|
waveBank(0) {}
|
||||||
|
};
|
||||||
|
Channel chan[16];
|
||||||
|
bool isMuted[16];
|
||||||
|
bool stereo=false;
|
||||||
|
unsigned char sampleBank;
|
||||||
|
DivX1_010Interface intf;
|
||||||
|
x1_010_core* x1_010;
|
||||||
|
unsigned char regPool[0x2000];
|
||||||
|
double NoteX1_010(int ch, int note);
|
||||||
|
void updateWave(int ch);
|
||||||
|
void updateEnvelope(int ch);
|
||||||
|
friend void putDispatchChan(void*,int,int);
|
||||||
|
public:
|
||||||
|
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||||
|
int dispatch(DivCommand c);
|
||||||
|
void* getChanState(int chan);
|
||||||
|
unsigned char* getRegisterPool();
|
||||||
|
int getRegisterPoolSize();
|
||||||
|
void reset();
|
||||||
|
void forceIns();
|
||||||
|
void tick();
|
||||||
|
void muteChannel(int ch, bool mute);
|
||||||
|
bool isStereo();
|
||||||
|
bool keyOffAffectsArp(int ch);
|
||||||
|
void setFlags(unsigned int flags);
|
||||||
|
void notifyWaveChange(int wave);
|
||||||
|
void notifyInsDeletion(void* ins);
|
||||||
|
void poke(unsigned int addr, unsigned short val);
|
||||||
|
void poke(std::vector<DivRegWrite>& wlist);
|
||||||
|
const char** getRegisterSheet();
|
||||||
|
const char* getEffectName(unsigned char effect);
|
||||||
|
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||||
|
void quit();
|
||||||
|
~DivPlatformX1_010();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -313,11 +313,22 @@ const char* DivPlatformYM2610::getEffectName(unsigned char effect) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double DivPlatformYM2610::NOTE_OPNB(int ch, int note) {
|
||||||
|
if (ch>6) { // ADPCM
|
||||||
|
return NOTE_ADPCMB(note);
|
||||||
|
} else if (ch>3) { // PSG
|
||||||
|
return NOTE_PERIODIC(note);
|
||||||
|
}
|
||||||
|
// FM
|
||||||
|
return NOTE_FREQUENCY(note);
|
||||||
|
}
|
||||||
|
|
||||||
double DivPlatformYM2610::NOTE_ADPCMB(int note) {
|
double DivPlatformYM2610::NOTE_ADPCMB(int note) {
|
||||||
DivInstrument* ins=parent->getIns(chan[13].ins);
|
if (chan[13].sample>=0 && chan[13].sample<parent->song.sampleLen) {
|
||||||
if (ins->type!=DIV_INS_AMIGA) return 0;
|
double off=(double)(parent->getSample(chan[13].sample)->centerRate)/8363.0;
|
||||||
double off=(double)(parent->getSample(ins->amiga.initSample)->centerRate)/8363.0;
|
|
||||||
return off*parent->calcBaseFreq((double)chipClock/144,65535,note,false);
|
return off*parent->calcBaseFreq((double)chipClock/144,65535,note,false);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||||
|
@ -691,7 +702,9 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
immWrite(0x1b,chan[c.chan].outVol);
|
immWrite(0x1b,chan[c.chan].outVol);
|
||||||
}
|
}
|
||||||
DivSample* s=parent->getSample(ins->amiga.initSample);
|
chan[c.chan].sample=ins->amiga.initSample;
|
||||||
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
immWrite(0x12,(s->offB>>8)&0xff);
|
immWrite(0x12,(s->offB>>8)&0xff);
|
||||||
immWrite(0x13,s->offB>>16);
|
immWrite(0x13,s->offB>>16);
|
||||||
int end=s->offB+s->lengthB-1;
|
int end=s->offB+s->lengthB-1;
|
||||||
|
@ -707,6 +720,15 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||||
chan[c.chan].active=true;
|
chan[c.chan].active=true;
|
||||||
chan[c.chan].keyOn=true;
|
chan[c.chan].keyOn=true;
|
||||||
} else {
|
} else {
|
||||||
|
immWrite(0x10,0x01); // reset
|
||||||
|
immWrite(0x12,0);
|
||||||
|
immWrite(0x13,0);
|
||||||
|
immWrite(0x14,0);
|
||||||
|
immWrite(0x15,0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chan[c.chan].sample=-1;
|
||||||
chan[c.chan].std.init(NULL);
|
chan[c.chan].std.init(NULL);
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
|
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
|
||||||
|
@ -915,8 +937,8 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_NOTE_PORTA: {
|
case DIV_CMD_NOTE_PORTA: {
|
||||||
if (c.chan>3) { // PSG
|
if (c.chan>3) { // PSG, ADPCM-B
|
||||||
int destFreq=NOTE_PERIODIC(c.value2);
|
int destFreq=NOTE_OPNB(c.chan,c.value2);
|
||||||
bool return2=false;
|
bool return2=false;
|
||||||
if (destFreq>chan[c.chan].baseFreq) {
|
if (destFreq>chan[c.chan].baseFreq) {
|
||||||
chan[c.chan].baseFreq+=c.value;
|
chan[c.chan].baseFreq+=c.value;
|
||||||
|
@ -975,11 +997,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||||
iface.sampleBank=sampleBank;
|
iface.sampleBank=sampleBank;
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_LEGATO: {
|
case DIV_CMD_LEGATO: {
|
||||||
if (c.chan>3) { // PSG
|
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
|
||||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
|
||||||
} else {
|
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
|
||||||
}
|
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1210,11 +1228,6 @@ void DivPlatformYM2610::reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
lastBusy=60;
|
lastBusy=60;
|
||||||
dacMode=0;
|
|
||||||
dacPeriod=0;
|
|
||||||
dacPos=0;
|
|
||||||
dacRate=0;
|
|
||||||
dacSample=-1;
|
|
||||||
sampleBank=0;
|
sampleBank=0;
|
||||||
ayEnvPeriod=0;
|
ayEnvPeriod=0;
|
||||||
ayEnvMode=0;
|
ayEnvMode=0;
|
||||||
|
|
|
@ -47,6 +47,7 @@ class DivPlatformYM2610: public DivDispatch {
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
|
int sample;
|
||||||
unsigned char pan;
|
unsigned char pan;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
Channel():
|
Channel():
|
||||||
|
@ -70,6 +71,7 @@ class DivPlatformYM2610: public DivDispatch {
|
||||||
furnacePCM(false),
|
furnacePCM(false),
|
||||||
vol(0),
|
vol(0),
|
||||||
outVol(15),
|
outVol(15),
|
||||||
|
sample(-1),
|
||||||
pan(3) {}
|
pan(3) {}
|
||||||
};
|
};
|
||||||
Channel chan[14];
|
Channel chan[14];
|
||||||
|
@ -87,11 +89,6 @@ class DivPlatformYM2610: public DivDispatch {
|
||||||
unsigned char regPool[512];
|
unsigned char regPool[512];
|
||||||
unsigned char lastBusy;
|
unsigned char lastBusy;
|
||||||
|
|
||||||
bool dacMode;
|
|
||||||
int dacPeriod;
|
|
||||||
int dacRate;
|
|
||||||
int dacPos;
|
|
||||||
int dacSample;
|
|
||||||
int ayNoiseFreq;
|
int ayNoiseFreq;
|
||||||
unsigned char sampleBank;
|
unsigned char sampleBank;
|
||||||
|
|
||||||
|
@ -108,6 +105,7 @@ class DivPlatformYM2610: public DivDispatch {
|
||||||
|
|
||||||
int octave(int freq);
|
int octave(int freq);
|
||||||
int toFreq(int freq);
|
int toFreq(int freq);
|
||||||
|
double NOTE_OPNB(int ch, int note);
|
||||||
double NOTE_ADPCMB(int note);
|
double NOTE_ADPCMB(int note);
|
||||||
friend void putDispatchChan(void*,int,int);
|
friend void putDispatchChan(void*,int,int);
|
||||||
|
|
||||||
|
|
|
@ -377,11 +377,22 @@ const char* DivPlatformYM2610B::getEffectName(unsigned char effect) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double DivPlatformYM2610B::NOTE_OPNB(int ch, int note) {
|
||||||
|
if (ch>8) { // ADPCM-B
|
||||||
|
return NOTE_ADPCMB(note);
|
||||||
|
} else if (ch>5) { // PSG
|
||||||
|
return NOTE_PERIODIC(note);
|
||||||
|
}
|
||||||
|
// FM
|
||||||
|
return NOTE_FREQUENCY(note);
|
||||||
|
}
|
||||||
|
|
||||||
double DivPlatformYM2610B::NOTE_ADPCMB(int note) {
|
double DivPlatformYM2610B::NOTE_ADPCMB(int note) {
|
||||||
DivInstrument* ins=parent->getIns(chan[15].ins);
|
if (chan[15].sample>=0 && chan[15].sample<parent->song.sampleLen) {
|
||||||
if (ins->type!=DIV_INS_AMIGA) return 0;
|
double off=(double)(parent->getSample(chan[15].sample)->centerRate)/8363.0;
|
||||||
double off=(double)(parent->getSample(ins->amiga.initSample)->centerRate)/8363.0;
|
|
||||||
return off*parent->calcBaseFreq((double)chipClock/144,65535,note,false);
|
return off*parent->calcBaseFreq((double)chipClock/144,65535,note,false);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||||
|
@ -754,7 +765,9 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
immWrite(0x1b,chan[c.chan].outVol);
|
immWrite(0x1b,chan[c.chan].outVol);
|
||||||
}
|
}
|
||||||
DivSample* s=parent->getSample(ins->amiga.initSample);
|
chan[c.chan].sample=ins->amiga.initSample;
|
||||||
|
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||||
|
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||||
immWrite(0x12,(s->offB>>8)&0xff);
|
immWrite(0x12,(s->offB>>8)&0xff);
|
||||||
immWrite(0x13,s->offB>>16);
|
immWrite(0x13,s->offB>>16);
|
||||||
int end=s->offB+s->lengthB-1;
|
int end=s->offB+s->lengthB-1;
|
||||||
|
@ -770,6 +783,15 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
||||||
chan[c.chan].active=true;
|
chan[c.chan].active=true;
|
||||||
chan[c.chan].keyOn=true;
|
chan[c.chan].keyOn=true;
|
||||||
} else {
|
} else {
|
||||||
|
immWrite(0x10,0x01); // reset
|
||||||
|
immWrite(0x12,0);
|
||||||
|
immWrite(0x13,0);
|
||||||
|
immWrite(0x14,0);
|
||||||
|
immWrite(0x15,0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chan[c.chan].sample=-1;
|
||||||
chan[c.chan].std.init(NULL);
|
chan[c.chan].std.init(NULL);
|
||||||
chan[c.chan].outVol=chan[c.chan].vol;
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
|
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
|
||||||
|
@ -978,8 +1000,8 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_NOTE_PORTA: {
|
case DIV_CMD_NOTE_PORTA: {
|
||||||
if (c.chan>5) { // PSG
|
if (c.chan>5) { // PSG, ADPCM-B
|
||||||
int destFreq=NOTE_PERIODIC(c.value2);
|
int destFreq=NOTE_OPNB(c.chan,c.value2);
|
||||||
bool return2=false;
|
bool return2=false;
|
||||||
if (destFreq>chan[c.chan].baseFreq) {
|
if (destFreq>chan[c.chan].baseFreq) {
|
||||||
chan[c.chan].baseFreq+=c.value;
|
chan[c.chan].baseFreq+=c.value;
|
||||||
|
@ -1038,11 +1060,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
||||||
iface.sampleBank=sampleBank;
|
iface.sampleBank=sampleBank;
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_LEGATO: {
|
case DIV_CMD_LEGATO: {
|
||||||
if (c.chan>5) { // PSG
|
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
|
||||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
|
||||||
} else {
|
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
|
||||||
}
|
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1273,11 +1291,6 @@ void DivPlatformYM2610B::reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
lastBusy=60;
|
lastBusy=60;
|
||||||
dacMode=0;
|
|
||||||
dacPeriod=0;
|
|
||||||
dacPos=0;
|
|
||||||
dacRate=0;
|
|
||||||
dacSample=-1;
|
|
||||||
sampleBank=0;
|
sampleBank=0;
|
||||||
ayEnvPeriod=0;
|
ayEnvPeriod=0;
|
||||||
ayEnvMode=0;
|
ayEnvMode=0;
|
||||||
|
|
|
@ -40,6 +40,7 @@ class DivPlatformYM2610B: public DivDispatch {
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
|
int sample;
|
||||||
unsigned char pan;
|
unsigned char pan;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
Channel():
|
Channel():
|
||||||
|
@ -63,6 +64,7 @@ class DivPlatformYM2610B: public DivDispatch {
|
||||||
furnacePCM(false),
|
furnacePCM(false),
|
||||||
vol(0),
|
vol(0),
|
||||||
outVol(15),
|
outVol(15),
|
||||||
|
sample(-1),
|
||||||
pan(3) {}
|
pan(3) {}
|
||||||
};
|
};
|
||||||
Channel chan[16];
|
Channel chan[16];
|
||||||
|
@ -80,11 +82,6 @@ class DivPlatformYM2610B: public DivDispatch {
|
||||||
unsigned char regPool[512];
|
unsigned char regPool[512];
|
||||||
unsigned char lastBusy;
|
unsigned char lastBusy;
|
||||||
|
|
||||||
bool dacMode;
|
|
||||||
int dacPeriod;
|
|
||||||
int dacRate;
|
|
||||||
int dacPos;
|
|
||||||
int dacSample;
|
|
||||||
int ayNoiseFreq;
|
int ayNoiseFreq;
|
||||||
unsigned char sampleBank;
|
unsigned char sampleBank;
|
||||||
|
|
||||||
|
@ -101,6 +98,7 @@ class DivPlatformYM2610B: public DivDispatch {
|
||||||
|
|
||||||
int octave(int freq);
|
int octave(int freq);
|
||||||
int toFreq(int freq);
|
int toFreq(int freq);
|
||||||
|
double NOTE_OPNB(int ch, int note);
|
||||||
double NOTE_ADPCMB(int note);
|
double NOTE_ADPCMB(int note);
|
||||||
friend void putDispatchChan(void*,int,int);
|
friend void putDispatchChan(void*,int,int);
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,16 @@ const char* cmdName[DIV_CMD_MAX]={
|
||||||
"QSOUND_ECHO_DELAY",
|
"QSOUND_ECHO_DELAY",
|
||||||
"QSOUND_ECHO_LEVEL",
|
"QSOUND_ECHO_LEVEL",
|
||||||
|
|
||||||
|
"X1_010_ENVELOPE_SHAPE",
|
||||||
|
"X1_010_ENVELOPE_ENABLE",
|
||||||
|
"X1_010_ENVELOPE_MODE",
|
||||||
|
"X1_010_ENVELOPE_PERIOD",
|
||||||
|
"X1_010_ENVELOPE_SLIDE",
|
||||||
|
"X1_010_AUTO_ENVELOPE",
|
||||||
|
|
||||||
|
"WS_SWEEP_TIME",
|
||||||
|
"WS_SWEEP_AMOUNT",
|
||||||
|
|
||||||
"ALWAYS_SET_VOLUME"
|
"ALWAYS_SET_VOLUME"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -251,6 +261,21 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
|
switch (effect) {
|
||||||
|
case 0x10: // select waveform
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal));
|
||||||
|
break;
|
||||||
|
case 0x11: // select envelope shape
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_SHAPE,ch,effectVal));
|
||||||
|
break;
|
||||||
|
case 0x17: // PCM enable
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case DIV_SYSTEM_SWAN:
|
case DIV_SYSTEM_SWAN:
|
||||||
switch (effect) {
|
switch (effect) {
|
||||||
case 0x10: // select waveform
|
case 0x10: // select waveform
|
||||||
|
@ -272,6 +297,18 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_VERA:
|
||||||
|
switch (effect) {
|
||||||
|
case 0x20: // select waveform
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal));
|
||||||
|
break;
|
||||||
|
case 0x22: // duty
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -555,6 +592,30 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
|
switch (effect) {
|
||||||
|
case 0x20: // PCM frequency
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal));
|
||||||
|
break;
|
||||||
|
case 0x22: // envelope mode
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_MODE,ch,effectVal));
|
||||||
|
break;
|
||||||
|
case 0x23: // envelope period
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_PERIOD,ch,effectVal));
|
||||||
|
break;
|
||||||
|
case 0x25: // envelope slide up
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_SLIDE,ch,effectVal));
|
||||||
|
break;
|
||||||
|
case 0x26: // envelope slide down
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_SLIDE,ch,-effectVal));
|
||||||
|
break;
|
||||||
|
case 0x29: // auto-envelope
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_X1_010_AUTO_ENVELOPE,ch,effectVal));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -593,6 +654,12 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
// instrument
|
// instrument
|
||||||
if (pat->data[whatRow][2]!=-1) {
|
if (pat->data[whatRow][2]!=-1) {
|
||||||
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,i,pat->data[whatRow][2]));
|
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,i,pat->data[whatRow][2]));
|
||||||
|
if (chan[i].lastIns!=pat->data[whatRow][2]) {
|
||||||
|
chan[i].lastIns=pat->data[whatRow][2];
|
||||||
|
if (chan[i].inPorta && song.newInsTriggersInPorta) {
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// note
|
// note
|
||||||
if (pat->data[whatRow][0]==100) { // note off
|
if (pat->data[whatRow][0]==100) { // note off
|
||||||
|
@ -1052,8 +1119,28 @@ void DivEngine::nextRow() {
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][curOrder],false);
|
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][curOrder],false);
|
||||||
if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) {
|
if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) {
|
||||||
if (pat->data[curRow][0]!=100) {
|
if (pat->data[curRow][0]!=100 && pat->data[curRow][0]!=101 && pat->data[curRow][0]!=102) {
|
||||||
if (!chan[i].legato) dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks));
|
if (!chan[i].legato) {
|
||||||
|
dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks));
|
||||||
|
|
||||||
|
if (song.oneTickCut) {
|
||||||
|
bool doPrepareCut=true;
|
||||||
|
|
||||||
|
for (int j=0; j<song.pat[i].effectRows; j++) {
|
||||||
|
if (pat->data[curRow][4+(j<<1)]==0x03) {
|
||||||
|
doPrepareCut=false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (pat->data[curRow][4+(j<<1)]==0xea) {
|
||||||
|
if (pat->data[curRow][5+(j<<1)]>0) {
|
||||||
|
doPrepareCut=false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (doPrepareCut) chan[i].cut=ticks;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,9 +112,9 @@ int SafeReader::readI() {
|
||||||
|
|
||||||
int SafeReader::readI_BE() {
|
int SafeReader::readI_BE() {
|
||||||
if (curSeek+4>len) throw EndOfFileException(this,len);
|
if (curSeek+4>len) throw EndOfFileException(this,len);
|
||||||
int ret=*(int*)(&buf[curSeek]);
|
unsigned int ret=*(unsigned int*)(&buf[curSeek]);
|
||||||
curSeek+=4;
|
curSeek+=4;
|
||||||
return (ret>>24)|((ret&0xff0000)>>8)|((ret&0xff00)<<8)|((ret&0xff)<<24);
|
return (int)((ret>>24)|((ret&0xff0000)>>8)|((ret&0xff00)<<8)|((ret&0xff)<<24));
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t SafeReader::readL() {
|
int64_t SafeReader::readL() {
|
||||||
|
|
|
@ -115,8 +115,9 @@ bool DivSample::initInternal(unsigned char d, int count) {
|
||||||
case 8: // 8-bit
|
case 8: // 8-bit
|
||||||
if (data8!=NULL) delete[] data8;
|
if (data8!=NULL) delete[] data8;
|
||||||
length8=count;
|
length8=count;
|
||||||
data8=new signed char[length8];
|
// for padding X1-010 sample
|
||||||
memset(data8,0,length8);
|
data8=new signed char[(count+4095)&(~0xfff)];
|
||||||
|
memset(data8,0,(count+4095)&(~0xfff));
|
||||||
break;
|
break;
|
||||||
case 9: // BRR
|
case 9: // BRR
|
||||||
if (dataBRR!=NULL) delete[] dataBRR;
|
if (dataBRR!=NULL) delete[] dataBRR;
|
||||||
|
|
|
@ -49,7 +49,7 @@ struct DivSample {
|
||||||
|
|
||||||
unsigned int length8, length16, length1, lengthDPCM, lengthQSoundA, lengthA, lengthB, lengthX68, lengthBRR, lengthVOX;
|
unsigned int length8, length16, length1, lengthDPCM, lengthQSoundA, lengthA, lengthB, lengthX68, lengthBRR, lengthVOX;
|
||||||
unsigned int off8, off16, off1, offDPCM, offQSoundA, offA, offB, offX68, offBRR, offVOX;
|
unsigned int off8, off16, off1, offDPCM, offQSoundA, offA, offB, offX68, offBRR, offVOX;
|
||||||
unsigned int offSegaPCM, offQSound;
|
unsigned int offSegaPCM, offQSound, offX1_010;
|
||||||
|
|
||||||
unsigned int samples;
|
unsigned int samples;
|
||||||
|
|
||||||
|
|
|
@ -90,8 +90,10 @@ enum DivSystem {
|
||||||
DIV_SYSTEM_OPLL_DRUMS,
|
DIV_SYSTEM_OPLL_DRUMS,
|
||||||
DIV_SYSTEM_LYNX,
|
DIV_SYSTEM_LYNX,
|
||||||
DIV_SYSTEM_QSOUND,
|
DIV_SYSTEM_QSOUND,
|
||||||
|
DIV_SYSTEM_VERA,
|
||||||
DIV_SYSTEM_YM2610B_EXT,
|
DIV_SYSTEM_YM2610B_EXT,
|
||||||
DIV_SYSTEM_SEGAPCM_COMPAT
|
DIV_SYSTEM_SEGAPCM_COMPAT,
|
||||||
|
DIV_SYSTEM_X1_010
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DivSong {
|
struct DivSong {
|
||||||
|
@ -241,6 +243,13 @@ struct DivSong {
|
||||||
// - 2: YM2423
|
// - 2: YM2423
|
||||||
// - 3: VRC7
|
// - 3: VRC7
|
||||||
// - 4: custom (TODO)
|
// - 4: custom (TODO)
|
||||||
|
// - X1-010:
|
||||||
|
// - bit 0-3: clock rate
|
||||||
|
// - 0: 16MHz (Seta 1)
|
||||||
|
// - 1: 16.67MHz (Seta 2)
|
||||||
|
// - bit 4: stereo
|
||||||
|
// - 0: mono
|
||||||
|
// - 1: stereo
|
||||||
unsigned int systemFlags[32];
|
unsigned int systemFlags[32];
|
||||||
|
|
||||||
// song information
|
// song information
|
||||||
|
@ -286,6 +295,9 @@ struct DivSong {
|
||||||
bool ignoreDuplicateSlides;
|
bool ignoreDuplicateSlides;
|
||||||
bool stopPortaOnNoteOff;
|
bool stopPortaOnNoteOff;
|
||||||
bool continuousVibrato;
|
bool continuousVibrato;
|
||||||
|
bool brokenDACMode;
|
||||||
|
bool oneTickCut;
|
||||||
|
bool newInsTriggersInPorta;
|
||||||
|
|
||||||
DivOrders orders;
|
DivOrders orders;
|
||||||
std::vector<DivInstrument*> ins;
|
std::vector<DivInstrument*> ins;
|
||||||
|
@ -350,7 +362,10 @@ struct DivSong {
|
||||||
brokenShortcutSlides(false),
|
brokenShortcutSlides(false),
|
||||||
ignoreDuplicateSlides(false),
|
ignoreDuplicateSlides(false),
|
||||||
stopPortaOnNoteOff(false),
|
stopPortaOnNoteOff(false),
|
||||||
continuousVibrato(false) {
|
continuousVibrato(false),
|
||||||
|
brokenDACMode(false),
|
||||||
|
oneTickCut(false),
|
||||||
|
newInsTriggersInPorta(true) {
|
||||||
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;
|
||||||
|
|
|
@ -135,6 +135,10 @@ DivSystem DivEngine::systemFromFile(unsigned char val) {
|
||||||
return DIV_SYSTEM_LYNX;
|
return DIV_SYSTEM_LYNX;
|
||||||
case 0xa9:
|
case 0xa9:
|
||||||
return DIV_SYSTEM_SEGAPCM_COMPAT;
|
return DIV_SYSTEM_SEGAPCM_COMPAT;
|
||||||
|
case 0xac:
|
||||||
|
return DIV_SYSTEM_VERA;
|
||||||
|
case 0xb0:
|
||||||
|
return DIV_SYSTEM_X1_010;
|
||||||
case 0xde:
|
case 0xde:
|
||||||
return DIV_SYSTEM_YM2610B_EXT;
|
return DIV_SYSTEM_YM2610B_EXT;
|
||||||
case 0xe0:
|
case 0xe0:
|
||||||
|
@ -258,6 +262,10 @@ unsigned char DivEngine::systemToFile(DivSystem val) {
|
||||||
return 0xa8;
|
return 0xa8;
|
||||||
case DIV_SYSTEM_SEGAPCM_COMPAT:
|
case DIV_SYSTEM_SEGAPCM_COMPAT:
|
||||||
return 0xa9;
|
return 0xa9;
|
||||||
|
case DIV_SYSTEM_VERA:
|
||||||
|
return 0xac;
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
|
return 0xb0;
|
||||||
case DIV_SYSTEM_YM2610B_EXT:
|
case DIV_SYSTEM_YM2610B_EXT:
|
||||||
return 0xde;
|
return 0xde;
|
||||||
case DIV_SYSTEM_QSOUND:
|
case DIV_SYSTEM_QSOUND:
|
||||||
|
@ -335,7 +343,7 @@ int DivEngine::getChannelCount(DivSystem sys) {
|
||||||
case DIV_SYSTEM_OPL3:
|
case DIV_SYSTEM_OPL3:
|
||||||
return 18;
|
return 18;
|
||||||
case DIV_SYSTEM_MULTIPCM:
|
case DIV_SYSTEM_MULTIPCM:
|
||||||
return 24;
|
return 28;
|
||||||
case DIV_SYSTEM_PCSPKR:
|
case DIV_SYSTEM_PCSPKR:
|
||||||
return 1;
|
return 1;
|
||||||
case DIV_SYSTEM_POKEY:
|
case DIV_SYSTEM_POKEY:
|
||||||
|
@ -351,6 +359,7 @@ int DivEngine::getChannelCount(DivSystem sys) {
|
||||||
case DIV_SYSTEM_POKEMINI:
|
case DIV_SYSTEM_POKEMINI:
|
||||||
return 1;
|
return 1;
|
||||||
case DIV_SYSTEM_SEGAPCM:
|
case DIV_SYSTEM_SEGAPCM:
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
return 16;
|
return 16;
|
||||||
case DIV_SYSTEM_VBOY:
|
case DIV_SYSTEM_VBOY:
|
||||||
return 6;
|
return 6;
|
||||||
|
@ -383,6 +392,8 @@ int DivEngine::getChannelCount(DivSystem sys) {
|
||||||
case DIV_SYSTEM_YM2610B_EXT:
|
case DIV_SYSTEM_YM2610B_EXT:
|
||||||
case DIV_SYSTEM_QSOUND:
|
case DIV_SYSTEM_QSOUND:
|
||||||
return 19;
|
return 19;
|
||||||
|
case DIV_SYSTEM_VERA:
|
||||||
|
return 17;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -408,10 +419,6 @@ const char* DivEngine::getSongSystemName() {
|
||||||
return "Vectrex";
|
return "Vectrex";
|
||||||
case 5: // AY-3-8910, 1MHz
|
case 5: // AY-3-8910, 1MHz
|
||||||
return "Amstrad CPC";
|
return "Amstrad CPC";
|
||||||
case 6: // AY-3-8910, 0.somethingMhz
|
|
||||||
return "Intellivision";
|
|
||||||
case 8: // AY-3-8910, 0.somethingMhz
|
|
||||||
return "Intellivision (PAL)";
|
|
||||||
|
|
||||||
case 0x10: // YM2149, 1.79MHz
|
case 0x10: // YM2149, 1.79MHz
|
||||||
return "MSX";
|
return "MSX";
|
||||||
|
@ -422,6 +429,11 @@ const char* DivEngine::getSongSystemName() {
|
||||||
case 0x28: // 5B PAL
|
case 0x28: // 5B PAL
|
||||||
return "Sunsoft 5B standalone (PAL)";
|
return "Sunsoft 5B standalone (PAL)";
|
||||||
|
|
||||||
|
case 0x30: // AY-3-8914, 1.79MHz
|
||||||
|
return "Intellivision";
|
||||||
|
case 0x33: // AY-3-8914, 2MHz
|
||||||
|
return "Intellivision (PAL)";
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if ((song.systemFlags[0]&0x30)==0x00) {
|
if ((song.systemFlags[0]&0x30)==0x00) {
|
||||||
return "AY-3-8910";
|
return "AY-3-8910";
|
||||||
|
@ -429,6 +441,8 @@ const char* DivEngine::getSongSystemName() {
|
||||||
return "Yamaha YM2149";
|
return "Yamaha YM2149";
|
||||||
} else if ((song.systemFlags[0]&0x30)==0x20) {
|
} else if ((song.systemFlags[0]&0x30)==0x20) {
|
||||||
return "Overclocked Sunsoft 5B";
|
return "Overclocked Sunsoft 5B";
|
||||||
|
} else if ((song.systemFlags[0]&0x30)==0x30) {
|
||||||
|
return "Intellivision";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (song.system[0]==DIV_SYSTEM_SMS) {
|
} else if (song.system[0]==DIV_SYSTEM_SMS) {
|
||||||
|
@ -522,6 +536,10 @@ const char* DivEngine::getSongSystemName() {
|
||||||
if (song.system[0]==DIV_SYSTEM_AY8910 && song.system[1]==DIV_SYSTEM_AY8910) {
|
if (song.system[0]==DIV_SYSTEM_AY8910 && song.system[1]==DIV_SYSTEM_AY8910) {
|
||||||
return "Bally Midway MCR";
|
return "Bally Midway MCR";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (song.system[0]==DIV_SYSTEM_YM2151 && song.system[1]==DIV_SYSTEM_VERA) {
|
||||||
|
return "Commander X16";
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
break;
|
break;
|
||||||
|
@ -650,6 +668,10 @@ const char* DivEngine::getSystemName(DivSystem sys) {
|
||||||
return "Taito Arcade Extended Channel 3";
|
return "Taito Arcade Extended Channel 3";
|
||||||
case DIV_SYSTEM_QSOUND:
|
case DIV_SYSTEM_QSOUND:
|
||||||
return "Capcom QSound";
|
return "Capcom QSound";
|
||||||
|
case DIV_SYSTEM_VERA:
|
||||||
|
return "VERA";
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
|
return "Seta/Allumer X1-010";
|
||||||
}
|
}
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
|
@ -775,6 +797,10 @@ const char* DivEngine::getSystemChips(DivSystem sys) {
|
||||||
return "Yamaha YM2610B Extended Channel 3";
|
return "Yamaha YM2610B Extended Channel 3";
|
||||||
case DIV_SYSTEM_QSOUND:
|
case DIV_SYSTEM_QSOUND:
|
||||||
return "Capcom DL-1425";
|
return "Capcom DL-1425";
|
||||||
|
case DIV_SYSTEM_VERA:
|
||||||
|
return "VERA";
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
|
return "Seta/Allumer X1-010";
|
||||||
}
|
}
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
|
@ -857,7 +883,7 @@ bool DivEngine::isSTDSystem(DivSystem sys) {
|
||||||
sys!=DIV_SYSTEM_YM2151);
|
sys!=DIV_SYSTEM_YM2151);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* chanNames[38][24]={
|
const char* chanNames[40][32]={
|
||||||
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM"}, // YMU759/SegaPCM
|
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM"}, // YMU759/SegaPCM
|
||||||
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Noise"}, // Genesis
|
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Noise"}, // Genesis
|
||||||
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Noise"}, // Genesis (extended channel 3)
|
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Noise"}, // Genesis (extended channel 3)
|
||||||
|
@ -870,7 +896,7 @@ const char* chanNames[38][24]={
|
||||||
{"FM 1", "FM 2", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610
|
{"FM 1", "FM 2", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610
|
||||||
{"FM 1", "FM 2 OP1", "FM 2 OP2", "FM 2 OP3", "FM 2 OP4", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610 (extended channel 2)
|
{"FM 1", "FM 2 OP1", "FM 2 OP2", "FM 2 OP3", "FM 2 OP4", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610 (extended channel 2)
|
||||||
{"PSG 1", "PSG 2", "PSG 3"}, // AY-3-8910
|
{"PSG 1", "PSG 2", "PSG 3"}, // AY-3-8910
|
||||||
{"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, // Amiga/POKEY/Swan/Lynx
|
{"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, // Amiga/POKEY/Lynx
|
||||||
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"}, // YM2151/YM2414
|
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"}, // YM2151/YM2414
|
||||||
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"}, // YM2612
|
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"}, // YM2612
|
||||||
{"Channel 1", "Channel 2"}, // TIA
|
{"Channel 1", "Channel 2"}, // TIA
|
||||||
|
@ -886,7 +912,7 @@ const char* chanNames[38][24]={
|
||||||
{"FM 1", "FM 2", "FM 3", "PSG 1", "PSG 2", "PSG 3"}, // OPN
|
{"FM 1", "FM 2", "FM 3", "PSG 1", "PSG 2", "PSG 3"}, // OPN
|
||||||
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"}, // PC-98
|
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"}, // PC-98
|
||||||
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9", "FM 10", "FM 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18"}, // OPL3
|
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9", "FM 10", "FM 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18"}, // OPL3
|
||||||
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "Channel 17", "Channel 18", "Channel 19", "Channel 20", "Channel 21", "Channel 22", "Channel 23", "Channel 24"}, // MultiPCM
|
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "Channel 17", "Channel 18", "Channel 19", "Channel 20", "Channel 21", "Channel 22", "Channel 23", "Channel 24", "Channel 25", "Channel 26", "Channel 27", "Channel 28"}, // MultiPCM
|
||||||
{"Square"}, // PC Speaker/Pokémon Mini
|
{"Square"}, // PC Speaker/Pokémon Mini
|
||||||
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Noise"}, // Virtual Boy/SCC
|
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Noise"}, // Virtual Boy/SCC
|
||||||
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610B
|
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610B
|
||||||
|
@ -896,9 +922,11 @@ const char* chanNames[38][24]={
|
||||||
{"FM 1", "FM 2", "FM 3", "4OP 1", "4OP 2", "4OP 3", "4OP 4", "4OP 5", "4OP 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, // OPL3 4-op + drums
|
{"FM 1", "FM 2", "FM 3", "4OP 1", "4OP 2", "4OP 3", "4OP 4", "4OP 5", "4OP 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, // OPL3 4-op + drums
|
||||||
{"PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "ADPCM 1", "ADPCM 2", "ADPCM 3"}, // QSound
|
{"PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "ADPCM 1", "ADPCM 2", "ADPCM 3"}, // QSound
|
||||||
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610B (extended channel 3)
|
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610B (extended channel 3)
|
||||||
|
{"Wave", "Wave/PCM", "Wave", "Wave/Noise"}, // Swan
|
||||||
|
{"PSG 1", "PSG 2", "PSG 3", "PSG 4", "PSG 5", "PSG 6", "PSG 7", "PSG 8", "PSG 9", "PSG 10", "PSG 11", "PSG 12", "PSG 13", "PSG 14", "PSG 15", "PSG 16", "PCM"}, // VERA
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* chanShortNames[38][24]={
|
const char* chanShortNames[38][32]={
|
||||||
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM"}, // YMU759
|
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM"}, // YMU759
|
||||||
{"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "NO"}, // Genesis
|
{"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "NO"}, // Genesis
|
||||||
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "S4"}, // Genesis (extended channel 3)
|
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "S4"}, // Genesis (extended channel 3)
|
||||||
|
@ -927,7 +955,7 @@ const char* chanShortNames[38][24]={
|
||||||
{"F1", "F2", "F3", "S1", "S2", "S3"}, // OPN
|
{"F1", "F2", "F3", "S1", "S2", "S3"}, // OPN
|
||||||
{"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "BD", "SD", "TP", "HH", "TM", "RM", "P"}, // PC-98
|
{"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "BD", "SD", "TP", "HH", "TM", "RM", "P"}, // PC-98
|
||||||
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18"}, // OPL3
|
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18"}, // OPL3
|
||||||
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24"}, // MultiPCM
|
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28"}, // MultiPCM
|
||||||
{"SQ"}, // PC Speaker/Pokémon Mini
|
{"SQ"}, // PC Speaker/Pokémon Mini
|
||||||
{"CH1", "CH2", "CH3", "CH4", "CH5", "NO"}, // Virtual Boy/SCC
|
{"CH1", "CH2", "CH3", "CH4", "CH5", "NO"}, // Virtual Boy/SCC
|
||||||
{"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, // YM2610B
|
{"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, // YM2610B
|
||||||
|
@ -939,7 +967,7 @@ const char* chanShortNames[38][24]={
|
||||||
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, // YM2610B (extended channel 3)
|
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, // YM2610B (extended channel 3)
|
||||||
};
|
};
|
||||||
|
|
||||||
const int chanTypes[38][24]={
|
const int chanTypes[41][32]={
|
||||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}, // YMU759
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}, // YMU759
|
||||||
{0, 0, 0, 0, 0, 0, 1, 1, 1, 2}, // Genesis
|
{0, 0, 0, 0, 0, 0, 1, 1, 1, 2}, // Genesis
|
||||||
{0, 0, 5, 5, 5, 5, 0, 0, 0, 1, 1, 1, 2}, // Genesis (extended channel 3)
|
{0, 0, 5, 5, 5, 5, 0, 0, 0, 1, 1, 1, 2}, // Genesis (extended channel 3)
|
||||||
|
@ -968,7 +996,7 @@ const int chanTypes[38][24]={
|
||||||
{0, 0, 0, 1, 1, 1}, // OPN
|
{0, 0, 0, 1, 1, 1}, // OPN
|
||||||
{0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 4}, // PC-98
|
{0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 4}, // PC-98
|
||||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // OPL3
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // OPL3
|
||||||
{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, // MultiPCM/QSound
|
{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, // MultiPCM/QSound
|
||||||
{1}, // PC Speaker/Pokémon Mini
|
{1}, // PC Speaker/Pokémon Mini
|
||||||
{3, 3, 3, 3, 3, 2}, // Virtual Boy/SCC
|
{3, 3, 3, 3, 3, 2}, // Virtual Boy/SCC
|
||||||
{0, 0, 0, 0, 0, 0, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4}, // YM2610B
|
{0, 0, 0, 0, 0, 0, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4}, // YM2610B
|
||||||
|
@ -976,11 +1004,14 @@ const int chanTypes[38][24]={
|
||||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPL3 drums
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPL3 drums
|
||||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // OPL3 4-op
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // OPL3 4-op
|
||||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPL3 4-op + drums
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPL3 4-op + drums
|
||||||
{3, 3, 3, 3}, //Lynx
|
{3, 3, 3, 3}, // Lynx
|
||||||
{0, 0, 5, 5, 5, 5, 0, 0, 0, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4}, // YM2610B (extended channel 3)
|
{0, 0, 5, 5, 5, 5, 0, 0, 0, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4}, // YM2610B (extended channel 3)
|
||||||
|
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4}, // VERA
|
||||||
|
{3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, // X1-010
|
||||||
|
{3, 4, 3, 2}, // Swan
|
||||||
};
|
};
|
||||||
|
|
||||||
const DivInstrumentType chanPrefType[44][24]={
|
const DivInstrumentType chanPrefType[46][28]={
|
||||||
{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_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM}, // YMU759
|
{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_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM}, // YMU759
|
||||||
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, // Genesis
|
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, // Genesis
|
||||||
{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_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, // Genesis (extended channel 3)
|
{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_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, // Genesis (extended channel 3)
|
||||||
|
@ -1009,7 +1040,7 @@ const DivInstrumentType chanPrefType[44][24]={
|
||||||
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY}, // OPN
|
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY}, // OPN
|
||||||
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // PC-98
|
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // PC-98
|
||||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, // OPL/OPL2/OPL3
|
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, // OPL/OPL2/OPL3
|
||||||
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // MultiPCM/QSound
|
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // MultiPCM/QSound
|
||||||
{DIV_INS_STD}, // PC Speaker/Pokémon Mini
|
{DIV_INS_STD}, // PC Speaker/Pokémon Mini
|
||||||
{DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY}, // Virtual Boy
|
{DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY}, // Virtual Boy
|
||||||
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // YM2610B
|
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // YM2610B
|
||||||
|
@ -1025,6 +1056,8 @@ const DivInstrumentType chanPrefType[44][24]={
|
||||||
{DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ}, // Z
|
{DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ}, // Z
|
||||||
{DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY}, // Lynx
|
{DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY}, // Lynx
|
||||||
{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_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // YM2610B (extended channel 3)
|
{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_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // YM2610B (extended channel 3)
|
||||||
|
{DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_AMIGA}, // VERA
|
||||||
|
{DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010}, // X1-010
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* DivEngine::getChannelName(int chan) {
|
const char* DivEngine::getChannelName(int chan) {
|
||||||
|
@ -1078,10 +1111,12 @@ const char* DivEngine::getChannelName(int chan) {
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_AMIGA:
|
case DIV_SYSTEM_AMIGA:
|
||||||
case DIV_SYSTEM_POKEY:
|
case DIV_SYSTEM_POKEY:
|
||||||
case DIV_SYSTEM_SWAN:
|
|
||||||
case DIV_SYSTEM_LYNX:
|
case DIV_SYSTEM_LYNX:
|
||||||
return chanNames[12][dispatchChanOfChan[chan]];
|
return chanNames[12][dispatchChanOfChan[chan]];
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_SWAN:
|
||||||
|
return chanNames[38][dispatchChanOfChan[chan]];
|
||||||
|
break;
|
||||||
case DIV_SYSTEM_YM2151:
|
case DIV_SYSTEM_YM2151:
|
||||||
return chanNames[13][dispatchChanOfChan[chan]];
|
return chanNames[13][dispatchChanOfChan[chan]];
|
||||||
break;
|
break;
|
||||||
|
@ -1129,6 +1164,7 @@ const char* DivEngine::getChannelName(int chan) {
|
||||||
case DIV_SYSTEM_MULTIPCM:
|
case DIV_SYSTEM_MULTIPCM:
|
||||||
case DIV_SYSTEM_SEGAPCM:
|
case DIV_SYSTEM_SEGAPCM:
|
||||||
case DIV_SYSTEM_SEGAPCM_COMPAT:
|
case DIV_SYSTEM_SEGAPCM_COMPAT:
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
return chanNames[28][dispatchChanOfChan[chan]];
|
return chanNames[28][dispatchChanOfChan[chan]];
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_PCSPKR:
|
case DIV_SYSTEM_PCSPKR:
|
||||||
|
@ -1162,6 +1198,9 @@ const char* DivEngine::getChannelName(int chan) {
|
||||||
case DIV_SYSTEM_QSOUND:
|
case DIV_SYSTEM_QSOUND:
|
||||||
return chanNames[36][dispatchChanOfChan[chan]];
|
return chanNames[36][dispatchChanOfChan[chan]];
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_VERA:
|
||||||
|
return chanNames[39][dispatchChanOfChan[chan]];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return "??";
|
return "??";
|
||||||
}
|
}
|
||||||
|
@ -1268,6 +1307,7 @@ const char* DivEngine::getChannelShortName(int chan) {
|
||||||
case DIV_SYSTEM_MULTIPCM:
|
case DIV_SYSTEM_MULTIPCM:
|
||||||
case DIV_SYSTEM_SEGAPCM:
|
case DIV_SYSTEM_SEGAPCM:
|
||||||
case DIV_SYSTEM_SEGAPCM_COMPAT:
|
case DIV_SYSTEM_SEGAPCM_COMPAT:
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
return chanShortNames[28][dispatchChanOfChan[chan]];
|
return chanShortNames[28][dispatchChanOfChan[chan]];
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_PCSPKR:
|
case DIV_SYSTEM_PCSPKR:
|
||||||
|
@ -1301,6 +1341,9 @@ const char* DivEngine::getChannelShortName(int chan) {
|
||||||
case DIV_SYSTEM_QSOUND:
|
case DIV_SYSTEM_QSOUND:
|
||||||
return chanShortNames[36][dispatchChanOfChan[chan]];
|
return chanShortNames[36][dispatchChanOfChan[chan]];
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_VERA:
|
||||||
|
return chanShortNames[0][dispatchChanOfChan[chan]];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return "??";
|
return "??";
|
||||||
}
|
}
|
||||||
|
@ -1354,7 +1397,6 @@ int DivEngine::getChannelType(int chan) {
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_AMIGA:
|
case DIV_SYSTEM_AMIGA:
|
||||||
case DIV_SYSTEM_POKEY:
|
case DIV_SYSTEM_POKEY:
|
||||||
case DIV_SYSTEM_SWAN:
|
|
||||||
return chanTypes[12][dispatchChanOfChan[chan]];
|
return chanTypes[12][dispatchChanOfChan[chan]];
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_YM2151:
|
case DIV_SYSTEM_YM2151:
|
||||||
|
@ -1438,6 +1480,15 @@ int DivEngine::getChannelType(int chan) {
|
||||||
case DIV_SYSTEM_LYNX:
|
case DIV_SYSTEM_LYNX:
|
||||||
return chanTypes[36][dispatchChanOfChan[chan]];
|
return chanTypes[36][dispatchChanOfChan[chan]];
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_VERA:
|
||||||
|
return chanTypes[38][dispatchChanOfChan[chan]];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
|
return chanTypes[39][dispatchChanOfChan[chan]];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_SWAN:
|
||||||
|
return chanTypes[40][dispatchChanOfChan[chan]];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1588,6 +1639,12 @@ DivInstrumentType DivEngine::getPreferInsType(int chan) {
|
||||||
case DIV_SYSTEM_LYNX:
|
case DIV_SYSTEM_LYNX:
|
||||||
return chanPrefType[42][dispatchChanOfChan[chan]];
|
return chanPrefType[42][dispatchChanOfChan[chan]];
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_VERA:
|
||||||
|
return chanPrefType[44][dispatchChanOfChan[chan]];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
|
return chanPrefType[45][dispatchChanOfChan[chan]];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return DIV_INS_FM;
|
return DIV_INS_FM;
|
||||||
}
|
}
|
||||||
|
@ -1616,6 +1673,7 @@ bool DivEngine::isVGMExportable(DivSystem which) {
|
||||||
case DIV_SYSTEM_OPLL:
|
case DIV_SYSTEM_OPLL:
|
||||||
case DIV_SYSTEM_OPLL_DRUMS:
|
case DIV_SYSTEM_OPLL_DRUMS:
|
||||||
case DIV_SYSTEM_VRC7:
|
case DIV_SYSTEM_VRC7:
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
case DIV_SYSTEM_SWAN:
|
case DIV_SYSTEM_SWAN:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -154,6 +154,13 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
w->writeC(3);
|
w->writeC(3);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
w->writeC(0xc8);
|
||||||
|
w->writeS_BE(baseAddr2S+(i<<3));
|
||||||
|
w->writeC(0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case DIV_SYSTEM_YM2610:
|
case DIV_SYSTEM_YM2610:
|
||||||
case DIV_SYSTEM_YM2610_FULL:
|
case DIV_SYSTEM_YM2610_FULL:
|
||||||
case DIV_SYSTEM_YM2610B:
|
case DIV_SYSTEM_YM2610B:
|
||||||
|
@ -395,6 +402,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
w->writeS(baseAddr2S|(write.addr&0xffff));
|
w->writeS(baseAddr2S|(write.addr&0xffff));
|
||||||
w->writeC(write.val);
|
w->writeC(write.val);
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
|
w->writeC(0xc8);
|
||||||
|
w->writeS_BE(baseAddr2S|(write.addr&0x1fff));
|
||||||
|
w->writeC(write.val);
|
||||||
|
break;
|
||||||
case DIV_SYSTEM_YM2610:
|
case DIV_SYSTEM_YM2610:
|
||||||
case DIV_SYSTEM_YM2610_FULL:
|
case DIV_SYSTEM_YM2610_FULL:
|
||||||
case DIV_SYSTEM_YM2610B:
|
case DIV_SYSTEM_YM2610B:
|
||||||
|
@ -568,6 +580,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) {
|
||||||
bool writePCESamples=false;
|
bool writePCESamples=false;
|
||||||
bool writeADPCM=false;
|
bool writeADPCM=false;
|
||||||
bool writeSegaPCM=false;
|
bool writeSegaPCM=false;
|
||||||
|
bool writeX1010=false;
|
||||||
bool writeQSound=false;
|
bool writeQSound=false;
|
||||||
|
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
|
@ -651,6 +664,18 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) {
|
||||||
howManyChips++;
|
howManyChips++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_X1_010:
|
||||||
|
if (!hasX1) {
|
||||||
|
hasX1=disCont[i].dispatch->chipClock;
|
||||||
|
willExport[i]=true;
|
||||||
|
writeX1010=true;
|
||||||
|
} else if (!(hasX1&0x40000000)) {
|
||||||
|
isSecond[i]=true;
|
||||||
|
willExport[i]=true;
|
||||||
|
hasX1|=0x40000000;
|
||||||
|
howManyChips++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case DIV_SYSTEM_YM2610:
|
case DIV_SYSTEM_YM2610:
|
||||||
case DIV_SYSTEM_YM2610_FULL:
|
case DIV_SYSTEM_YM2610_FULL:
|
||||||
case DIV_SYSTEM_YM2610B:
|
case DIV_SYSTEM_YM2610B:
|
||||||
|
@ -995,6 +1020,16 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) {
|
||||||
w->write(qsoundMem,blockSize);
|
w->write(qsoundMem,blockSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (writeX1010 && x1_010MemLen>0) {
|
||||||
|
w->writeC(0x67);
|
||||||
|
w->writeC(0x66);
|
||||||
|
w->writeC(0x91);
|
||||||
|
w->writeI(x1_010MemLen+8);
|
||||||
|
w->writeI(x1_010MemLen);
|
||||||
|
w->writeI(0);
|
||||||
|
w->write(x1_010Mem,x1_010MemLen);
|
||||||
|
}
|
||||||
|
|
||||||
// initialize streams
|
// initialize streams
|
||||||
int streamID=0;
|
int streamID=0;
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "../engine/platform/tia.h"
|
#include "../engine/platform/tia.h"
|
||||||
#include "../engine/platform/saa.h"
|
#include "../engine/platform/saa.h"
|
||||||
#include "../engine/platform/amiga.h"
|
#include "../engine/platform/amiga.h"
|
||||||
|
#include "../engine/platform/x1_010.h"
|
||||||
#include "../engine/platform/dummy.h"
|
#include "../engine/platform/dummy.h"
|
||||||
|
|
||||||
#define GENESIS_DEBUG \
|
#define GENESIS_DEBUG \
|
||||||
|
@ -232,6 +233,47 @@ void putDispatchChan(void* data, int chanNum, int type) {
|
||||||
ImGui::TextColored(ch->inPorta?colorOn:colorOff,">> InPorta");
|
ImGui::TextColored(ch->inPorta?colorOn:colorOff,">> InPorta");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case DIV_SYSTEM_X1_010: {
|
||||||
|
DivPlatformX1_010::Channel* ch=(DivPlatformX1_010::Channel*)data;
|
||||||
|
ImGui::Text("> X1-010");
|
||||||
|
ImGui::Text("* freq: %.4x",ch->freq);
|
||||||
|
ImGui::Text(" - base: %d",ch->baseFreq);
|
||||||
|
ImGui::Text(" - pitch: %d",ch->pitch);
|
||||||
|
ImGui::Text("- note: %d",ch->note);
|
||||||
|
ImGui::Text("- wave: %d",ch->wave);
|
||||||
|
ImGui::Text("- sample: %d",ch->sample);
|
||||||
|
ImGui::Text("- ins: %d",ch->ins);
|
||||||
|
ImGui::Text("- pan: %d",ch->pan);
|
||||||
|
ImGui::Text("* envelope:");
|
||||||
|
ImGui::Text(" - shape: %d",ch->env.shape);
|
||||||
|
ImGui::Text(" - period: %.2x",ch->env.period);
|
||||||
|
ImGui::Text(" - slide: %.2x",ch->env.slide);
|
||||||
|
ImGui::Text(" - slidefrac: %.2x",ch->env.slidefrac);
|
||||||
|
ImGui::Text(" - autoEnvNum: %.2x",ch->autoEnvNum);
|
||||||
|
ImGui::Text(" - autoEnvDen: %.2x",ch->autoEnvDen);
|
||||||
|
ImGui::Text("- WaveBank: %d",ch->waveBank);
|
||||||
|
ImGui::Text("- vol: %.2x",ch->vol);
|
||||||
|
ImGui::Text("- outVol: %.2x",ch->outVol);
|
||||||
|
ImGui::Text("- Lvol: %.2x",ch->lvol);
|
||||||
|
ImGui::Text("- Rvol: %.2x",ch->rvol);
|
||||||
|
ImGui::TextColored(ch->active?colorOn:colorOff,">> Active");
|
||||||
|
ImGui::TextColored(ch->insChanged?colorOn:colorOff,">> InsChanged");
|
||||||
|
ImGui::TextColored(ch->envChanged?colorOn:colorOff,">> EnvChanged");
|
||||||
|
ImGui::TextColored(ch->freqChanged?colorOn:colorOff,">> FreqChanged");
|
||||||
|
ImGui::TextColored(ch->keyOn?colorOn:colorOff,">> KeyOn");
|
||||||
|
ImGui::TextColored(ch->keyOff?colorOn:colorOff,">> KeyOff");
|
||||||
|
ImGui::TextColored(ch->inPorta?colorOn:colorOff,">> InPorta");
|
||||||
|
ImGui::TextColored(ch->furnacePCM?colorOn:colorOff,">> FurnacePCM");
|
||||||
|
ImGui::TextColored(ch->pcm?colorOn:colorOff,">> PCM");
|
||||||
|
ImGui::TextColored(ch->env.flag.envEnable?colorOn:colorOff,">> EnvEnable");
|
||||||
|
ImGui::TextColored(ch->env.flag.envOneshot?colorOn:colorOff,">> EnvOneshot");
|
||||||
|
ImGui::TextColored(ch->env.flag.envSplit?colorOn:colorOff,">> EnvSplit");
|
||||||
|
ImGui::TextColored(ch->env.flag.envHinvR?colorOn:colorOff,">> EnvHinvR");
|
||||||
|
ImGui::TextColored(ch->env.flag.envVinvR?colorOn:colorOff,">> EnvVinvR");
|
||||||
|
ImGui::TextColored(ch->env.flag.envHinvL?colorOn:colorOff,">> EnvHinvL");
|
||||||
|
ImGui::TextColored(ch->env.flag.envVinvL?colorOn:colorOff,">> EnvVinvL");
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
ImGui::Text("Unknown system! Help!");
|
ImGui::Text("Unknown system! Help!");
|
||||||
break;
|
break;
|
||||||
|
|
106
src/gui/fileDialog.cpp
Normal file
106
src/gui/fileDialog.cpp
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#include "fileDialog.h"
|
||||||
|
#include "ImGuiFileDialog.h"
|
||||||
|
#include "../ta-log.h"
|
||||||
|
|
||||||
|
#include "../../extern/pfd-fixed/portable-file-dialogs.h"
|
||||||
|
|
||||||
|
bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale) {
|
||||||
|
if (opened) return false;
|
||||||
|
saving=false;
|
||||||
|
curPath=path;
|
||||||
|
if (sysDialog) {
|
||||||
|
dialogO=new pfd::open_file(header,path,filter);
|
||||||
|
} else {
|
||||||
|
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
|
||||||
|
ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path);
|
||||||
|
}
|
||||||
|
opened=true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FurnaceGUIFileDialog::openSave(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale) {
|
||||||
|
if (opened) return false;
|
||||||
|
saving=true;
|
||||||
|
curPath=path;
|
||||||
|
if (sysDialog) {
|
||||||
|
dialogS=new pfd::save_file(header,path,filter);
|
||||||
|
} else {
|
||||||
|
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
|
||||||
|
ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite);
|
||||||
|
}
|
||||||
|
opened=true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FurnaceGUIFileDialog::accepted() {
|
||||||
|
if (sysDialog) {
|
||||||
|
return (fileName!="");
|
||||||
|
} else {
|
||||||
|
return ImGuiFileDialog::Instance()->IsOk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIFileDialog::close() {
|
||||||
|
if (sysDialog) {
|
||||||
|
if (saving) {
|
||||||
|
if (dialogS!=NULL) {
|
||||||
|
delete dialogS;
|
||||||
|
dialogS=NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (dialogO!=NULL) {
|
||||||
|
delete dialogO;
|
||||||
|
dialogO=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ImGuiFileDialog::Instance()->Close();
|
||||||
|
}
|
||||||
|
opened=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FurnaceGUIFileDialog::render(const ImVec2& min, const ImVec2& max) {
|
||||||
|
if (sysDialog) {
|
||||||
|
if (saving) {
|
||||||
|
if (dialogS!=NULL) {
|
||||||
|
if (dialogS->ready(1)) {
|
||||||
|
fileName=dialogS->result();
|
||||||
|
logD("returning %s\n",fileName.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (dialogO!=NULL) {
|
||||||
|
if (dialogO->ready(1)) {
|
||||||
|
if (dialogO->result().empty()) {
|
||||||
|
fileName="";
|
||||||
|
logD("returning nothing\n");
|
||||||
|
} else {
|
||||||
|
fileName=dialogO->result()[0];
|
||||||
|
logD("returning %s\n",fileName.c_str());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return ImGuiFileDialog::Instance()->Display("FileDialog",ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove,min,max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String FurnaceGUIFileDialog::getPath() {
|
||||||
|
if (sysDialog) {
|
||||||
|
return curPath;
|
||||||
|
} else {
|
||||||
|
return ImGuiFileDialog::Instance()->GetCurrentPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String FurnaceGUIFileDialog::getFileName() {
|
||||||
|
if (sysDialog) {
|
||||||
|
return fileName;
|
||||||
|
} else {
|
||||||
|
return ImGuiFileDialog::Instance()->GetFilePathName();
|
||||||
|
}
|
||||||
|
}
|
32
src/gui/fileDialog.h
Normal file
32
src/gui/fileDialog.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#include "../ta-utils.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace pfd {
|
||||||
|
class open_file;
|
||||||
|
class save_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
class FurnaceGUIFileDialog {
|
||||||
|
bool sysDialog;
|
||||||
|
bool opened;
|
||||||
|
bool saving;
|
||||||
|
String curPath;
|
||||||
|
String fileName;
|
||||||
|
pfd::open_file* dialogO;
|
||||||
|
pfd::save_file* dialogS;
|
||||||
|
public:
|
||||||
|
bool openLoad(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale);
|
||||||
|
bool openSave(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale);
|
||||||
|
bool accepted();
|
||||||
|
void close();
|
||||||
|
bool render(const ImVec2& min, const ImVec2& max);
|
||||||
|
String getPath();
|
||||||
|
String getFileName();
|
||||||
|
FurnaceGUIFileDialog(bool system):
|
||||||
|
sysDialog(system),
|
||||||
|
opened(false),
|
||||||
|
saving(false),
|
||||||
|
dialogO(NULL),
|
||||||
|
dialogS(NULL) {}
|
||||||
|
};
|
1241
src/gui/gui.cpp
1241
src/gui/gui.cpp
File diff suppressed because it is too large
Load diff
|
@ -27,6 +27,8 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "fileDialog.h"
|
||||||
|
|
||||||
#define rightClickable if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) ImGui::SetKeyboardFocusHere(-1);
|
#define rightClickable if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) ImGui::SetKeyboardFocusHere(-1);
|
||||||
|
|
||||||
#define handleUnimportant if (settings.insFocusesPattern && patternOpen) {nextWindow=GUI_WINDOW_PATTERN;}
|
#define handleUnimportant if (settings.insFocusesPattern && patternOpen) {nextWindow=GUI_WINDOW_PATTERN;}
|
||||||
|
@ -73,6 +75,8 @@ enum FurnaceGUIColors {
|
||||||
GUI_COLOR_INSTR_BEEPER,
|
GUI_COLOR_INSTR_BEEPER,
|
||||||
GUI_COLOR_INSTR_SWAN,
|
GUI_COLOR_INSTR_SWAN,
|
||||||
GUI_COLOR_INSTR_MIKEY,
|
GUI_COLOR_INSTR_MIKEY,
|
||||||
|
GUI_COLOR_INSTR_VERA,
|
||||||
|
GUI_COLOR_INSTR_X1_010,
|
||||||
GUI_COLOR_INSTR_UNKNOWN,
|
GUI_COLOR_INSTR_UNKNOWN,
|
||||||
GUI_COLOR_CHANNEL_FM,
|
GUI_COLOR_CHANNEL_FM,
|
||||||
GUI_COLOR_CHANNEL_PULSE,
|
GUI_COLOR_CHANNEL_PULSE,
|
||||||
|
@ -233,6 +237,10 @@ enum FurnaceGUIActions {
|
||||||
GUI_ACTION_PAT_CUT,
|
GUI_ACTION_PAT_CUT,
|
||||||
GUI_ACTION_PAT_COPY,
|
GUI_ACTION_PAT_COPY,
|
||||||
GUI_ACTION_PAT_PASTE,
|
GUI_ACTION_PAT_PASTE,
|
||||||
|
GUI_ACTION_PAT_PASTE_MIX,
|
||||||
|
GUI_ACTION_PAT_PASTE_MIX_BG,
|
||||||
|
GUI_ACTION_PAT_PASTE_FLOOD,
|
||||||
|
GUI_ACTION_PAT_PASTE_OVERFLOW,
|
||||||
GUI_ACTION_PAT_CURSOR_UP,
|
GUI_ACTION_PAT_CURSOR_UP,
|
||||||
GUI_ACTION_PAT_CURSOR_DOWN,
|
GUI_ACTION_PAT_CURSOR_DOWN,
|
||||||
GUI_ACTION_PAT_CURSOR_LEFT,
|
GUI_ACTION_PAT_CURSOR_LEFT,
|
||||||
|
@ -268,6 +276,17 @@ enum FurnaceGUIActions {
|
||||||
GUI_ACTION_PAT_COLLAPSE,
|
GUI_ACTION_PAT_COLLAPSE,
|
||||||
GUI_ACTION_PAT_INCREASE_COLUMNS,
|
GUI_ACTION_PAT_INCREASE_COLUMNS,
|
||||||
GUI_ACTION_PAT_DECREASE_COLUMNS,
|
GUI_ACTION_PAT_DECREASE_COLUMNS,
|
||||||
|
GUI_ACTION_PAT_INTERPOLATE,
|
||||||
|
GUI_ACTION_PAT_FADE,
|
||||||
|
GUI_ACTION_PAT_INVERT_VALUES,
|
||||||
|
GUI_ACTION_PAT_FLIP_SELECTION,
|
||||||
|
GUI_ACTION_PAT_COLLAPSE_ROWS,
|
||||||
|
GUI_ACTION_PAT_EXPAND_ROWS,
|
||||||
|
GUI_ACTION_PAT_COLLAPSE_PAT,
|
||||||
|
GUI_ACTION_PAT_EXPAND_PAT,
|
||||||
|
GUI_ACTION_PAT_COLLAPSE_SONG,
|
||||||
|
GUI_ACTION_PAT_EXPAND_SONG,
|
||||||
|
GUI_ACTION_PAT_LATCH,
|
||||||
GUI_ACTION_PAT_MAX,
|
GUI_ACTION_PAT_MAX,
|
||||||
|
|
||||||
GUI_ACTION_INS_LIST_MIN,
|
GUI_ACTION_INS_LIST_MIN,
|
||||||
|
@ -334,6 +353,14 @@ enum FurnaceGUIActions {
|
||||||
GUI_ACTION_MAX
|
GUI_ACTION_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum PasteMode {
|
||||||
|
GUI_PASTE_MODE_NORMAL=0,
|
||||||
|
GUI_PASTE_MODE_MIX_FG,
|
||||||
|
GUI_PASTE_MODE_MIX_BG,
|
||||||
|
GUI_PASTE_MODE_FLOOD,
|
||||||
|
GUI_PASTE_MODE_OVERFLOW
|
||||||
|
};
|
||||||
|
|
||||||
#define FURKMOD_CTRL (1<<31)
|
#define FURKMOD_CTRL (1<<31)
|
||||||
#define FURKMOD_SHIFT (1<<29)
|
#define FURKMOD_SHIFT (1<<29)
|
||||||
#define FURKMOD_META (1<<28)
|
#define FURKMOD_META (1<<28)
|
||||||
|
@ -354,7 +381,16 @@ enum ActionType {
|
||||||
GUI_UNDO_PATTERN_PULL,
|
GUI_UNDO_PATTERN_PULL,
|
||||||
GUI_UNDO_PATTERN_PUSH,
|
GUI_UNDO_PATTERN_PUSH,
|
||||||
GUI_UNDO_PATTERN_CUT,
|
GUI_UNDO_PATTERN_CUT,
|
||||||
GUI_UNDO_PATTERN_PASTE
|
GUI_UNDO_PATTERN_PASTE,
|
||||||
|
GUI_UNDO_PATTERN_CHANGE_INS,
|
||||||
|
GUI_UNDO_PATTERN_INTERPOLATE,
|
||||||
|
GUI_UNDO_PATTERN_FADE,
|
||||||
|
GUI_UNDO_PATTERN_SCALE,
|
||||||
|
GUI_UNDO_PATTERN_RANDOMIZE,
|
||||||
|
GUI_UNDO_PATTERN_INVERT_VAL,
|
||||||
|
GUI_UNDO_PATTERN_FLIP,
|
||||||
|
GUI_UNDO_PATTERN_COLLAPSE,
|
||||||
|
GUI_UNDO_PATTERN_EXPAND
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UndoPatternData {
|
struct UndoPatternData {
|
||||||
|
@ -442,6 +478,8 @@ class FurnaceGUI {
|
||||||
FurnaceGUIFileDialogs curFileDialog;
|
FurnaceGUIFileDialogs curFileDialog;
|
||||||
FurnaceGUIWarnings warnAction;
|
FurnaceGUIWarnings warnAction;
|
||||||
|
|
||||||
|
FurnaceGUIFileDialog* fileDialog;
|
||||||
|
|
||||||
int scrW, scrH;
|
int scrW, scrH;
|
||||||
|
|
||||||
double dpiScale;
|
double dpiScale;
|
||||||
|
@ -499,6 +537,11 @@ class FurnaceGUI {
|
||||||
int guiColorsBase;
|
int guiColorsBase;
|
||||||
int avoidRaisingPattern;
|
int avoidRaisingPattern;
|
||||||
int insFocusesPattern;
|
int insFocusesPattern;
|
||||||
|
int stepOnInsert;
|
||||||
|
// TODO flags
|
||||||
|
int unifiedDataView;
|
||||||
|
int sysFileDialog;
|
||||||
|
// end
|
||||||
unsigned int maxUndoSteps;
|
unsigned int maxUndoSteps;
|
||||||
String mainFontPath;
|
String mainFontPath;
|
||||||
String patFontPath;
|
String patFontPath;
|
||||||
|
@ -512,7 +555,7 @@ class FurnaceGUI {
|
||||||
audioQuality(0),
|
audioQuality(0),
|
||||||
arcadeCore(0),
|
arcadeCore(0),
|
||||||
ym2612Core(0),
|
ym2612Core(0),
|
||||||
saaCore(0),
|
saaCore(1),
|
||||||
mainFont(0),
|
mainFont(0),
|
||||||
patFont(0),
|
patFont(0),
|
||||||
audioRate(44100),
|
audioRate(44100),
|
||||||
|
@ -542,6 +585,9 @@ class FurnaceGUI {
|
||||||
guiColorsBase(0),
|
guiColorsBase(0),
|
||||||
avoidRaisingPattern(0),
|
avoidRaisingPattern(0),
|
||||||
insFocusesPattern(1),
|
insFocusesPattern(1),
|
||||||
|
stepOnInsert(0),
|
||||||
|
unifiedDataView(0),
|
||||||
|
sysFileDialog(1),
|
||||||
maxUndoSteps(100),
|
maxUndoSteps(100),
|
||||||
mainFontPath(""),
|
mainFontPath(""),
|
||||||
patFontPath(""),
|
patFontPath(""),
|
||||||
|
@ -558,13 +604,16 @@ class FurnaceGUI {
|
||||||
bool pianoOpen, notesOpen, channelsOpen, regViewOpen;
|
bool pianoOpen, notesOpen, channelsOpen, regViewOpen;
|
||||||
SelectionPoint selStart, selEnd, cursor;
|
SelectionPoint selStart, selEnd, cursor;
|
||||||
bool selecting, curNibble, orderNibble, followOrders, followPattern, changeAllOrders;
|
bool selecting, curNibble, orderNibble, followOrders, followPattern, changeAllOrders;
|
||||||
bool collapseWindow, demandScrollX, fancyPattern, wantPatName;
|
bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame;
|
||||||
FurnaceGUIWindows curWindow, nextWindow;
|
FurnaceGUIWindows curWindow, nextWindow;
|
||||||
float peak[2];
|
float peak[2];
|
||||||
float patChanX[DIV_MAX_CHANS+1];
|
float patChanX[DIV_MAX_CHANS+1];
|
||||||
float patChanSlideY[DIV_MAX_CHANS+1];
|
float patChanSlideY[DIV_MAX_CHANS+1];
|
||||||
const int* nextDesc;
|
const int* nextDesc;
|
||||||
|
|
||||||
|
bool opMaskNote, opMaskIns, opMaskVol, opMaskEffect, opMaskEffectVal;
|
||||||
|
short latchNote, latchIns, latchVol, latchEffect, latchEffectVal;
|
||||||
|
|
||||||
// bit 31: ctrl
|
// bit 31: ctrl
|
||||||
// bit 30: reserved for SDL scancode mask
|
// bit 30: reserved for SDL scancode mask
|
||||||
// bit 29: shift
|
// bit 29: shift
|
||||||
|
@ -624,6 +673,7 @@ class FurnaceGUI {
|
||||||
bool macroDragInitialValueSet;
|
bool macroDragInitialValueSet;
|
||||||
bool macroDragInitialValue;
|
bool macroDragInitialValue;
|
||||||
bool macroDragChar;
|
bool macroDragChar;
|
||||||
|
bool macroDragLineMode; // TODO
|
||||||
bool macroDragActive;
|
bool macroDragActive;
|
||||||
|
|
||||||
ImVec2 macroLoopDragStart;
|
ImVec2 macroLoopDragStart;
|
||||||
|
@ -650,6 +700,9 @@ class FurnaceGUI {
|
||||||
ImVec2 threeChars, twoChars;
|
ImVec2 threeChars, twoChars;
|
||||||
SelectionPoint sel1, sel2;
|
SelectionPoint sel1, sel2;
|
||||||
int dummyRows, demandX;
|
int dummyRows, demandX;
|
||||||
|
int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax;
|
||||||
|
float scaleMax;
|
||||||
|
bool fadeMode, randomMode;
|
||||||
|
|
||||||
int oldOrdersLen;
|
int oldOrdersLen;
|
||||||
DivOrders oldOrders;
|
DivOrders oldOrders;
|
||||||
|
@ -668,6 +721,9 @@ class FurnaceGUI {
|
||||||
|
|
||||||
void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord);
|
void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord);
|
||||||
|
|
||||||
|
void actualWaveList();
|
||||||
|
void actualSampleList();
|
||||||
|
|
||||||
void drawEditControls();
|
void drawEditControls();
|
||||||
void drawSongInfo();
|
void drawSongInfo();
|
||||||
void drawOrders();
|
void drawOrders();
|
||||||
|
@ -718,9 +774,19 @@ class FurnaceGUI {
|
||||||
void doInsert();
|
void doInsert();
|
||||||
void doTranspose(int amount);
|
void doTranspose(int amount);
|
||||||
void doCopy(bool cut);
|
void doCopy(bool cut);
|
||||||
void doPaste();
|
void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL);
|
||||||
|
void doChangeIns(int ins);
|
||||||
|
void doInterpolate();
|
||||||
|
void doFade(int p0, int p1, bool mode);
|
||||||
|
void doInvertValues();
|
||||||
|
void doScale(float top);
|
||||||
|
void doRandomize(int bottom, int top, bool mode);
|
||||||
|
void doFlip();
|
||||||
|
void doCollapse(int divider);
|
||||||
|
void doExpand(int multiplier);
|
||||||
void doUndo();
|
void doUndo();
|
||||||
void doRedo();
|
void doRedo();
|
||||||
|
void editOptions(bool topMenu);
|
||||||
|
|
||||||
void play(int row=0);
|
void play(int row=0);
|
||||||
void stop();
|
void stop();
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
// guiConst: constants used in the GUI like arrays, strings and other stuff
|
// guiConst: constants used in the GUI like arrays, strings and other stuff
|
||||||
#include "guiConst.h"
|
#include "guiConst.h"
|
||||||
|
#include "../engine/instrument.h"
|
||||||
|
|
||||||
const int opOrder[4]={
|
const int opOrder[4]={
|
||||||
0, 2, 1, 3
|
0, 2, 1, 3
|
||||||
|
@ -64,3 +65,31 @@ const char* pitchLabel[11]={
|
||||||
"1/6", "1/5", "1/4", "1/3", "1/2", "1x", "2x", "3x", "4x", "5x", "6x"
|
"1/6", "1/5", "1/4", "1/3", "1/2", "1x", "2x", "3x", "4x", "5x", "6x"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char* insTypes[DIV_INS_MAX]={
|
||||||
|
"Standard",
|
||||||
|
"FM (4-operator)",
|
||||||
|
"Game Boy",
|
||||||
|
"C64",
|
||||||
|
"Amiga/Sample",
|
||||||
|
"PC Engine",
|
||||||
|
"AY-3-8910/SSG",
|
||||||
|
"AY8930",
|
||||||
|
"TIA",
|
||||||
|
"SAA1099",
|
||||||
|
"VIC",
|
||||||
|
"PET",
|
||||||
|
"VRC6",
|
||||||
|
"FM (OPLL)",
|
||||||
|
"FM (OPL)",
|
||||||
|
"FDS",
|
||||||
|
"Virtual Boy",
|
||||||
|
"Namco 163",
|
||||||
|
"Konami SCC",
|
||||||
|
"FM (OPZ)",
|
||||||
|
"POKEY",
|
||||||
|
"PC Beeper",
|
||||||
|
"WonderSwan",
|
||||||
|
"Atari Lynx",
|
||||||
|
"VERA",
|
||||||
|
"X1-010"
|
||||||
|
};
|
|
@ -23,3 +23,4 @@ extern const int opOrder[4];
|
||||||
extern const char* noteNames[180];
|
extern const char* noteNames[180];
|
||||||
extern const char* noteNamesG[180];
|
extern const char* noteNamesG[180];
|
||||||
extern const char* pitchLabel[11];
|
extern const char* pitchLabel[11];
|
||||||
|
extern const char* insTypes[];
|
|
@ -27,33 +27,6 @@
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include "plot_nolerp.h"
|
#include "plot_nolerp.h"
|
||||||
|
|
||||||
const char* insTypes[24]={
|
|
||||||
"Standard",
|
|
||||||
"FM (4-operator)",
|
|
||||||
"Game Boy",
|
|
||||||
"C64",
|
|
||||||
"Amiga/Sample",
|
|
||||||
"PC Engine",
|
|
||||||
"AY-3-8910/SSG",
|
|
||||||
"AY8930",
|
|
||||||
"TIA",
|
|
||||||
"SAA1099",
|
|
||||||
"VIC",
|
|
||||||
"PET",
|
|
||||||
"VRC6",
|
|
||||||
"FM (OPLL)",
|
|
||||||
"FM (OPL)",
|
|
||||||
"FDS",
|
|
||||||
"Virtual Boy",
|
|
||||||
"Namco 163",
|
|
||||||
"Konami SCC",
|
|
||||||
"FM (OPZ)",
|
|
||||||
"POKEY",
|
|
||||||
"PC Beeper",
|
|
||||||
"WonderSwan",
|
|
||||||
"Atari Lynx"
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* ssgEnvTypes[8]={
|
const char* ssgEnvTypes[8]={
|
||||||
"Down Down Down", "Down.", "Down Up Down Up", "Down UP", "Up Up Up", "Up.", "Up Down Up Down", "Up DOWN"
|
"Down Down Down", "Down.", "Down Up Down Up", "Down UP", "Up Up Up", "Up.", "Up Down Up Down", "Up DOWN"
|
||||||
};
|
};
|
||||||
|
@ -156,6 +129,10 @@ const char* mikeyFeedbackBits[11] = {
|
||||||
"0", "1", "2", "3", "4", "5", "7", "10", "11", "int", NULL
|
"0", "1", "2", "3", "4", "5", "7", "10", "11", "int", NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char* x1_010EnvBits[8]={
|
||||||
|
"enable", "oneshot", "split L/R", "HinvR", "VinvR", "HinvL", "VinvL", NULL
|
||||||
|
};
|
||||||
|
|
||||||
const char* oneBit[2]={
|
const char* oneBit[2]={
|
||||||
"on", NULL
|
"on", NULL
|
||||||
};
|
};
|
||||||
|
@ -791,9 +768,9 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
} else {
|
} else {
|
||||||
DivInstrument* ins=e->song.ins[curIns];
|
DivInstrument* ins=e->song.ins[curIns];
|
||||||
ImGui::InputText("Name",&ins->name);
|
ImGui::InputText("Name",&ins->name);
|
||||||
if (ins->type<0 || ins->type>23) ins->type=DIV_INS_FM;
|
if (ins->type<0 || ins->type>=DIV_INS_MAX) ins->type=DIV_INS_FM;
|
||||||
int insType=ins->type;
|
int insType=ins->type;
|
||||||
if (ImGui::Combo("Type",&insType,insTypes,24,24)) {
|
if (ImGui::Combo("Type",&insType,insTypes,DIV_INS_MAX,DIV_INS_MAX)) {
|
||||||
ins->type=(DivInstrumentType)insType;
|
ins->type=(DivInstrumentType)insType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1355,7 +1332,7 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
float loopIndicator[256];
|
float loopIndicator[256];
|
||||||
const char* volumeLabel="Volume";
|
const char* volumeLabel="Volume";
|
||||||
|
|
||||||
int volMax=(ins->type==DIV_INS_PCE || ins->type==DIV_INS_AY8930)?31:15;
|
int volMax=15;
|
||||||
int volMin=0;
|
int volMin=0;
|
||||||
if (ins->type==DIV_INS_C64) {
|
if (ins->type==DIV_INS_C64) {
|
||||||
if (ins->c64.volIsCutoff) {
|
if (ins->c64.volIsCutoff) {
|
||||||
|
@ -1368,6 +1345,12 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ((ins->type==DIV_INS_PCE || ins->type==DIV_INS_AY8930)) {
|
||||||
|
volMax=31;
|
||||||
|
}
|
||||||
|
if (ins->type==DIV_INS_VERA) {
|
||||||
|
volMax=63;
|
||||||
|
}
|
||||||
if (ins->type==DIV_INS_AMIGA) {
|
if (ins->type==DIV_INS_AMIGA) {
|
||||||
volMax=64;
|
volMax=64;
|
||||||
}
|
}
|
||||||
|
@ -1381,7 +1364,7 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
bool arpMode=ins->std.arpMacroMode;
|
bool arpMode=ins->std.arpMacroMode;
|
||||||
|
|
||||||
const char* dutyLabel="Duty/Noise";
|
const char* dutyLabel="Duty/Noise";
|
||||||
int dutyMax=(ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?31:3;
|
int dutyMax=3;
|
||||||
if (ins->type==DIV_INS_C64) {
|
if (ins->type==DIV_INS_C64) {
|
||||||
dutyLabel="Duty";
|
dutyLabel="Duty";
|
||||||
if (ins->c64.dutyIsAbs) {
|
if (ins->c64.dutyIsAbs) {
|
||||||
|
@ -1393,6 +1376,9 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
if (ins->type==DIV_INS_FM) {
|
if (ins->type==DIV_INS_FM) {
|
||||||
dutyMax=32;
|
dutyMax=32;
|
||||||
}
|
}
|
||||||
|
if ((ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)) {
|
||||||
|
dutyMax=31;
|
||||||
|
}
|
||||||
if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_FM) {
|
if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_FM) {
|
||||||
dutyLabel="Noise Freq";
|
dutyLabel="Noise Freq";
|
||||||
}
|
}
|
||||||
|
@ -1416,9 +1402,13 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
if (ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL) {
|
if (ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL) {
|
||||||
dutyMax=0;
|
dutyMax=0;
|
||||||
}
|
}
|
||||||
|
if (ins->type==DIV_INS_VERA) {
|
||||||
|
dutyLabel="Duty";
|
||||||
|
dutyMax=63;
|
||||||
|
}
|
||||||
bool dutyIsRel=(ins->type==DIV_INS_C64 && !ins->c64.dutyIsAbs);
|
bool dutyIsRel=(ins->type==DIV_INS_C64 && !ins->c64.dutyIsAbs);
|
||||||
|
|
||||||
int waveMax=(ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?3:63;
|
int waveMax=(ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_VERA)?3:63;
|
||||||
bool bitMode=false;
|
bool bitMode=false;
|
||||||
if (ins->type==DIV_INS_C64 || ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SAA1099) {
|
if (ins->type==DIV_INS_C64 || ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SAA1099) {
|
||||||
bitMode=true;
|
bitMode=true;
|
||||||
|
@ -1435,11 +1425,18 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
|
|
||||||
int ex1Max=(ins->type==DIV_INS_AY8930)?8:0;
|
int ex1Max=(ins->type==DIV_INS_AY8930)?8:0;
|
||||||
int ex2Max=(ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?4:0;
|
int ex2Max=(ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?4:0;
|
||||||
|
bool ex2Bit=true;
|
||||||
|
|
||||||
if (ins->type==DIV_INS_C64) {
|
if (ins->type==DIV_INS_C64) {
|
||||||
ex1Max=4;
|
ex1Max=4;
|
||||||
ex2Max=15;
|
ex2Max=15;
|
||||||
}
|
}
|
||||||
|
if (ins->type==DIV_INS_X1_010) {
|
||||||
|
dutyMax=0;
|
||||||
|
ex1Max=7;
|
||||||
|
ex2Max=63;
|
||||||
|
ex2Bit=false;
|
||||||
|
}
|
||||||
if (ins->type==DIV_INS_SAA1099) ex1Max=8;
|
if (ins->type==DIV_INS_SAA1099) ex1Max=8;
|
||||||
|
|
||||||
if (settings.macroView==0) { // modern view
|
if (settings.macroView==0) { // modern view
|
||||||
|
@ -1464,6 +1461,8 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
NORMAL_MACRO(ins->std.ex1Macro,ins->std.ex1MacroLen,ins->std.ex1MacroLoop,ins->std.ex1MacroRel,0,ex1Max,"ex1","Filter Mode",64,ins->std.ex1MacroOpen,true,filtModeBits,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false);
|
NORMAL_MACRO(ins->std.ex1Macro,ins->std.ex1MacroLen,ins->std.ex1MacroLoop,ins->std.ex1MacroRel,0,ex1Max,"ex1","Filter Mode",64,ins->std.ex1MacroOpen,true,filtModeBits,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false);
|
||||||
} else if (ins->type==DIV_INS_SAA1099) {
|
} else if (ins->type==DIV_INS_SAA1099) {
|
||||||
NORMAL_MACRO(ins->std.ex1Macro,ins->std.ex1MacroLen,ins->std.ex1MacroLoop,ins->std.ex1MacroRel,0,ex1Max,"ex1","Envelope",160,ins->std.ex1MacroOpen,true,saaEnvBits,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false);
|
NORMAL_MACRO(ins->std.ex1Macro,ins->std.ex1MacroLen,ins->std.ex1MacroLoop,ins->std.ex1MacroRel,0,ex1Max,"ex1","Envelope",160,ins->std.ex1MacroOpen,true,saaEnvBits,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false);
|
||||||
|
} else if (ins->type==DIV_INS_X1_010) {
|
||||||
|
NORMAL_MACRO(ins->std.ex1Macro,ins->std.ex1MacroLen,ins->std.ex1MacroLoop,ins->std.ex1MacroRel,0,ex1Max,"ex1","Envelope Mode",160,ins->std.ex1MacroOpen,true,x1_010EnvBits,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false);
|
||||||
} else {
|
} else {
|
||||||
NORMAL_MACRO(ins->std.ex1Macro,ins->std.ex1MacroLen,ins->std.ex1MacroLoop,ins->std.ex1MacroRel,0,ex1Max,"ex1","Duty",160,ins->std.ex1MacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false);
|
NORMAL_MACRO(ins->std.ex1Macro,ins->std.ex1MacroLen,ins->std.ex1MacroLoop,ins->std.ex1MacroRel,0,ex1Max,"ex1","Duty",160,ins->std.ex1MacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false);
|
||||||
}
|
}
|
||||||
|
@ -1472,13 +1471,13 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
if (ins->type==DIV_INS_C64) {
|
if (ins->type==DIV_INS_C64) {
|
||||||
NORMAL_MACRO(ins->std.ex2Macro,ins->std.ex2MacroLen,ins->std.ex2MacroLoop,ins->std.ex2MacroRel,0,ex2Max,"ex2","Resonance",64,ins->std.ex2MacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false);
|
NORMAL_MACRO(ins->std.ex2Macro,ins->std.ex2MacroLen,ins->std.ex2MacroLoop,ins->std.ex2MacroRel,0,ex2Max,"ex2","Resonance",64,ins->std.ex2MacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false);
|
||||||
} else {
|
} else {
|
||||||
NORMAL_MACRO(ins->std.ex2Macro,ins->std.ex2MacroLen,ins->std.ex2MacroLoop,ins->std.ex2MacroRel,0,ex2Max,"ex2","Envelope",64,ins->std.ex2MacroOpen,true,ayEnvBits,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false);
|
NORMAL_MACRO(ins->std.ex2Macro,ins->std.ex2MacroLen,ins->std.ex2MacroLoop,ins->std.ex2MacroRel,0,ex2Max,"ex2","Envelope",ex2Bit?64:160,ins->std.ex2MacroOpen,ex2Bit,ayEnvBits,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ins->type==DIV_INS_C64) {
|
if (ins->type==DIV_INS_C64) {
|
||||||
NORMAL_MACRO(ins->std.ex3Macro,ins->std.ex3MacroLen,ins->std.ex3MacroLoop,ins->std.ex3MacroRel,0,2,"ex3","Special",32,ins->std.ex3MacroOpen,true,c64SpecialBits,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false);
|
NORMAL_MACRO(ins->std.ex3Macro,ins->std.ex3MacroLen,ins->std.ex3MacroLoop,ins->std.ex3MacroRel,0,2,"ex3","Special",32,ins->std.ex3MacroOpen,true,c64SpecialBits,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false);
|
||||||
}
|
}
|
||||||
if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930) {
|
if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_X1_010) {
|
||||||
NORMAL_MACRO(ins->std.ex3Macro,ins->std.ex3MacroLen,ins->std.ex3MacroLoop,ins->std.ex3MacroRel,0,15,"ex3","AutoEnv Num",96,ins->std.ex3MacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,15,NULL,false);
|
NORMAL_MACRO(ins->std.ex3Macro,ins->std.ex3MacroLen,ins->std.ex3MacroLoop,ins->std.ex3MacroRel,0,15,"ex3","AutoEnv Num",96,ins->std.ex3MacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,15,NULL,false);
|
||||||
NORMAL_MACRO(ins->std.algMacro,ins->std.algMacroLen,ins->std.algMacroLoop,ins->std.algMacroRel,0,15,"alg","AutoEnv Den",96,ins->std.algMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,15,NULL,false);
|
NORMAL_MACRO(ins->std.algMacro,ins->std.algMacroLen,ins->std.algMacroLoop,ins->std.algMacroRel,0,15,"alg","AutoEnv Den",96,ins->std.algMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,15,NULL,false);
|
||||||
}
|
}
|
||||||
|
@ -1759,7 +1758,6 @@ void FurnaceGUI::drawWaveList() {
|
||||||
nextWindow=GUI_WINDOW_NOTHING;
|
nextWindow=GUI_WINDOW_NOTHING;
|
||||||
}
|
}
|
||||||
if (!waveListOpen) return;
|
if (!waveListOpen) return;
|
||||||
float wavePreview[256];
|
|
||||||
if (ImGui::Begin("Wavetables",&waveListOpen)) {
|
if (ImGui::Begin("Wavetables",&waveListOpen)) {
|
||||||
if (ImGui::Button(ICON_FA_PLUS "##WaveAdd")) {
|
if (ImGui::Button(ICON_FA_PLUS "##WaveAdd")) {
|
||||||
doAction(GUI_ACTION_WAVE_LIST_ADD);
|
doAction(GUI_ACTION_WAVE_LIST_ADD);
|
||||||
|
@ -1790,25 +1788,7 @@ void FurnaceGUI::drawWaveList() {
|
||||||
}
|
}
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (ImGui::BeginTable("WaveListScroll",1,ImGuiTableFlags_ScrollY)) {
|
if (ImGui::BeginTable("WaveListScroll",1,ImGuiTableFlags_ScrollY)) {
|
||||||
for (int i=0; i<(int)e->song.wave.size(); i++) {
|
actualWaveList();
|
||||||
DivWavetable* wave=e->song.wave[i];
|
|
||||||
for (int i=0; i<wave->len; i++) {
|
|
||||||
wavePreview[i]=wave->data[i];
|
|
||||||
}
|
|
||||||
if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1];
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
if (ImGui::Selectable(fmt::sprintf("%d##_WAVE%d\n",i,i).c_str(),curWave==i)) {
|
|
||||||
curWave=i;
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
|
||||||
waveEditOpen=true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
PlotNoLerp(fmt::sprintf("##_WAVEP%d",i).c_str(),wavePreview,wave->len+1,0,NULL,0,wave->max);
|
|
||||||
}
|
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1857,6 +1837,7 @@ void FurnaceGUI::drawWaveEdit() {
|
||||||
modified=true;
|
modified=true;
|
||||||
}
|
}
|
||||||
for (int i=0; i<wave->len; i++) {
|
for (int i=0; i<wave->len; i++) {
|
||||||
|
if (wave->data[i]>wave->max) wave->data[i]=wave->max;
|
||||||
wavePreview[i]=wave->data[i];
|
wavePreview[i]=wave->data[i];
|
||||||
}
|
}
|
||||||
if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1];
|
if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1];
|
||||||
|
|
|
@ -830,22 +830,8 @@ void FurnaceGUI::drawPattern() {
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
if (patternOpen) {
|
if (patternOpen) {
|
||||||
if (!inhibitMenu && ImGui::IsItemClicked(ImGuiMouseButton_Right)) ImGui::OpenPopup("patternActionMenu");
|
if (!inhibitMenu && ImGui::IsItemClicked(ImGuiMouseButton_Right)) ImGui::OpenPopup("patternActionMenu");
|
||||||
if (ImGui::BeginPopup("patternActionMenu",ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
|
if (ImGui::BeginPopup("patternActionMenu",ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
|
||||||
char id[4096];
|
editOptions(false);
|
||||||
ImGui::Selectable("cut");
|
|
||||||
ImGui::Selectable("copy");
|
|
||||||
ImGui::Selectable("paste");
|
|
||||||
if (ImGui::BeginMenu("change instrument...")) {
|
|
||||||
if (e->song.ins.empty()) {
|
|
||||||
ImGui::Text("no instruments available");
|
|
||||||
}
|
|
||||||
for (size_t i=0; i<e->song.ins.size(); i++) {
|
|
||||||
snprintf(id,4095,"%.2X: %s",(int)i,e->song.ins[i]->name.c_str());
|
|
||||||
if (ImGui::Selectable(id)) { // TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,6 +157,11 @@ void FurnaceGUI::drawSettings() {
|
||||||
settings.stepOnDelete=stepOnDeleteB;
|
settings.stepOnDelete=stepOnDeleteB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool stepOnInsertB=settings.stepOnInsert;
|
||||||
|
if (ImGui::Checkbox("Move cursor by edit step on insert (push)",&stepOnInsertB)) {
|
||||||
|
settings.stepOnInsert=stepOnInsertB;
|
||||||
|
}
|
||||||
|
|
||||||
bool allowEditDockingB=settings.allowEditDocking;
|
bool allowEditDockingB=settings.allowEditDocking;
|
||||||
if (ImGui::Checkbox("Allow docking editors",&allowEditDockingB)) {
|
if (ImGui::Checkbox("Allow docking editors",&allowEditDockingB)) {
|
||||||
settings.allowEditDocking=allowEditDockingB;
|
settings.allowEditDocking=allowEditDockingB;
|
||||||
|
@ -177,6 +182,11 @@ void FurnaceGUI::drawSettings() {
|
||||||
settings.restartOnFlagChange=restartOnFlagChangeB;
|
settings.restartOnFlagChange=restartOnFlagChangeB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sysFileDialogB=settings.sysFileDialog;
|
||||||
|
if (ImGui::Checkbox("Use system file picker",&sysFileDialogB)) {
|
||||||
|
settings.sysFileDialog=sysFileDialogB;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Text("Wrap pattern cursor horizontally:");
|
ImGui::Text("Wrap pattern cursor horizontally:");
|
||||||
if (ImGui::RadioButton("No##wrapH0",settings.wrapHorizontal==0)) {
|
if (ImGui::RadioButton("No##wrapH0",settings.wrapHorizontal==0)) {
|
||||||
settings.wrapHorizontal=0;
|
settings.wrapHorizontal=0;
|
||||||
|
@ -206,6 +216,7 @@ void FurnaceGUI::drawSettings() {
|
||||||
if (ImGui::RadioButton("Move by Edit Step##cmk1",settings.scrollStep==1)) {
|
if (ImGui::RadioButton("Move by Edit Step##cmk1",settings.scrollStep==1)) {
|
||||||
settings.scrollStep=1;
|
settings.scrollStep=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
if (ImGui::BeginTabItem("Audio")) {
|
if (ImGui::BeginTabItem("Audio")) {
|
||||||
|
@ -397,6 +408,11 @@ void FurnaceGUI::drawSettings() {
|
||||||
settings.macroView=macroViewB;
|
settings.macroView=macroViewB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool unifiedDataViewB=settings.unifiedDataView;
|
||||||
|
if (ImGui::Checkbox("Unified instrument/wavetable/sample list",&unifiedDataViewB)) {
|
||||||
|
settings.unifiedDataView=unifiedDataViewB;
|
||||||
|
}
|
||||||
|
|
||||||
bool chipNamesB=settings.chipNames;
|
bool chipNamesB=settings.chipNames;
|
||||||
if (ImGui::Checkbox("Use chip names instead of system names",&chipNamesB)) {
|
if (ImGui::Checkbox("Use chip names instead of system names",&chipNamesB)) {
|
||||||
settings.chipNames=chipNamesB;
|
settings.chipNames=chipNamesB;
|
||||||
|
@ -492,6 +508,8 @@ void FurnaceGUI::drawSettings() {
|
||||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_BEEPER,"PC Beeper");
|
UI_COLOR_CONFIG(GUI_COLOR_INSTR_BEEPER,"PC Beeper");
|
||||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_SWAN,"WonderSwan");
|
UI_COLOR_CONFIG(GUI_COLOR_INSTR_SWAN,"WonderSwan");
|
||||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_MIKEY,"Lynx");
|
UI_COLOR_CONFIG(GUI_COLOR_INSTR_MIKEY,"Lynx");
|
||||||
|
UI_COLOR_CONFIG(GUI_COLOR_INSTR_VERA,"VERA");
|
||||||
|
UI_COLOR_CONFIG(GUI_COLOR_INSTR_X1_010,"X1-010");
|
||||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown");
|
UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown");
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
|
@ -853,7 +871,7 @@ void FurnaceGUI::syncSettings() {
|
||||||
settings.audioRate=e->getConfInt("audioRate",44100);
|
settings.audioRate=e->getConfInt("audioRate",44100);
|
||||||
settings.arcadeCore=e->getConfInt("arcadeCore",0);
|
settings.arcadeCore=e->getConfInt("arcadeCore",0);
|
||||||
settings.ym2612Core=e->getConfInt("ym2612Core",0);
|
settings.ym2612Core=e->getConfInt("ym2612Core",0);
|
||||||
settings.saaCore=e->getConfInt("saaCore",0);
|
settings.saaCore=e->getConfInt("saaCore",1);
|
||||||
settings.mainFont=e->getConfInt("mainFont",0);
|
settings.mainFont=e->getConfInt("mainFont",0);
|
||||||
settings.patFont=e->getConfInt("patFont",0);
|
settings.patFont=e->getConfInt("patFont",0);
|
||||||
settings.mainFontPath=e->getConfString("mainFontPath","");
|
settings.mainFontPath=e->getConfString("mainFontPath","");
|
||||||
|
@ -883,6 +901,9 @@ void FurnaceGUI::syncSettings() {
|
||||||
settings.guiColorsBase=e->getConfInt("guiColorsBase",0);
|
settings.guiColorsBase=e->getConfInt("guiColorsBase",0);
|
||||||
settings.avoidRaisingPattern=e->getConfInt("avoidRaisingPattern",0);
|
settings.avoidRaisingPattern=e->getConfInt("avoidRaisingPattern",0);
|
||||||
settings.insFocusesPattern=e->getConfInt("insFocusesPattern",1);
|
settings.insFocusesPattern=e->getConfInt("insFocusesPattern",1);
|
||||||
|
settings.stepOnInsert=e->getConfInt("stepOnInsert",0);
|
||||||
|
settings.unifiedDataView=e->getConfInt("unifiedDataView",0);
|
||||||
|
settings.sysFileDialog=e->getConfInt("sysFileDialog",1);
|
||||||
|
|
||||||
clampSetting(settings.mainFontSize,2,96);
|
clampSetting(settings.mainFontSize,2,96);
|
||||||
clampSetting(settings.patFontSize,2,96);
|
clampSetting(settings.patFontSize,2,96);
|
||||||
|
@ -920,6 +941,9 @@ void FurnaceGUI::syncSettings() {
|
||||||
clampSetting(settings.guiColorsBase,0,1);
|
clampSetting(settings.guiColorsBase,0,1);
|
||||||
clampSetting(settings.avoidRaisingPattern,0,1);
|
clampSetting(settings.avoidRaisingPattern,0,1);
|
||||||
clampSetting(settings.insFocusesPattern,0,1);
|
clampSetting(settings.insFocusesPattern,0,1);
|
||||||
|
clampSetting(settings.stepOnInsert,0,1);
|
||||||
|
clampSetting(settings.unifiedDataView,0,1);
|
||||||
|
clampSetting(settings.sysFileDialog,0,1);
|
||||||
|
|
||||||
// keybinds
|
// keybinds
|
||||||
LOAD_KEYBIND(GUI_ACTION_OPEN,FURKMOD_CMD|SDLK_o);
|
LOAD_KEYBIND(GUI_ACTION_OPEN,FURKMOD_CMD|SDLK_o);
|
||||||
|
@ -1118,6 +1142,9 @@ void FurnaceGUI::commitSettings() {
|
||||||
e->setConf("guiColorsBase",settings.guiColorsBase);
|
e->setConf("guiColorsBase",settings.guiColorsBase);
|
||||||
e->setConf("avoidRaisingPattern",settings.avoidRaisingPattern);
|
e->setConf("avoidRaisingPattern",settings.avoidRaisingPattern);
|
||||||
e->setConf("insFocusesPattern",settings.insFocusesPattern);
|
e->setConf("insFocusesPattern",settings.insFocusesPattern);
|
||||||
|
e->setConf("stepOnInsert",settings.stepOnInsert);
|
||||||
|
e->setConf("unifiedDataView",settings.unifiedDataView);
|
||||||
|
e->setConf("sysFileDialog",settings.sysFileDialog);
|
||||||
|
|
||||||
PUT_UI_COLOR(GUI_COLOR_BACKGROUND);
|
PUT_UI_COLOR(GUI_COLOR_BACKGROUND);
|
||||||
PUT_UI_COLOR(GUI_COLOR_FRAME_BACKGROUND);
|
PUT_UI_COLOR(GUI_COLOR_FRAME_BACKGROUND);
|
||||||
|
@ -1159,6 +1186,8 @@ void FurnaceGUI::commitSettings() {
|
||||||
PUT_UI_COLOR(GUI_COLOR_INSTR_BEEPER);
|
PUT_UI_COLOR(GUI_COLOR_INSTR_BEEPER);
|
||||||
PUT_UI_COLOR(GUI_COLOR_INSTR_SWAN);
|
PUT_UI_COLOR(GUI_COLOR_INSTR_SWAN);
|
||||||
PUT_UI_COLOR(GUI_COLOR_INSTR_MIKEY);
|
PUT_UI_COLOR(GUI_COLOR_INSTR_MIKEY);
|
||||||
|
PUT_UI_COLOR(GUI_COLOR_INSTR_VERA);
|
||||||
|
PUT_UI_COLOR(GUI_COLOR_INSTR_X1_010);
|
||||||
PUT_UI_COLOR(GUI_COLOR_INSTR_UNKNOWN);
|
PUT_UI_COLOR(GUI_COLOR_INSTR_UNKNOWN);
|
||||||
PUT_UI_COLOR(GUI_COLOR_CHANNEL_FM);
|
PUT_UI_COLOR(GUI_COLOR_CHANNEL_FM);
|
||||||
PUT_UI_COLOR(GUI_COLOR_CHANNEL_PULSE);
|
PUT_UI_COLOR(GUI_COLOR_CHANNEL_PULSE);
|
||||||
|
|
Loading…
Reference in a new issue