Merge branch 'master' into dialog-nitpicks
This commit is contained in:
commit
a099c313ab
|
@ -11,7 +11,7 @@ defaults:
|
|||
shell: bash
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Debug
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -82,7 +82,7 @@ jobs:
|
|||
package_ext=".dmg"
|
||||
else
|
||||
package_name="${package_name}-Linux-${{ matrix.config.arch }}"
|
||||
package_ext=".AppImage"
|
||||
package_ext=".tar.gz"
|
||||
fi
|
||||
|
||||
echo "Package identifier: ${package_name}"
|
||||
|
@ -128,10 +128,7 @@ jobs:
|
|||
librtmidi-dev \
|
||||
libsndfile1-dev \
|
||||
zlib1g-dev \
|
||||
libjack-jackd2-dev \
|
||||
appstream
|
||||
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" || wget "https://tildearrow.org/storage/furnace/ci/appimagetool-x86_64.AppImage"
|
||||
chmod +x appimagetool-x86_64.AppImage
|
||||
libjack-jackd2-dev
|
||||
|
||||
- name: Install Dependencies [Linux armhf]
|
||||
if: ${{ runner.os == 'Linux' && matrix.config.compiler != 'mingw' && matrix.config.arch == 'armhf' }}
|
||||
|
@ -151,9 +148,6 @@ jobs:
|
|||
libsndfile1-dev:armhf \
|
||||
zlib1g-dev:armhf \
|
||||
libjack-jackd2-dev:armhf
|
||||
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" || wget "https://tildearrow.org/storage/furnace/ci/appimagetool-x86_64.AppImage"
|
||||
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-armhf" || wget "https://tildearrow.org/storage/furnace/ci/runtime-armhf"
|
||||
chmod +x appimagetool-x86_64.AppImage
|
||||
ls /usr/arm-linux-gnueabihf/lib
|
||||
|
||||
- name: Configure (System Libraries)
|
||||
|
@ -243,6 +237,7 @@ jobs:
|
|||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
|
||||
-DWARNINGS_ARE_ERRORS=${USE_WAE} \
|
||||
-DWITH_DEMOS=OFF -DWITH_INSTRUMENTS=OFF -DWITH_WAVETABLES=OFF \
|
||||
"${CMAKE_EXTRA_ARGS[@]}"
|
||||
|
||||
- name: Build
|
||||
|
@ -300,24 +295,37 @@ jobs:
|
|||
# strip -s build/furnace
|
||||
#fi
|
||||
|
||||
mkdir -p target/furnace.AppDir
|
||||
make -C ${PWD}/build DESTDIR=${PWD}/target/furnace.AppDir install
|
||||
pushd target
|
||||
mkdir -p target/furnace
|
||||
make -C ${PWD}/build DESTDIR=${PWD}/target/furnace install
|
||||
pushd target/furnace
|
||||
|
||||
cp -v ../../res/logo.png .DirIcon
|
||||
|
||||
cd usr
|
||||
|
||||
mv bin/furnace ..
|
||||
rmdir bin
|
||||
|
||||
rm -r share/applications
|
||||
rm -r share/doc
|
||||
rm -r share/icons
|
||||
rm -r share/licenses
|
||||
rm -r share/metainfo
|
||||
|
||||
rmdir share
|
||||
|
||||
cd ..
|
||||
|
||||
cp ../../LICENSE .
|
||||
cp ../../README.md .
|
||||
cp -r ../../papers papers
|
||||
rmdir usr
|
||||
|
||||
pushd furnace.AppDir
|
||||
cp -v usr/share/{icons/hicolor/1024x1024/apps/furnace.png,applications/furnace.desktop} ./
|
||||
ln -s furnace.png .DirIcon
|
||||
mv -v usr/share/metainfo/{furnace.appdata,org.tildearrow.furnace.metainfo}.xml
|
||||
cp -v ../../res/AppRun ./
|
||||
popd
|
||||
|
||||
if [ '${{ matrix.config.arch }}' == 'armhf' ]; then
|
||||
../appimagetool-x86_64.AppImage --runtime-file=../runtime-armhf furnace.AppDir
|
||||
else
|
||||
../appimagetool-x86_64.AppImage furnace.AppDir
|
||||
fi
|
||||
mv Furnace-*.AppImage ../${{ steps.package-identify.outputs.filename }}
|
||||
popd
|
||||
cd target
|
||||
|
||||
tar -zcv -f ../${{ steps.package-identify.outputs.filename }} furnace
|
||||
|
||||
- name: Upload artifact
|
||||
if: ${{ github.repository == 'tildearrow/furnace' && github.ref_name == 'master' }}
|
||||
|
|
|
@ -556,6 +556,9 @@ src/engine/platform/dummy.cpp
|
|||
|
||||
src/engine/export/abstract.cpp
|
||||
src/engine/export/amigaValidation.cpp
|
||||
|
||||
src/engine/effect/abstract.cpp
|
||||
src/engine/effect/dummy.cpp
|
||||
)
|
||||
|
||||
if (USE_SNDFILE)
|
||||
|
@ -792,7 +795,7 @@ endif()
|
|||
if(ANDROID AND NOT TERMUX)
|
||||
add_library(furnace SHARED ${USED_SOURCES})
|
||||
elseif(WIN32)
|
||||
add_executable(furnace ${USED_SOURCES})
|
||||
add_executable(furnace WIN32 ${USED_SOURCES})
|
||||
else()
|
||||
add_executable(furnace ${USED_SOURCES})
|
||||
endif()
|
||||
|
|
113
README.md
113
README.md
|
@ -1,17 +1,17 @@
|
|||
# Furnace (chiptune tracker)
|
||||
|
||||
![screenshot](papers/screenshot2.png)
|
||||
![screenshot](papers/screenshot3.png)
|
||||
|
||||
the biggest multi-system chiptune tracker ever made!
|
||||
|
||||
[downloads](#downloads) | [discussion/help](#quick-references) | [developer info](#developer-info) | [unofficial packages](#unofficial-packages) | [FAQ](#frequently-asked-questions)
|
||||
[downloads](#downloads) | [discussion/help](#quick-references) | [developer info](#developer-info) | [Unix/Linux packages](#packages) | [FAQ](#frequently-asked-questions)
|
||||
|
||||
---
|
||||
## downloads
|
||||
|
||||
check out the [Releases](https://github.com/tildearrow/furnace/releases) page. available for Windows, macOS and Linux (AppImage).
|
||||
|
||||
[see here](https://nightly.link/tildearrow/furnace/workflows/build/master) for unstable developer builds.
|
||||
[see here](https://nightly.link/tildearrow/furnace/workflows/build/master) for the latest unstable build.
|
||||
|
||||
## features
|
||||
|
||||
|
@ -66,9 +66,12 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
|
|||
- Family Noraebang (OPLL)
|
||||
- SID (6581/8580) used in Commodore 64
|
||||
- Mikey used in Atari Lynx
|
||||
- ZX Spectrum beeper (SFX-like engine)
|
||||
- ZX Spectrum beeper
|
||||
- SFX-like engine
|
||||
- QuadTone engine
|
||||
- Pokémon Mini
|
||||
- Commodore PET
|
||||
- Casio PV-1000
|
||||
- TIA used in Atari 2600
|
||||
- POKEY used in Atari 8-bit computers
|
||||
- Game Boy
|
||||
|
@ -80,7 +83,7 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
|
|||
- over 200 ready to use presets from computers, game consoles and arcade boards...
|
||||
- ...or create your own - up to 32 of them or a total of 128 channels!
|
||||
- DefleMask compatibility
|
||||
- loads .dmf modules from all versions (beta 1 to 1.1.7)
|
||||
- loads .dmf modules from all versions (beta 1 to 1.1.9)
|
||||
- saves .dmf modules - both modern and legacy
|
||||
- Furnace doubles as a module downgrader
|
||||
- loads/saves .dmp instruments and .dmw wavetables as well
|
||||
|
@ -116,18 +119,19 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
|
|||
---
|
||||
# quick references
|
||||
|
||||
- **discussion**: see the [Discussions](https://github.com/tildearrow/furnace/discussions) section, or (preferably) the [official Discord server](https://discord.gg/EfrwT2wq7z).
|
||||
- **help**: check out the [documentation](papers/doc/README.md). it's mostly incomplete, but has details on effects.
|
||||
- **discussion**: see the [Discussions](https://github.com/tildearrow/furnace/discussions) section, or the [official Discord server](https://discord.gg/EfrwT2wq7z).
|
||||
- **help**: check out the [documentation](papers/doc/README.md). it's incomplete, but has details on effects.
|
||||
|
||||
## unofficial packages
|
||||
## packages
|
||||
|
||||
[![Packaging status](https://repology.org/badge/tiny-repos/furnace.svg)](https://repology.org/project/furnace/versions)
|
||||
|
||||
some people have provided packages for Unix/Unix-like distributions. here's a list.
|
||||
- **Arch Linux**: [furnace](https://archlinux.org/packages/community/x86_64/furnace/) is now in the community repo!
|
||||
- **FreeBSD**: [a package in ports](https://www.freshports.org/audio/furnace/) is available courtesy of ehaupt.
|
||||
- **Nix**: [package](https://search.nixos.org/packages?channel=unstable&show=furnace&from=0&size=50&sort=relevance&type=packages&query=furnace) thanks to OPNA2608.
|
||||
- **openSUSE**: [a package](https://software.opensuse.org/package/furnace) is available, courtesy of fpesari.
|
||||
|
||||
- **Arch Linux**: [furnace](https://archlinux.org/packages/extra/x86_64/furnace/) is in the official repositories.
|
||||
- **FreeBSD**: [a package in ports](https://www.freshports.org/audio/furnace/) is available courtesy of ehaupt (warning: 0.5.8!).
|
||||
- **Nix**: [package](https://search.nixos.org/packages?channel=unstable&show=furnace&from=0&size=50&sort=relevance&type=packages&query=furnace) thanks to OPNA2608.
|
||||
- **openSUSE**: [a package](https://software.opensuse.org/package/furnace) is available, courtesy of fpesari.
|
||||
|
||||
---
|
||||
# developer info
|
||||
|
@ -141,7 +145,9 @@ if you can't download these artifacts (because GitHub requires you to be logged
|
|||
## dependencies
|
||||
|
||||
- CMake
|
||||
- Git (for cloning the repository)
|
||||
- JACK (optional, macOS/Linux only)
|
||||
- a C/C++ compiler (e.g. Visual Studio or MinGW on Windows, Xcode (the command-line tools are enough) on macOS or GCC on Linux)
|
||||
|
||||
if building under Windows or macOS, no additional dependencies are required.
|
||||
otherwise, you may also need the following:
|
||||
|
@ -178,10 +184,32 @@ from the developer tools command prompt:
|
|||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
```
|
||||
|
||||
then open the solution file in Visual Studio and build.
|
||||
|
||||
alternatively, do:
|
||||
|
||||
```
|
||||
msbuild ALL_BUILD.vcxproj
|
||||
```
|
||||
|
||||
### macOS and Linux
|
||||
### Windows using MinGW
|
||||
|
||||
setting up MinGW is a bit more complicated. two benefits are a faster, hotter Furnace, and Windows XP support.
|
||||
|
||||
however, one huge drawback is lack of backtrace support, so you'll have to use gdb when diagnosing a crash.
|
||||
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G "MinGW Makefiles" ..
|
||||
mingw32-make
|
||||
```
|
||||
|
||||
you may use "MSYS Makefiles" instead, depending on how you installed MinGW.
|
||||
|
||||
### macOS, Linux and other Unix/Unix-like
|
||||
|
||||
```
|
||||
mkdir build
|
||||
|
@ -189,7 +217,16 @@ cd build
|
|||
cmake ..
|
||||
make
|
||||
```
|
||||
Alternatively, build scripts are provided in the `scripts/` folder in the root of the repository.
|
||||
|
||||
on macOS you may do the following instead:
|
||||
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G Xcode ..
|
||||
```
|
||||
|
||||
...and then load the project on Xcode or type `xcodebuild`.
|
||||
|
||||
### CMake options
|
||||
|
||||
|
@ -220,6 +257,8 @@ Available options:
|
|||
|
||||
## console usage
|
||||
|
||||
(note: if on Windows, type `furnace.exe` instead, or `Debug\furnace.exe` on MSVC)
|
||||
|
||||
```
|
||||
./furnace
|
||||
```
|
||||
|
@ -238,23 +277,17 @@ this will play a compatible file.
|
|||
|
||||
this will play a compatible file and enable the commands view.
|
||||
|
||||
**note that these commands only actually work in Linux environments. on other command lines, such as Windows' Command Prompt, or MacOS Terminal, it may not work correctly.**
|
||||
**note that console mode may not work correctly on Windows. you may have to quit using the Task Manager.**
|
||||
|
||||
---
|
||||
# frequently asked questions
|
||||
|
||||
> woah! 50 sound chips?! I can't believe it!
|
||||
|
||||
yup, it's real.
|
||||
|
||||
> where's the manual?
|
||||
|
||||
see [papers/](papers/doc/README.md). it's kind of incomplete, but at least the sound chips section is there.
|
||||
|
||||
> it doesn't open under macOS!
|
||||
|
||||
this is due to Apple's application signing policy. a workaround is to right click on the Furnace app icon and select Open.
|
||||
|
||||
> it says "Furnace" is damaged and can't be opened!
|
||||
|
||||
**as of Monterey, this workaround no longer works (especially on ARM).** yeah, Apple has decided to be strict on the matter.
|
||||
if you happen to be on that version, use this workaround instead (on a Terminal):
|
||||
|
||||
|
@ -266,24 +299,25 @@ xattr -d com.apple.quarantine /path/to/Furnace.app
|
|||
|
||||
you may need to log out and/or reboot after doing this.
|
||||
|
||||
> where's the manual?
|
||||
|
||||
see [papers/](papers/doc/README.md). it's kind of incomplete, but at least the sound chips section is there.
|
||||
|
||||
> is there a tutorial?
|
||||
|
||||
sadly, the in-program tutorial isn't ready yet. however, [a video tutorial is available on YouTube](https://youtube.com/playlist?list=PLCELB6AsTZUnwv0PC5AAGHjvg47F44YQ1), made by Spinning Square Waves.
|
||||
|
||||
> I've lost my song!
|
||||
|
||||
Furnace keeps backups of the songs you've worked on before. go to **file > restore backup**.
|
||||
|
||||
> .spc export?
|
||||
|
||||
**not yet!** coming in 0.7 though, eventually...
|
||||
|
||||
> how do I use C64 absolute filter/duty?
|
||||
> ROM export?
|
||||
|
||||
on Instrument Editor in the C64 tab there are two options to toggle these.
|
||||
also provided are two effects:
|
||||
|
||||
- `3xxx`: set fine duty.
|
||||
- `4xxx`: set fine cutoff. `xxx` range is 000-7ff.
|
||||
additionally, you can change the cutoff and/or duty as a macro inside an instrument by clicking the `absolute cutoff macro` and/or `absolute duty macro` checkbox at the bottom of the instrument. (for the filter, you also need to click the checkbox that says `volume macro is cutoff macro`.)
|
||||
|
||||
> how do I use PCM on a PCM-capable chip?
|
||||
|
||||
two possibilities:
|
||||
- the recommended way is by creating the "Sample" type instrument and assigning a sample to it.
|
||||
- otherwise you may employ the DefleMask-compatible method, using `17xx` effect.
|
||||
**not yet!** coming in 0.7 though, eventually...
|
||||
|
||||
> my .dmf song sounds odd at a certain point
|
||||
|
||||
|
@ -293,10 +327,6 @@ Furnace's .dmf compatibility isn't perfect and it's mostly because DefleMask doe
|
|||
|
||||
you should only save as .dmf if you're really sure, because the DefleMask format has several limitations. save in Furnace song format instead (.fur).
|
||||
|
||||
> how do I solo channels?
|
||||
|
||||
right click on the channel name.
|
||||
|
||||
---
|
||||
# footnotes
|
||||
|
||||
|
@ -309,4 +339,5 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|||
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.
|
||||
|
||||
|
||||
despite the fact this program works with the .dmf, .dmp and .dmw file formats (besides its native .fur format), it is NOT affiliated with Delek or DefleMask in any way, nor it is a replacement for the original program.
|
||||
Furnace is NOT affiliated with Delek or DefleMask in any form, regardless of its ability to load and save the .dmf, .dmp and .dmw file formats.
|
||||
additionally, Furnace does not intend to replace DefleMask, nor any other program.
|
||||
|
|
|
@ -15,8 +15,8 @@ android {
|
|||
}
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 26
|
||||
versionCode 143
|
||||
versionName "0.6pre4"
|
||||
versionCode 158
|
||||
versionName "0.6pre5"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_APP_PLATFORM=android-21", "-DANDROID_STL=c++_static"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.tildearrow.furnace"
|
||||
android:versionCode="143"
|
||||
android:versionName="0.6pre4"
|
||||
android:versionCode="158"
|
||||
android:versionName="0.6pre5"
|
||||
android:installLocation="auto">
|
||||
|
||||
<!-- OpenGL ES 2.0 -->
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -94,13 +94,14 @@ static void ImGui_ImplSDLRenderer_SetupRenderState()
|
|||
SDL_RenderSetClipRect(bd->SDLRenderer, NULL);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer_NewFrame()
|
||||
bool ImGui_ImplSDLRenderer_NewFrame()
|
||||
{
|
||||
ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData();
|
||||
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplSDLRenderer_Init()?");
|
||||
|
||||
if (!bd->FontTexture)
|
||||
ImGui_ImplSDLRenderer_CreateDeviceObjects();
|
||||
return ImGui_ImplSDLRenderer_CreateDeviceObjects();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data)
|
||||
|
|
|
@ -21,7 +21,7 @@ struct SDL_Renderer;
|
|||
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_Init(SDL_Renderer* renderer);
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer_NewFrame();
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Called by Init/NewFrame/Shutdown
|
||||
|
|
|
@ -12478,9 +12478,12 @@ bool ImGui::LoadIniSettingsFromDisk(const char* ini_filename, bool redundancy)
|
|||
if (!file_data) continue;
|
||||
|
||||
for (size_t j=0; j<file_data_size; j++) {
|
||||
if (file_data[j]==0) {
|
||||
viable=false;
|
||||
break;
|
||||
}
|
||||
if (file_data[j]!='\r' && file_data[j]!='\n' && file_data[j]!=' ') {
|
||||
viable=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,13 +95,15 @@ static void ImGui_ImplSDLRenderer_SetupRenderState()
|
|||
SDL_RenderSetClipRect(bd->SDLRenderer, NULL);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer_NewFrame()
|
||||
bool ImGui_ImplSDLRenderer_NewFrame()
|
||||
{
|
||||
ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData();
|
||||
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplSDLRenderer_Init()?");
|
||||
|
||||
if (!bd->FontTexture)
|
||||
ImGui_ImplSDLRenderer_CreateDeviceObjects();
|
||||
return ImGui_ImplSDLRenderer_CreateDeviceObjects();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data)
|
||||
|
|
|
@ -21,7 +21,7 @@ struct SDL_Renderer;
|
|||
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_Init(SDL_Renderer* renderer);
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer_NewFrame();
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Called by Init/NewFrame/Shutdown
|
||||
|
|
|
@ -3112,8 +3112,9 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
|
|||
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
|
||||
const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
|
||||
|
||||
const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
|
||||
ItemSize(bb, style.FramePadding.y);
|
||||
if (!ItemAdd(frame_bb, id, NULL, ImGuiItemFlags_NoInertialScroll))
|
||||
if (!ItemAdd(frame_bb, id, NULL, (temp_input_allowed ? ImGuiItemFlags_Inputable : 0) | ImGuiItemFlags_NoInertialScroll))
|
||||
return false;
|
||||
|
||||
// Default format string when passing NULL
|
||||
|
@ -3122,13 +3123,29 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
|
|||
else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.)
|
||||
format = PatchFormatStringFloatToInt(format);
|
||||
|
||||
// Tabbing or CTRL-clicking on Slider turns it into an input box
|
||||
const bool hovered = ItemHoverable(frame_bb, id);
|
||||
if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavActivateInputId == id)
|
||||
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
|
||||
if (!temp_input_is_active)
|
||||
{
|
||||
SetActiveID(id, window);
|
||||
SetFocusID(id, window);
|
||||
FocusWindow(window);
|
||||
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
|
||||
const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
|
||||
const bool clicked = (hovered && g.IO.MouseClicked[0]);
|
||||
if (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id)
|
||||
{
|
||||
SetActiveID(id, window);
|
||||
SetFocusID(id, window);
|
||||
FocusWindow(window);
|
||||
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
|
||||
if (temp_input_allowed && (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id))
|
||||
temp_input_is_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (temp_input_is_active)
|
||||
{
|
||||
// Only clamp CTRL+Click input when ImGuiSliderFlags_AlwaysClamp is set
|
||||
const bool is_clamp_input = (flags & ImGuiSliderFlags_AlwaysClamp) != 0;
|
||||
return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL);
|
||||
}
|
||||
|
||||
// Draw frame
|
||||
|
@ -3154,6 +3171,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
|
|||
if (label_size.x > 0.0f)
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
|
||||
return value_changed;
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -121,8 +121,8 @@ ID | macro
|
|||
33 | KSR
|
||||
---|-----------------------------
|
||||
40 | operator 2 macros
|
||||
60 | operator 2 macros
|
||||
80 | operator 2 macros
|
||||
60 | operator 3 macros
|
||||
80 | operator 4 macros
|
||||
|
||||
the interpretation of duty, wave and extra macros depends on chip/instrument type:
|
||||
|
||||
|
|
|
@ -25,6 +25,6 @@ C64 instrument editor consists of two tabs: one controlling various parameters o
|
|||
- [Arpeggio] - pitch sequence
|
||||
- [Duty cycle] - pulse duty cycle sequence
|
||||
- [Waveform] - select the waveform used by instrument
|
||||
- [Filter mode] - select the filter mode/squence
|
||||
- [Filter mode] - select the filter mode/sequence
|
||||
- [Resonance] - filter resonance sequence
|
||||
- [Special] - ring and oscillator sync selector
|
||||
|
|
|
@ -8,7 +8,7 @@ FM editor is divided into 7 tabs:
|
|||
- [Macros (OP2)] - for macros controlling FM paramets of operator 2
|
||||
- [Macros (OP3)] - for macros controlling FM paramets of operator 3
|
||||
- [Macros (OP4)] - for macros controlling FM paramets of operator 4
|
||||
- [Macros] - for miscellanous macros controlling volume, argeggio and YM2151 noise generator.
|
||||
- [Macros] - for miscellaneous macros controlling volume, argeggio and YM2151 noise generator.
|
||||
|
||||
## FM
|
||||
|
||||
|
@ -21,7 +21,7 @@ FM synthesizers Furnace supports are for-operator, meaning it takes four oscilla
|
|||
- [Sustain Level(SL)] - Determines the point at which the sound ceases to decay and changes to a sound having a constant level. The sustain level is expressed as a fraction of the maximum level. (0-15 range)
|
||||
- [Total Level (TL)] - Represents the envelope’s highest amplitude, with 0 being the largest and 127 (decimal) the smallest. A change of one unit is about 0.75 dB.
|
||||
- [Envelope Scale (KSR)] - A parameter that determines the degree to which the envelope execution speed increases according to the pitch. (0-3 range)
|
||||
- [Frequency Multiplier (MULT)] - Determines the operator frequncy in relation to the pitch. (0-15 range)
|
||||
- [Frequency Multiplier (MULT)] - Determines the operator frequency in relation to the pitch. (0-15 range)
|
||||
- [Fine Detune (DT)] - Shifts the pitch a little (0-7 range)
|
||||
- [Coarse Detune (DT2)] - Shifts the pitch by tens of cents (0-3 range) WARNING: this parameter affects only YM2151 sound source!!!
|
||||
- [Hardware Envelope Generator (SSG-EG)] - Executes the built-in envelope, inherited from AY-3-8910 PSG. Speed of execution is controlled via Decay Rate. WARNING: this parameter affects only YM2610/YM2612 sound source!!!
|
||||
|
@ -33,14 +33,14 @@ FM synthesizers Furnace supports are for-operator, meaning it takes four oscilla
|
|||
|
||||
## Macros
|
||||
|
||||
Macros define the squence of values passed to the given parameter. Via macro, aside previously mentioned parameters, the following can be controlled:
|
||||
Macros define the sequence of values passed to the given parameter. Via macro, aside previously mentioned parameters, the following can be controlled:
|
||||
|
||||
- LFO Frequency
|
||||
- LFO waveform selection WARNING: this parameter affects only YM2151 sound source!!!
|
||||
- Amplitude Modulation Depth WARNING: this parameter affects only YM2151 sound source!!!
|
||||
- Frequency Modulation Depth WARNING: this parameter affects only YM2151 sound source!!!
|
||||
- Arpeggio Macro: pitch change sequence in semitones. Two modes are available:
|
||||
Absolute (defult) - Executes the pitch with absolute change based on the pitch of the actual note.
|
||||
Absolute (default) - Executes the pitch with absolute change based on the pitch of the actual note.
|
||||
Fixed - Executes at the pitch specified in the sequence regardless of the note pitch.
|
||||
- Noise Frequency: specifies the noise frequency in noise mode of YM2151's Channel 8 Operator 4 special mode.
|
||||
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
# wavetable editor
|
||||
|
||||
Wavetable synthesizers, in context of Furnace, are sound sources that operate on extremely short n-bit PCM streams. By extremely short, no more than 256 bytes. This amount of space is nowhere near enough to store an actual sampled sound, it allows certain amount of freedom to define a waveform shape. As of Furnace 0.5.8, wavetable editor affects PC Engine, WonderSwan and channel 3 of Game Boy.
|
||||
Wavetable synthesizers, in context of Furnace, are sound sources that operate on extremely short n-bit PCM streams. By extremely short, no more than 256 bytes. This amount of space is nowhere near enough to store an actual sampled sound, it allows certain amount of freedom to define a waveform shape. As of Furnace 0.6pre4, wavetable editor affects PC Engine, WonderSwan, Namco WSGs, Virtual Boy, Game.com, SCC, FDS, Seta X1-010, Konami Bubble System WSG, SNES, Amiga and channel 3 of Game Boy.
|
||||
|
||||
Furnace's wavetable editor is rather simple, you can draw the waveform using mouse or by pasting an MML bit stream in the input field. Maximum wave width (length) is 256 bytes, and maximum wave height (depth) is 256. NOTE: Game Boy, PCE, WonderSwan and Bubble System can handle max 32 byte waveforms, X1-010 can handle max 128 byte waveforms as of now, with 16-level height for GB, X1-010 Envelope, WS, Bubble System and N163, and 32-level height for PCE. If a larger wave is defined for these chips, it will be squashed to fit within the constraints of the chips.
|
||||
Furnace's wavetable editor is rather simple, you can draw the waveform using mouse or by pasting an MML bit stream in the input field. Maximum wave width (length) is 256 bytes, and maximum wave height (depth) is 256. NOTE: Game Boy, PCE, WonderSwan, Namco WSG, N163, Game.com, Virtual Boy and Bubble System can handle max 32 byte waveforms, X1-010 can handle max 128 byte waveforms as of now, with 16-level height for GB, X1-010 Envelope, WS, Bubble System, SNES, Namco WSG and N163, 32-level height for PCE and 64-level height for Virtual Boy. If a larger wave is defined for these chips, it will be squashed to fit within the constraints of the chips.
|
||||
|
||||
Furnace's wavetable editor features multiple ways of creating desired waveform shape:
|
||||
|
||||
- Shape tab allows you to select a few predefined basic shapes and indirectly edit it via "Duty", "Exponent" and "XOR Point" sliders TODO: what the last two are doing? What is amplitude/phase for?)
|
||||
- FM is for creating the waveform with frequency modulation synthesis principles: One can set carrier/modulation levels, frquency multiplier, connection between operators and FM waveforms of these operators.
|
||||
- WaveTools allows user to fine-tune the waveform: scale said waveform in both X and Y axes, smoothen, amplify, normalize, convert to signed/unisgned, invert or even randomize the wavetable.
|
||||
|
||||
## wavetable synthesizer
|
||||
|
||||
|
|
|
@ -64,4 +64,4 @@ In there, you can modify certain data pertaining to your sample, such as the:
|
|||
- what frequencies to filter, along with filter level/sweep and resonance options (much like the C64)
|
||||
- and many more.
|
||||
|
||||
The changes you make will be applied as soon as you've committed them to your sample, but they can be undoed and redoed, just like text.
|
||||
The changes you make will be applied as soon as you've committed them to your sample, but they can be undone and redoed, just like text.
|
||||
|
|
|
@ -38,11 +38,11 @@ this is a list of sound chips that Furnace supports, including effects.
|
|||
- [VERA](vera.md)
|
||||
- [WonderSwan](wonderswan.md)
|
||||
- [Virtual Boy](virtual-boy.md)
|
||||
- [Yamaha OPLL](opll.md)
|
||||
- [Yamaha YM2413 (OPLL)](opll.md)
|
||||
- [Yamaha OPL](opl.md)
|
||||
- [Yamaha YM2151](ym2151.md)
|
||||
- [Yamaha YM2203](ym2203.md)
|
||||
- [Yamaha YM2413](opz.md)
|
||||
- [Yamaha YM2414 (OPZ)](opz.md)
|
||||
- [Yamaha YM2608](ym2608.md)
|
||||
- [Neo Geo/YM2610](ym2610.md)
|
||||
- [Taito Arcade/YM2610B](ym2610b.md)
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# Ensoniq ES5506 (OTTO)
|
||||
|
||||
Sample-based synthesis chip used in a bunch of Taito arcade machines and PC sound cards like Soundscape Elite. A variant of it was the heart of the well-known Gravis Ultrasound.
|
||||
|
||||
it supports a whooping 32 channels of 16-bit PCM and:
|
||||
|
||||
- Real time digital filters
|
||||
- Frequency interpolation
|
||||
- Loop start and stop positions for each voice (bidirectional and reverse looping)
|
||||
- Internal volume multiplication and stereo panning
|
||||
- Hardware support for envelopes
|
||||
|
||||
# effects
|
||||
|
||||
- `10xx`: set waveform.
|
||||
- `11xx`: set filter mode (0-3)
|
||||
- `120x`: set pause (bit 0). Pauses the sample until the bit is unset, where it will then resume where it left off.
|
||||
- `14xx`: set filter coefficient K1 low byte.
|
||||
- `15xx`: set filter coefficient K1 high byte.
|
||||
- `16xx`: set filter coefficient K2 low byte.
|
||||
- `17xx`: set filter coefficient K2 high byte.
|
||||
- `18xx`: set filter coefficient K1 slide up.
|
||||
- `19xx`: set filter coefficient K1 slide down.
|
||||
- `1Axx`: set filter coefficient K2 slide up.
|
||||
- `1Bxx`: set filter coefficient K2 slide down.
|
||||
- `20xx`: set envelope count.
|
||||
- `22xx`: set envelope left volume ramp.
|
||||
- `23xx`: set envelope right volume ramp.
|
||||
- `24xx`: set envelope filter coefficient K1 ramp.
|
||||
- `25xx`: set envelope filter coefficient K1 ramp (slower).
|
||||
- `26xx`: set envelope filter coefficient K2 ramp.
|
||||
- `27xx`: set envelope filter coefficient K2 ramp (slower).
|
||||
- `3xxx`: set coarse filter coefficient K1.
|
||||
- `4xxx`: set coarse filter coefficient K2.
|
||||
- `81xx`: set panning (left channel).
|
||||
- `82xx`: set panning (right channel).
|
||||
- `88xx`: set panning (rear channels).
|
||||
- `89xx`: set panning (rear left channel).
|
||||
- `8Axx`: set panning (rear right channel).
|
||||
- `9xxx`: set sample offset (x256).
|
||||
- `DFxx`: set sample playback direction.
|
|
@ -16,7 +16,7 @@ this console is powered by two sound chips: the [Yamaha YM2612](ym2612.md) and [
|
|||
- `15xx`: set operator 4 level.
|
||||
- `16xy`: set multiplier of operator.
|
||||
- `x` is the operator (1-4).
|
||||
- `y` is the mutliplier.
|
||||
- `y` is the multiplier.
|
||||
- `17xx`: enable PCM channel.
|
||||
- this only works on channel 6.
|
||||
- **this effect is there for compatibility reasons** - it is otherwise recommended to use Sample type instruments (which automatically enable PCM mode when used).
|
||||
|
|
|
@ -42,6 +42,33 @@ also known as Famicom. it is a five-channel sound generator: first two channels
|
|||
- `00`: PCM (software).
|
||||
- `01`: DPCM (hardware).
|
||||
- when in DPCM mode, samples will sound muffled (due to its nature), availables pitches are limited and loop point is ignored.
|
||||
- `19xx`: set triangle linear counter.
|
||||
- `00` to `7F` set the counter.
|
||||
- `80` and higher halt it.
|
||||
- `20xx`: set DPCM frequency.
|
||||
- only works in DPCM mode.
|
||||
- see table below for possible values.
|
||||
|
||||
# DPCM frequency table
|
||||
|
||||
val | NTSC | PAL
|
||||
----|-----------|-----------
|
||||
00 | 4181.7Hz | 4177.4Hz
|
||||
01 | 4709.9Hz | 4696.6Hz
|
||||
02 | 5264.0Hz | 5261.4Hz
|
||||
03 | 5593.0Hz | 5579.2Hz
|
||||
04 | 6257.9Hz | 6023.9Hz
|
||||
05 | 7046.3Hz | 7044.9Hz
|
||||
06 | 7919.3Hz | 7917.2Hz
|
||||
07 | 8363.4Hz | 8397.0Hz
|
||||
08 | 9419.9Hz | 9446.6Hz
|
||||
09 | 11186.1Hz | 11233.8Hz
|
||||
0A | 12604.0Hz | 12595.5Hz
|
||||
0B | 13982.6Hz | 14089.9Hz
|
||||
0C | 16884.6Hz | 16965.4Hz
|
||||
0D | 21306.8Hz | 21315.5Hz
|
||||
0E | 24858.0Hz | 25191.0Hz
|
||||
0F | 33143.9Hz | 33252.1Hz
|
||||
|
||||
# length counter table
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ afterwards everyone moved to Windows and software mixed PCM streaming...
|
|||
- only in 4-op mode (OPL3).
|
||||
- `16xy`: set multiplier of operator.
|
||||
- `x` is the operator (1-4; last 2 operators only in 4-op mode).
|
||||
- `y` is the mutliplier.
|
||||
- `y` is the multiplier.
|
||||
- 17xx: set vibrato depth. the following values are accepted:
|
||||
- 0: normal
|
||||
- 1: double
|
||||
|
|
|
@ -30,7 +30,7 @@ the YM2413 is equipped with the following features:
|
|||
- `13xx`: set operator 2 level.
|
||||
- `16xy`: set multiplier of operator.
|
||||
- `x` is the operator (1 or 2).
|
||||
- `y` is the mutliplier.
|
||||
- `y` is the multiplier.
|
||||
- `18xx`: toggle drums mode.
|
||||
- 0 disables it and 1 enables it.
|
||||
- only in drums chip.
|
||||
|
|
|
@ -33,7 +33,7 @@ no plans have been made for TX81Z MIDI passthrough, because:
|
|||
- `15xx`: set operator 4 level.
|
||||
- `16xy`: set multiplier of operator.
|
||||
- `x` is the operator (1-4).
|
||||
- `y` is the mutliplier.
|
||||
- `y` is the multiplier.
|
||||
- `17xx`: set LFO speed.
|
||||
- `18xx`: set LFO waveform. `xx` may be one of the following:
|
||||
- `00`: saw
|
||||
|
|
|
@ -8,7 +8,7 @@ because the chip lacks sample interpolation, it is recommended that you try to p
|
|||
|
||||
the QSound chip also has a small echo buffer, somewhat similar to the SNES, although with a very basic (and non-adjustable) filter. it is however possible to adjust the feedback and length of the echo buffer (the initial values can be set in the "configure chip" option in the file menu or the chip manager).
|
||||
|
||||
there are also 3 ADPCM channels, however they cannot be used in Furnace yet. they have been reserved in case this feature is added later. ADPCM samples are limited to 8012 Hz.
|
||||
there are also 3 ADPCM channels. ADPCM samples are fixed to 8012 Hz.
|
||||
|
||||
# effects
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
The SM8521 is the CPU and sound chip of the Game.com, a handheld console released in 1997 as a competitor to the infamous Nintendo Virtual Boy.
|
||||
|
||||
Ultimately, most of the games for the Game.com ended up being failiures in the eyes of reviewers, thus giving the Game.com a pretty bad reputation. This was one of the reasons that the Game.com only ended up selling at least 300,000 units. For these reasons and more, the Game.com ended up being discontinued in 2000.
|
||||
Ultimately, most of the games for the Game.com ended up being failures in the eyes of reviewers, thus giving the Game.com a pretty bad reputation. This was one of the reasons that the Game.com only ended up selling at least 300,000 units. For these reasons and more, the Game.com ended up being discontinued in 2000.
|
||||
|
||||
However, for its time, it was a pretty competitively priced system. The Gameboy Color was to be released in a year for $79.95, while the Game.com was released for $69.99, and its later model, the Pocket Pro, was released in mid-1999 for $29.99 due to the Game.com's apparent significant decrease in value.
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
a relatively simple sound chip made by Texas Instruments. a derivative of it is used in Sega's Master System, the predecessor to Genesis.
|
||||
|
||||
the original iteration of the SN76489 used in the TI-99/4A computers was clocked at 447 KHz, being able to play as low as 13.670 Hz (A -1). consequentially, pitch accuracy for higher notes is compromised.
|
||||
the original iteration of the SN76489 used in the TI-99/4A computers was clocked at 447 KHz, being able to play as low as 13.670 Hz (A -1). consequently, pitch accuracy for higher notes is compromised.
|
||||
|
||||
on the other hand, the chip was clocked at a much higher speed on Master System and Genesis, which makes it rather poor in the bass range.
|
||||
|
||||
|
|
|
@ -54,6 +54,10 @@ Furnace also allows the SNES to use wavetables (and the wavetable synthesizer) i
|
|||
- 80 to FF for -128 to -1.
|
||||
- setting this to -128 is not recommended as it may cause echo output to overflow and therefore click.
|
||||
- `1Dxx`: set noise generator frequency (00 to 1F).
|
||||
- `1Exx`: set left dry/global volume.
|
||||
- this does not affect echo.
|
||||
- `1Fxx`: set right dry/global volume.
|
||||
- this does not affect echo.
|
||||
- `20xx`: set attack (0 to F).
|
||||
- only in ADSR envelope mode.
|
||||
- `21xx`: set decay (0 to 7).
|
||||
|
|
|
@ -16,7 +16,7 @@ in most arcade boards the chip was used in combination with a PCM chip, like [Se
|
|||
- `15xx`: set operator 4 level.
|
||||
- `16xy`: set multiplier of operator.
|
||||
- `x` is the operator (1-4).
|
||||
- `y` is the mutliplier.
|
||||
- `y` is the multiplier.
|
||||
- `17xx`: set LFO speed.
|
||||
- `18xx`: set LFO waveform. `xx` may be one of the following:
|
||||
- `00`: saw
|
||||
|
|
|
@ -18,7 +18,7 @@ several variants of this chip were released as well, with more features.
|
|||
- `15xx`: set operator 4 level.
|
||||
- `16xy`: set multiplier of operator.
|
||||
- `x` is the operator (1-4).
|
||||
- `y` is the mutliplier.
|
||||
- `y` is the multiplier.
|
||||
- `18xx`: toggle extended channel 3 mode.
|
||||
- 0 disables it and 1 enables it.
|
||||
- only in extended channel 3 chip.
|
||||
|
|
|
@ -18,7 +18,7 @@ the YM2610 (OPNB) and YM2610B chips are very similar to this one, but the built-
|
|||
- `15xx`: set operator 4 level.
|
||||
- `16xy`: set multiplier of operator.
|
||||
- `x` is the operator (1-4).
|
||||
- `y` is the mutliplier.
|
||||
- `y` is the multiplier.
|
||||
- `18xx`: toggle extended channel 3 mode.
|
||||
- 0 disables it and 1 enables it.
|
||||
- only in extended channel 3 chip.
|
||||
|
|
|
@ -16,7 +16,7 @@ its soundchip is a 4-in-1: 4ch 4-op FM, YM2149 (AY-3-8910 clone) and 2 different
|
|||
- `15xx`: set operator 4 level.
|
||||
- `16xy`: set multiplier of operator.
|
||||
- `x` is the operator (1-4).
|
||||
- `y` is the mutliplier.
|
||||
- `y` is the multiplier.
|
||||
- `18xx`: toggle extended channel 2 mode.
|
||||
- 0 disables it and 1 enables it.
|
||||
- only in extended channel 2 chip.
|
||||
|
|
|
@ -15,7 +15,7 @@ it is backward compatible with the original chip.
|
|||
- `15xx`: set operator 4 level.
|
||||
- `16xy`: set multiplier of operator.
|
||||
- `x` is the operator (1-4).
|
||||
- `y` is the mutliplier.
|
||||
- `y` is the multiplier.
|
||||
- `18xx`: toggle extended channel 3 mode.
|
||||
- 0 disables it and 1 enables it.
|
||||
- only in extended channel 3 chip.
|
||||
|
|
|
@ -14,7 +14,7 @@ one of two chips that powered the Sega Genesis. It is a six-channel, four-operat
|
|||
- `15xx`: set operator 4 level.
|
||||
- `16xy`: set multiplier of operator.
|
||||
- `x` is the operator (1-4).
|
||||
- `y` is the mutliplier.
|
||||
- `y` is the multiplier.
|
||||
- `17xx`: enable PCM channel.
|
||||
- this only works on channel 6.
|
||||
- `18xx`: toggle extended channel 3 mode.
|
||||
|
|
|
@ -21,6 +21,7 @@ writers:
|
|||
- cam900
|
||||
- host12prog
|
||||
- WindowxDeveloper
|
||||
- polluks
|
||||
|
||||
other:
|
||||
|
||||
|
|
|
@ -32,6 +32,12 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
|
|||
|
||||
the format versions are:
|
||||
|
||||
- 158: Furnace 0.6pre5
|
||||
- 157: Furnace dev157
|
||||
- 156: Furnace dev156
|
||||
- 155: Furnace dev155
|
||||
- 154: Furnace dev154
|
||||
- 153: Furnace dev153
|
||||
- 152: Furnace dev152
|
||||
- 151: Furnace dev151
|
||||
- 150: Furnace dev150
|
||||
|
@ -422,7 +428,8 @@ size | description
|
|||
1 | automatic patchbay (>=136)
|
||||
--- | **a couple more compat flags** (>=138)
|
||||
1 | broken portamento during legato
|
||||
7 | reserved
|
||||
1 | broken macro during note off in some FM chips (>=155)
|
||||
6 | reserved
|
||||
--- | **speed pattern of first song** (>=139)
|
||||
1 | length of speed pattern (fail if this is lower than 0 or higher than 16)
|
||||
16 | speed pattern (this overrides speed 1 and speed 2 settings)
|
||||
|
@ -431,12 +438,16 @@ size | description
|
|||
??? | groove entries. the format is:
|
||||
| - 1 byte: length of groove
|
||||
| - 16 bytes: groove pattern
|
||||
--- | **pointers to asset directories** (>=156)
|
||||
4 | instrument directories
|
||||
4 | wavetable directories
|
||||
4 | sample directories
|
||||
```
|
||||
|
||||
# patchbay
|
||||
|
||||
Furnace dev135 adds a "patchbay" which allows for arbitrary connection of chip outputs to system outputs.
|
||||
it eventually will allow connecting outputs to effects and so on.
|
||||
it also allows connecting outputs to effects and so on.
|
||||
|
||||
a connection is represented as an unsigned int in the following format:
|
||||
|
||||
|
@ -522,6 +533,22 @@ clock=4000000
|
|||
stereo=true
|
||||
```
|
||||
|
||||
# asset directories (>=156)
|
||||
|
||||
also known as "folder" in the user interface.
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
4 | "ADIR" block ID
|
||||
4 | size of this block
|
||||
4 | number of directories
|
||||
--- | **asset directory** (×numberOfDirs)
|
||||
STR | name (if empty, this is the uncategorized directory)
|
||||
2 | number of assets
|
||||
1?? | assets in this directory
|
||||
```
|
||||
|
||||
# instrument (>=127)
|
||||
|
||||
Furnace dev127 and higher use the new instrument format.
|
||||
|
@ -1253,7 +1280,58 @@ size | description
|
|||
| - version>=58 size is length
|
||||
```
|
||||
|
||||
# pattern
|
||||
# pattern (>=157)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
4 | "PATN" block ID
|
||||
4 | size of this block
|
||||
1 | subsong
|
||||
1 | channel
|
||||
2 | pattern index
|
||||
STR | pattern name (>=51)
|
||||
??? | pattern data
|
||||
| - read a byte per row.
|
||||
| - if it is 0xff, end of pattern.
|
||||
| - if bit 7 is set, then read bit 0-6 as "skip N+2 rows".
|
||||
| - if bit 7 is clear, then:
|
||||
| - bit 0: note present
|
||||
| - bit 1: ins present
|
||||
| - bit 2: volume present
|
||||
| - bit 3: effect 0 present
|
||||
| - bit 4: effect value 0 present
|
||||
| - bit 5: other effects (0-3) present
|
||||
| - bit 6: other effects (4-7) present
|
||||
| - if bit 5 is set, read another byte:
|
||||
| - bit 0: effect 0 present
|
||||
| - bit 1: effect value 0 present
|
||||
| - bit 2: effect 1 present
|
||||
| - bit 3: effect value 1 present
|
||||
| - bit 4: effect 2 present
|
||||
| - bit 5: effect value 2 present
|
||||
| - bit 6: effect 3 present
|
||||
| - bit 7: effect value 3 present
|
||||
| - if bit 6 is set, read another byte:
|
||||
| - bit 0: effect 4 present
|
||||
| - bit 1: effect value 4 present
|
||||
| - bit 2: effect 5 present
|
||||
| - bit 3: effect value 5 present
|
||||
| - bit 4: effect 6 present
|
||||
| - bit 5: effect value 6 present
|
||||
| - bit 6: effect 7 present
|
||||
| - bit 7: effect value 7 present
|
||||
| - then read note, ins, volume, effects and effect values depending on what is present.
|
||||
| - for note:
|
||||
| - 0 is C-(-5)
|
||||
| - 179 is B-9
|
||||
| - 180 is note off
|
||||
| - 181 is note release
|
||||
| - 182 is macro release
|
||||
```
|
||||
|
||||
|
||||
# old pattern (<157)
|
||||
|
||||
```
|
||||
size | description
|
||||
|
@ -1283,8 +1361,8 @@ size | description
|
|||
| - 12: C (of next octave)
|
||||
| - this is actually a leftover of the .dmf format.
|
||||
| - 100: note off
|
||||
| - 100: note release
|
||||
| - 100: macro release
|
||||
| - 101: note release
|
||||
| - 102: macro release
|
||||
| - octave
|
||||
| - this is an signed char stored in a short.
|
||||
| - therefore octave value 255 is actually octave -1.
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 395 KiB |
|
@ -15,17 +15,17 @@
|
|||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string>0.6pre4</string>
|
||||
<string>0.6pre5</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Furnace</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.6pre4</string>
|
||||
<string>0.6pre5</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.6pre4</string>
|
||||
<string>0.6pre5</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string></string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
it also offers DefleMask compatibility, allowing you to import your songs and even export them back for interoperability.
|
||||
</p>
|
||||
</description>
|
||||
|
||||
<content_rating type="oars-1.0">
|
||||
<content_attribute id="language-profanity">intense</content_attribute>
|
||||
<content_attribute id="language-humor">mild</content_attribute>
|
||||
</content_rating>
|
||||
|
||||
<launchable type="desktop-id">furnace.desktop</launchable>
|
||||
<screenshots>
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
#!/bin/bash
|
||||
# make linux release
|
||||
# run on an Ubuntu 16.04 machine or VM for best results.
|
||||
|
||||
if [ ! -e /tmp/furnace ]; then
|
||||
ln -s "$PWD" /tmp/furnace || exit 1
|
||||
fi
|
||||
|
||||
cd /tmp/furnace
|
||||
|
||||
if [ ! -e linuxbuild ]; then
|
||||
mkdir linuxbuild || exit 1
|
||||
fi
|
||||
|
||||
cd linuxbuild
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Werror" -DWITH_DEMOS=OFF -DWITH_INSTRUMENTS=OFF -DWITH_WAVETABLES=OFF .. || exit 1
|
||||
make -j4 || exit 1
|
||||
|
||||
cd ..
|
||||
|
||||
mkdir -p release/linux/furnace.AppDir || exit 1
|
||||
cd linuxbuild
|
||||
|
||||
make DESTDIR=/tmp/furnace/release/linux/furnace.AppDir install || exit 1
|
||||
|
||||
cd ../release/linux/furnace.AppDir
|
||||
|
||||
cp -v ../../../res/logo.png furnace.png || exit 1
|
||||
ln -s furnace.png .DirIcon || exit 1
|
||||
cp -v ../../../res/furnace.desktop . || exit 1
|
||||
#mkdir -p usr/share/metainfo || exit 1
|
||||
cp -v ../../../res/furnace.appdata.xml usr/share/metainfo/org.tildearrow.furnace.metainfo.xml || exit 1
|
||||
rm usr/share/metainfo/furnace.appdata.xml || exit 1
|
||||
cp -v ../../../res/AppRun . || exit 1
|
||||
|
||||
#cp /usr/lib/libm.so.6 usr/lib/ || exit 1
|
||||
#cp /usr/lib/libstdc++.so.6 usr/lib/ || exit 1
|
||||
#cp /usr/lib/libc.so.6 usr/lib/ || exit 1
|
||||
#cp /usr/lib/libgcc_s.so.1 usr/lib/ || exit 1
|
||||
|
||||
cd usr/bin
|
||||
strip -s furnace
|
||||
|
||||
cd ../../..
|
||||
|
||||
[ -e appimagetool-x86_64.AppImage ] || { wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" && chmod 755 appimagetool-x86_64.AppImage; }
|
||||
./appimagetool-x86_64.AppImage --appimage-extract
|
||||
ARCH=$(uname -m) ./squashfs-root/AppRun furnace.AppDir
|
||||
|
||||
#zip -r furnace.zip LICENSE.txt Furnace*.dmg README.txt papers
|
|
@ -19,32 +19,42 @@ make -j4 || exit 1
|
|||
|
||||
cd ..
|
||||
|
||||
mkdir -p release/linux/furnace.AppDir || exit 1
|
||||
mkdir -p release/linux/furnace || exit 1
|
||||
cd linuxbuild
|
||||
|
||||
make DESTDIR=/tmp/furnace/release/linux/furnace.AppDir install || exit 1
|
||||
make DESTDIR=/tmp/furnace/release/linux/furnace install || exit 1
|
||||
|
||||
cd ../release/linux/furnace.AppDir
|
||||
cd ../release/linux/furnace
|
||||
|
||||
cp -v ../../../res/logo.png furnace.png || exit 1
|
||||
ln -s furnace.png .DirIcon || exit 1
|
||||
cp -v ../../../res/furnace.desktop . || exit 1
|
||||
#mkdir -p usr/share/metainfo || exit 1
|
||||
cp -v ../../../res/furnace.appdata.xml usr/share/metainfo/org.tildearrow.furnace.metainfo.xml || exit 1
|
||||
cp -v ../../../res/AppRun . || exit 1
|
||||
cp -v ../../../res/logo.png .DirIcon || exit 1
|
||||
#cp -v ../../../res/furnace.desktop . || exit 1
|
||||
|
||||
#cp /usr/lib/libm.so.6 usr/lib/ || exit 1
|
||||
#cp /usr/lib/libstdc++.so.6 usr/lib/ || exit 1
|
||||
#cp /usr/lib/libc.so.6 usr/lib/ || exit 1
|
||||
#cp /usr/lib/libgcc_s.so.1 usr/lib/ || exit 1
|
||||
cd usr
|
||||
|
||||
mv bin/furnace .. || exit 1
|
||||
rmdir bin || exit 1
|
||||
|
||||
rm -r share/applications
|
||||
rm -r share/doc
|
||||
mv share/icons ..
|
||||
rm -r share/licenses
|
||||
rm -r share/metainfo
|
||||
|
||||
mv share/furnace/demos ..
|
||||
mv share/furnace/instruments ..
|
||||
mv share/furnace/wavetables ..
|
||||
rmdir share/furnace || exit 1
|
||||
rmdir share || exit 1
|
||||
|
||||
cd ..
|
||||
|
||||
cp ../../../LICENSE . || exit 1
|
||||
cp ../../../README.md . || exit 1
|
||||
cp -r ../../../papers papers || exit 1
|
||||
rmdir usr || exit 1
|
||||
|
||||
cd usr/bin
|
||||
strip -s furnace
|
||||
|
||||
cd ../../..
|
||||
cd ..
|
||||
|
||||
[ -e appimagetool-x86_64.AppImage ] || { wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" && chmod 755 appimagetool-x86_64.AppImage; }
|
||||
./appimagetool-x86_64.AppImage --appimage-extract
|
||||
ARCH=$(uname -m) ./squashfs-root/AppRun furnace.AppDir
|
||||
|
||||
#zip -r furnace.zip LICENSE.txt Furnace*.dmg README.txt papers
|
||||
tar -zcv -f furnace.tar.gz furnace
|
||||
|
|
|
@ -17,7 +17,6 @@ cd win32build
|
|||
# TODO: potential Arch-ism?
|
||||
i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Werror" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON .. || exit 1
|
||||
make -j8 || exit 1
|
||||
#i686-w64-mingw32-strip -s furnace.exe || exit 1
|
||||
|
||||
cd ..
|
||||
|
||||
|
@ -32,6 +31,8 @@ cp -r ../../demos demos || exit 1
|
|||
cp -r ../../instruments instruments || exit 1
|
||||
cp -r ../../wavetables wavetables || exit 1
|
||||
|
||||
i686-w64-mingw32-strip -s furnace.exe || exit 1
|
||||
|
||||
zip -r furnace.zip LICENSE.txt furnace.exe README.txt papers demos instruments wavetables
|
||||
|
||||
furName=$(git describe --tags | sed "s/v0/0/")
|
||||
|
|
|
@ -17,7 +17,6 @@ cd winbuild
|
|||
# TODO: potential Arch-ism?
|
||||
x86_64-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Wno-deprecated-declarations -Werror" .. || exit 1
|
||||
make -j8 || exit 1
|
||||
#x86_64-w64-mingw32-strip -s furnace.exe || exit 1
|
||||
|
||||
cd ..
|
||||
|
||||
|
@ -32,6 +31,8 @@ cp -r ../../demos demos || exit 1
|
|||
cp -r ../../instruments instruments || exit 1
|
||||
cp -r ../../wavetables wavetables || exit 1
|
||||
|
||||
x86_64-w64-mingw32-strip -s furnace.exe || exit 1
|
||||
|
||||
zip -r furnace.zip LICENSE.txt furnace.exe README.txt papers demos instruments wavetables
|
||||
|
||||
furName=$(git describe --tags | sed "s/v0/0/")
|
||||
|
|
|
@ -148,9 +148,13 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundanc
|
|||
}
|
||||
|
||||
for (size_t j=0; j<readBufLen; j++) {
|
||||
if (readBuf[j]==0) {
|
||||
viable=false;
|
||||
logW("a zero?");
|
||||
break;
|
||||
}
|
||||
if (readBuf[j]!='\r' && readBuf[j]!='\n' && readBuf[j]!=' ') {
|
||||
viable=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,7 +182,7 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundanc
|
|||
logD("config does not exist");
|
||||
if (createOnFail) {
|
||||
logI("creating default config.");
|
||||
reportError(fmt::sprintf("Creating default config: %s",strerror(errno)));
|
||||
//reportError(fmt::sprintf("Creating default config: %s",strerror(errno)));
|
||||
return save(path,redundancy);
|
||||
} else {
|
||||
reportError(fmt::sprintf("COULD NOT LOAD CONFIG %s",strerror(errno)));
|
||||
|
@ -191,7 +195,7 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundanc
|
|||
logD("config does not exist");
|
||||
if (createOnFail) {
|
||||
logI("creating default config.");
|
||||
reportError(fmt::sprintf("Creating default config: %s",strerror(errno)));
|
||||
//reportError(fmt::sprintf("Creating default config: %s",strerror(errno)));
|
||||
return save(path);
|
||||
} else {
|
||||
reportError(fmt::sprintf("COULD NOT LOAD CONFIG %s",strerror(errno)));
|
||||
|
|
|
@ -231,6 +231,11 @@ enum DivDispatchCmds {
|
|||
|
||||
DIV_CMD_HINT_ARP_TIME, // (value)
|
||||
|
||||
DIV_CMD_SNES_GLOBAL_VOL_LEFT,
|
||||
DIV_CMD_SNES_GLOBAL_VOL_RIGHT,
|
||||
|
||||
DIV_CMD_NES_LINEAR_LENGTH,
|
||||
|
||||
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
|
||||
|
||||
DIV_CMD_MAX
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 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 _EFFECT_H
|
||||
#define _EFFECT_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "../ta-utils.h"
|
||||
|
||||
class DivEngine;
|
||||
|
||||
class DivEffect {
|
||||
protected:
|
||||
DivEngine* parent;
|
||||
public:
|
||||
/**
|
||||
* fill a buffer with sound data.
|
||||
* @param in pointers to input buffers.
|
||||
* @param out pointers to output buffers.
|
||||
* @param len the amount of samples in input and output.
|
||||
*/
|
||||
virtual void acquire(float** in, float** out, size_t len);
|
||||
|
||||
/**
|
||||
* reset the state of this effect.
|
||||
*/
|
||||
virtual void reset();
|
||||
|
||||
/**
|
||||
* get the number of inputs this effect requests.
|
||||
* @return number of inputs. SHALL NOT be less than zero.
|
||||
*/
|
||||
virtual int getInputCount();
|
||||
|
||||
/**
|
||||
* get the number of outputs this effect provides.
|
||||
* @return number of outputs. SHALL NOT be less than one.
|
||||
*/
|
||||
virtual int getOutputCount();
|
||||
|
||||
/**
|
||||
* called when the sample rate changes.
|
||||
* @param rate the new sample rate.
|
||||
*/
|
||||
virtual void rateChanged(double rate);
|
||||
|
||||
/**
|
||||
* get the value of a parameter.
|
||||
* @param param parameter ID.
|
||||
* @return a String with the value.
|
||||
* @throws std::out_of_range if the parameter ID is out of range.
|
||||
*/
|
||||
virtual String getParam(size_t param);
|
||||
|
||||
/**
|
||||
* set the value of a parameter.
|
||||
* @param param parameter ID.
|
||||
* @param value the value.
|
||||
* @return whether the parameter was set successfully.
|
||||
*/
|
||||
virtual bool setParam(size_t param, String value);
|
||||
|
||||
/**
|
||||
* get a list of parameters.
|
||||
* @return a C string with a list of parameters, or NULL if there are none.
|
||||
* PARAMETER TYPES
|
||||
*
|
||||
* Parameter
|
||||
* id:type:name:description:[...]
|
||||
* type may be one of the following:
|
||||
* - s: string
|
||||
* - i: integer
|
||||
* - I: integer slider
|
||||
* - r: integer list (radio button)
|
||||
* - R: integer list (combo box)
|
||||
* - h: integer hex
|
||||
* - f: float
|
||||
* - F: float slider
|
||||
* - d: double
|
||||
* - D: double slider
|
||||
* - b: boolean (checkbox)
|
||||
* - t: boolean (toggle button)
|
||||
* - x: X/Y integer
|
||||
* - X: X/Y float
|
||||
* - c: color (RGB)
|
||||
* - C: color (RGBA)
|
||||
* - B: button
|
||||
*
|
||||
* SameLine
|
||||
* !s
|
||||
*
|
||||
* Separator
|
||||
* ---
|
||||
*
|
||||
* Indent/Unindent
|
||||
* > Indent
|
||||
* < Unindent
|
||||
*
|
||||
* TreeNode
|
||||
* >> Begin
|
||||
* << End
|
||||
*
|
||||
* Tabs
|
||||
* >TABBAR BeginTabBar
|
||||
* >TAB:name Begin
|
||||
* <TAB End
|
||||
* <TABBAR EndTabBar
|
||||
*
|
||||
* Text
|
||||
* TEXT:text (static text)
|
||||
* TEXTF:id (dynamic text)
|
||||
*
|
||||
* NOTES
|
||||
*
|
||||
* use a new line to separate parameters.
|
||||
* use `\:` if you need a colon.
|
||||
*/
|
||||
virtual const char* getParams();
|
||||
|
||||
/**
|
||||
* get the number of parameters.
|
||||
* @return count.
|
||||
*/
|
||||
virtual size_t getParamCount();
|
||||
|
||||
/**
|
||||
* get a dynamic text.
|
||||
* @param id the text ID.
|
||||
* @return a String with the text.
|
||||
* @throws std::out_of_range if the text ID is out of range.
|
||||
*/
|
||||
virtual String getDynamicText(size_t id);
|
||||
|
||||
/**
|
||||
* load effect data.
|
||||
* @param version effect data version. may be zero.
|
||||
* @param data effect data. may be NULL.
|
||||
* @param len effect data length. may be zero.
|
||||
* @return whether loading was successful.
|
||||
*/
|
||||
virtual bool load(unsigned short version, const unsigned char* data, size_t len);
|
||||
|
||||
/**
|
||||
* save effect data.
|
||||
* @param version effect data version.
|
||||
* @param len effect data length.
|
||||
* @return a pointer to effect data. this must be de-allocated manually.
|
||||
* may also return NULL if it can't save.
|
||||
*/
|
||||
virtual unsigned char* save(unsigned short* version, size_t* len);
|
||||
|
||||
/**
|
||||
* initialize this DivEffect.
|
||||
* @param parent the parent DivEngine.
|
||||
* @param version effect data version. may be zero.
|
||||
* @param data effect data. may be NULL.
|
||||
* @param len effect data length. may be zero.
|
||||
* @return whether initialization was successful.
|
||||
*/
|
||||
virtual bool init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len);
|
||||
|
||||
/**
|
||||
* quit the DivEffect.
|
||||
*/
|
||||
virtual void quit();
|
||||
|
||||
virtual ~DivEffect();
|
||||
};
|
||||
|
||||
// additional notes:
|
||||
// - we don't have a GUI API yet, but that will be added when I make the plugin bridge.
|
||||
|
||||
#endif
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 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 "../effect.h"
|
||||
#include "../../ta-log.h"
|
||||
#include <stdexcept>
|
||||
|
||||
void DivEffect::acquire(float** in, float** out, size_t len) {
|
||||
}
|
||||
|
||||
void DivEffect::reset() {
|
||||
}
|
||||
|
||||
int DivEffect::getInputCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DivEffect::getOutputCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DivEffect::rateChanged(double rate) {
|
||||
}
|
||||
|
||||
String DivEffect::getParam(size_t param) {
|
||||
throw std::out_of_range("param");
|
||||
|
||||
// unreachable
|
||||
return "";
|
||||
}
|
||||
|
||||
bool DivEffect::setParam(size_t param, String value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* DivEffect::getParams() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t DivEffect::getParamCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String DivEffect::getDynamicText(size_t id) {
|
||||
throw std::out_of_range("param");
|
||||
|
||||
// unreachable
|
||||
return "";
|
||||
}
|
||||
|
||||
bool DivEffect::load(unsigned short version, const unsigned char* data, size_t len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char* DivEffect::save(unsigned short* version, size_t* len) {
|
||||
*len=0;
|
||||
*version=0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool DivEffect::init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DivEffect::quit() {
|
||||
}
|
||||
|
||||
DivEffect::~DivEffect() {
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 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 "dummy.h"
|
||||
|
||||
void DivEffectDummy::acquire(float** in, float** out, size_t len) {
|
||||
memcpy(out[0],in[0],len*sizeof(float));
|
||||
}
|
||||
|
||||
void DivEffectDummy::reset() {
|
||||
}
|
||||
|
||||
int DivEffectDummy::getInputCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int DivEffectDummy::getOutputCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* DivEffectDummy::getParams() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t DivEffectDummy::getParamCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool DivEffectDummy::load(unsigned short version, const unsigned char* data, size_t len) {
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char* DivEffectDummy::save(unsigned short* version, size_t* len) {
|
||||
*len=0;
|
||||
*version=0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool DivEffectDummy::init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DivEffectDummy::quit() {
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 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 "../effect.h"
|
||||
|
||||
class DivEffectDummy: public DivEffect {
|
||||
public:
|
||||
void acquire(float** in, float** out, size_t len);
|
||||
void reset();
|
||||
int getInputCount();
|
||||
int getOutputCount();
|
||||
const char* getParams();
|
||||
size_t getParamCount();
|
||||
bool load(unsigned short version, const unsigned char* data, size_t len);
|
||||
unsigned char* save(unsigned short* version, size_t* len);
|
||||
bool init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len);
|
||||
void quit();
|
||||
};
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 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 "engine.h"
|
||||
#include "effect/dummy.h"
|
||||
|
||||
void DivEffectContainer::preAcquire(size_t count) {
|
||||
if (!count) return;
|
||||
|
||||
int inCount=effect->getInputCount();
|
||||
|
||||
if (inLen<count) {
|
||||
for (int i=0; i<inCount; i++) {
|
||||
if (in[i]!=NULL) {
|
||||
delete[] in[i];
|
||||
in[i]=new float[count];
|
||||
}
|
||||
}
|
||||
inLen=count;
|
||||
}
|
||||
|
||||
for (int i=0; i<inCount; i++) {
|
||||
if (in[i]==NULL) {
|
||||
in[i]=new float[count];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivEffectContainer::acquire(size_t count) {
|
||||
if (!count) return;
|
||||
|
||||
int outCount=effect->getOutputCount();
|
||||
|
||||
if (outLen<count) {
|
||||
for (int i=0; i<outCount; i++) {
|
||||
if (out[i]!=NULL) {
|
||||
delete[] out[i];
|
||||
out[i]=new float[count];
|
||||
}
|
||||
}
|
||||
outLen=count;
|
||||
}
|
||||
|
||||
for (int i=0; i<outCount; i++) {
|
||||
if (out[i]==NULL) {
|
||||
out[i]=new float[count];
|
||||
}
|
||||
}
|
||||
|
||||
effect->acquire(in,out,count);
|
||||
}
|
||||
|
||||
bool DivEffectContainer::init(DivEffectType effectType, DivEngine* eng, double rate, unsigned short version, const unsigned char* data, size_t len) {
|
||||
switch (effectType) {
|
||||
case DIV_EFFECT_DUMMY:
|
||||
default:
|
||||
effect=new DivEffectDummy;
|
||||
}
|
||||
return effect->init(eng,rate,version,data,len);
|
||||
}
|
||||
|
||||
void DivEffectContainer::quit() {
|
||||
effect->quit();
|
||||
delete effect;
|
||||
effect=NULL;
|
||||
|
||||
for (int i=0; i<DIV_MAX_OUTPUTS; i++) {
|
||||
if (in[i]) {
|
||||
delete[] in[i];
|
||||
in[i]=NULL;
|
||||
}
|
||||
if (out[i]) {
|
||||
delete[] out[i];
|
||||
out[i]=NULL;
|
||||
}
|
||||
}
|
||||
inLen=0;
|
||||
outLen=0;
|
||||
}
|
|
@ -55,6 +55,10 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
|||
return "03xx: Portamento";
|
||||
case 0x04:
|
||||
return "04xy: Vibrato (x: speed; y: depth)";
|
||||
case 0x05:
|
||||
return "05xy: Volume slide + vibrato (compatibility only!)";
|
||||
case 0x06:
|
||||
return "06xy: Volume slide + portamento (compatibility only!)";
|
||||
case 0x07:
|
||||
return "07xy: Tremolo (x: speed; y: depth)";
|
||||
case 0x08:
|
||||
|
@ -1568,6 +1572,35 @@ void DivEngine::changeSong(size_t songIndex) {
|
|||
prevRow=0;
|
||||
}
|
||||
|
||||
void DivEngine::moveAsset(std::vector<DivAssetDir>& dir, int before, int after) {
|
||||
if (before<0 || after<0) return;
|
||||
for (DivAssetDir& i: dir) {
|
||||
for (size_t j=0; j<i.entries.size(); j++) {
|
||||
// erase matching entry
|
||||
if (i.entries[j]==before) {
|
||||
i.entries[j]=after;
|
||||
} else if (i.entries[j]==after) {
|
||||
i.entries[j]=before;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivEngine::removeAsset(std::vector<DivAssetDir>& dir, int entry) {
|
||||
if (entry<0) return;
|
||||
for (DivAssetDir& i: dir) {
|
||||
for (size_t j=0; j<i.entries.size(); j++) {
|
||||
// erase matching entry
|
||||
if (i.entries[j]==entry) {
|
||||
i.entries.erase(i.entries.begin()+j);
|
||||
j--;
|
||||
} else if (i.entries[j]>entry) {
|
||||
i.entries[j]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivEngine::checkAssetDir(std::vector<DivAssetDir>& dir, size_t entries) {
|
||||
bool* inAssetDir=new bool[entries];
|
||||
memset(inAssetDir,0,entries*sizeof(bool));
|
||||
|
@ -1580,9 +1613,16 @@ void DivEngine::checkAssetDir(std::vector<DivAssetDir>& dir, size_t entries) {
|
|||
j--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// erase duplicate entry
|
||||
if (inAssetDir[i.entries[j]]) {
|
||||
i.entries.erase(i.entries.begin()+j);
|
||||
j--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// mark entry as present
|
||||
inAssetDir[j]=true;
|
||||
inAssetDir[i.entries[j]]=true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1595,15 +1635,14 @@ void DivEngine::checkAssetDir(std::vector<DivAssetDir>& dir, size_t entries) {
|
|||
}
|
||||
}
|
||||
|
||||
// create unsorted directory if it doesn't exist
|
||||
if (unsortedDir==NULL) {
|
||||
dir.push_back(DivAssetDir(""));
|
||||
unsortedDir=&(*dir.rbegin());
|
||||
}
|
||||
|
||||
// add missing items to unsorted directory
|
||||
for (size_t i=0; i<entries; i++) {
|
||||
if (!inAssetDir[i]) {
|
||||
// create unsorted directory if it doesn't exist
|
||||
if (unsortedDir==NULL) {
|
||||
dir.push_back(DivAssetDir(""));
|
||||
unsortedDir=&(*dir.rbegin());
|
||||
}
|
||||
unsortedDir->entries.push_back(i);
|
||||
}
|
||||
}
|
||||
|
@ -2030,11 +2069,17 @@ String DivEngine::getPlaybackDebugInfo() {
|
|||
"divider: %f\n"
|
||||
"cycles: %d\n"
|
||||
"clockDrift: %f\n"
|
||||
"midiClockCycles: %d\n"
|
||||
"midiClockDrift: %f\n"
|
||||
"midiTimeCycles: %d\n"
|
||||
"midiTimeDrift: %f\n"
|
||||
"changeOrd: %d\n"
|
||||
"changePos: %d\n"
|
||||
"totalSeconds: %d\n"
|
||||
"totalTicks: %d\n"
|
||||
"totalTicksR: %d\n"
|
||||
"curMidiClock: %d\n"
|
||||
"curMidiTime: %d\n"
|
||||
"totalCmds: %d\n"
|
||||
"lastCmds: %d\n"
|
||||
"cmdsPerSecond: %d\n"
|
||||
|
@ -2044,7 +2089,8 @@ String DivEngine::getPlaybackDebugInfo() {
|
|||
"totalProcessed: %d\n"
|
||||
"bufferPos: %d\n",
|
||||
curOrder,prevOrder,curRow,prevRow,ticks,subticks,totalLoops,lastLoopPos,nextSpeed,divider,cycles,clockDrift,
|
||||
changeOrd,changePos,totalSeconds,totalTicks,totalTicksR,totalCmds,lastCmds,cmdsPerSecond,globalPitch,
|
||||
midiClockCycles,midiClockDrift,midiTimeCycles,midiTimeDrift,changeOrd,changePos,totalSeconds,totalTicks,
|
||||
totalTicksR,curMidiClock,curMidiTime,totalCmds,lastCmds,cmdsPerSecond,globalPitch,
|
||||
(int)extValue,(int)tempoAccum,(int)totalProcessed,(int)bufferPos
|
||||
);
|
||||
}
|
||||
|
@ -2161,16 +2207,27 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
|
|||
prevOrder=0;
|
||||
prevRow=0;
|
||||
stepPlay=0;
|
||||
int prevDrift;
|
||||
if (curSubSong!=NULL) curSubSong->arpLen=1;
|
||||
int prevDrift, prevMidiClockDrift, prevMidiTimeDrift;
|
||||
prevDrift=clockDrift;
|
||||
prevMidiClockDrift=midiClockDrift;
|
||||
prevMidiTimeDrift=midiTimeDrift;
|
||||
clockDrift=0;
|
||||
cycles=0;
|
||||
midiClockCycles=0;
|
||||
midiClockDrift=0;
|
||||
midiTimeCycles=0;
|
||||
midiTimeDrift=0;
|
||||
if (!preserveDrift) {
|
||||
ticks=1;
|
||||
tempoAccum=0;
|
||||
totalTicks=0;
|
||||
totalSeconds=0;
|
||||
totalTicksR=0;
|
||||
curMidiClock=0;
|
||||
curMidiTime=0;
|
||||
curMidiTimeCode=0;
|
||||
curMidiTimePiece=0;
|
||||
totalLoops=0;
|
||||
lastLoopPos=-1;
|
||||
}
|
||||
|
@ -2187,6 +2244,10 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
|
|||
skipping=false;
|
||||
return;
|
||||
}
|
||||
if (!preserveDrift) {
|
||||
runMidiClock(cycles);
|
||||
runMidiTime(cycles);
|
||||
}
|
||||
}
|
||||
int oldOrder=curOrder;
|
||||
while (playing && (curRow<goalRow || ticks>1)) {
|
||||
|
@ -2194,6 +2255,10 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
|
|||
skipping=false;
|
||||
return;
|
||||
}
|
||||
if (!preserveDrift) {
|
||||
runMidiClock(cycles);
|
||||
runMidiTime(cycles);
|
||||
}
|
||||
if (oldOrder!=curOrder) break;
|
||||
if (ticks-((tempoAccum+curSubSong->virtualTempoN)/MAX(1,curSubSong->virtualTempoD))<1 && curRow>=goalRow) break;
|
||||
}
|
||||
|
@ -2207,9 +2272,22 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
|
|||
repeatPattern=oldRepeatPattern;
|
||||
if (preserveDrift) {
|
||||
clockDrift=prevDrift;
|
||||
midiClockDrift=prevMidiClockDrift;
|
||||
midiTimeDrift=prevMidiTimeDrift;
|
||||
} else {
|
||||
clockDrift=0;
|
||||
cycles=0;
|
||||
midiClockCycles=0;
|
||||
midiClockDrift=0;
|
||||
midiTimeCycles=0;
|
||||
midiTimeDrift=0;
|
||||
if (curMidiTime>0) {
|
||||
curMidiTime--;
|
||||
}
|
||||
if (curMidiClock>0) {
|
||||
curMidiClock--;
|
||||
}
|
||||
curMidiTimePiece=0;
|
||||
}
|
||||
if (!preserveDrift) {
|
||||
ticks=1;
|
||||
|
@ -2378,9 +2456,74 @@ void DivEngine::play() {
|
|||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
keyHit[i]=false;
|
||||
}
|
||||
curMidiTimePiece=0;
|
||||
if (output) if (!skipping && output->midiOut!=NULL) {
|
||||
int pos=totalTicksR/6;
|
||||
output->midiOut->send(TAMidiMessage(TA_MIDI_POSITION,(pos>>7)&0x7f,pos&0x7f));
|
||||
if (midiOutClock) {
|
||||
output->midiOut->send(TAMidiMessage(TA_MIDI_POSITION,(curMidiClock>>7)&0x7f,curMidiClock&0x7f));
|
||||
}
|
||||
if (midiOutTime) {
|
||||
TAMidiMessage msg;
|
||||
msg.type=TA_MIDI_SYSEX;
|
||||
msg.sysExData.reset(new unsigned char[10],std::default_delete<unsigned char[]>());
|
||||
msg.sysExLen=10;
|
||||
unsigned char* msgData=msg.sysExData.get();
|
||||
int actualTime=curMidiTime;
|
||||
int timeRate=midiOutTimeRate;
|
||||
int drop=0;
|
||||
if (timeRate<1 || timeRate>4) {
|
||||
if (curSubSong->hz>=47.98 && curSubSong->hz<=48.02) {
|
||||
timeRate=1;
|
||||
} else if (curSubSong->hz>=49.98 && curSubSong->hz<=50.02) {
|
||||
timeRate=2;
|
||||
} else if (curSubSong->hz>=59.9 && curSubSong->hz<=60.11) {
|
||||
timeRate=4;
|
||||
} else {
|
||||
timeRate=4;
|
||||
}
|
||||
}
|
||||
|
||||
switch (timeRate) {
|
||||
case 1: // 24
|
||||
msgData[5]=(actualTime/(60*60*24))%24;
|
||||
msgData[6]=(actualTime/(60*24))%60;
|
||||
msgData[7]=(actualTime/24)%60;
|
||||
msgData[8]=actualTime%24;
|
||||
break;
|
||||
case 2: // 25
|
||||
msgData[5]=(actualTime/(60*60*25))%24;
|
||||
msgData[6]=(actualTime/(60*25))%60;
|
||||
msgData[7]=(actualTime/25)%60;
|
||||
msgData[8]=actualTime%25;
|
||||
break;
|
||||
case 3: // 29.97 (NTSC drop)
|
||||
// drop
|
||||
drop=((actualTime/(30*60))-(actualTime/(30*600)))*2;
|
||||
actualTime+=drop;
|
||||
|
||||
msgData[5]=(actualTime/(60*60*30))%24;
|
||||
msgData[6]=(actualTime/(60*30))%60;
|
||||
msgData[7]=(actualTime/30)%60;
|
||||
msgData[8]=actualTime%30;
|
||||
break;
|
||||
case 4: // 30 (NTSC non-drop)
|
||||
default:
|
||||
msgData[5]=(actualTime/(60*60*30))%24;
|
||||
msgData[6]=(actualTime/(60*30))%60;
|
||||
msgData[7]=(actualTime/30)%60;
|
||||
msgData[8]=actualTime%30;
|
||||
break;
|
||||
}
|
||||
|
||||
msgData[5]|=(timeRate-1)<<5;
|
||||
|
||||
msgData[0]=0xf0;
|
||||
msgData[1]=0x7f;
|
||||
msgData[2]=0x7f;
|
||||
msgData[3]=0x01;
|
||||
msgData[4]=0x01;
|
||||
msgData[9]=0xf7;
|
||||
output->midiOut->send(msg);
|
||||
}
|
||||
output->midiOut->send(TAMidiMessage(TA_MIDI_MACHINE_PLAY,0,0));
|
||||
}
|
||||
BUSY_END;
|
||||
|
@ -2419,6 +2562,13 @@ void DivEngine::stepOne(int row) {
|
|||
void DivEngine::stop() {
|
||||
BUSY_BEGIN;
|
||||
freelance=false;
|
||||
if (!playing) {
|
||||
//Send midi panic
|
||||
if (output) if (output->midiOut!=NULL) {
|
||||
output->midiOut->send(TAMidiMessage(TA_MIDI_CONTROL,0x7B,0));
|
||||
logV("Midi panic sent");
|
||||
}
|
||||
}
|
||||
playing=false;
|
||||
extValuePresent=false;
|
||||
endOfSong=false; // what?
|
||||
|
@ -2668,10 +2818,17 @@ void DivEngine::previewSampleNoLock(int sample, int note, int pStart, int pEnd)
|
|||
if (rate<=0) rate=song.sample[sample]->centerRate;
|
||||
}
|
||||
if (rate<100) rate=100;
|
||||
double rateOrig=rate;
|
||||
sPreview.rateMul=1;
|
||||
while (sPreview.rateMul<0x40000000 && rate<got.rate) {
|
||||
sPreview.rateMul<<=1;
|
||||
rate*=2.0;
|
||||
}
|
||||
blip_set_rates(samp_bb,rate,got.rate);
|
||||
samp_prevSample=0;
|
||||
sPreview.rate=rate;
|
||||
sPreview.rate=rateOrig;
|
||||
sPreview.pos=(sPreview.pBegin>=0)?sPreview.pBegin:0;
|
||||
sPreview.posSub=0;
|
||||
sPreview.sample=sample;
|
||||
sPreview.wave=-1;
|
||||
sPreview.dir=false;
|
||||
|
@ -2696,10 +2853,17 @@ void DivEngine::previewWaveNoLock(int wave, int note) {
|
|||
blip_clear(samp_bb);
|
||||
double rate=song.wave[wave]->len*((song.tuning*0.0625)*pow(2.0,(double)(note+3)/12.0));
|
||||
if (rate<100) rate=100;
|
||||
double rateOrig=rate;
|
||||
sPreview.rateMul=1;
|
||||
while (sPreview.rateMul<0x40000000 && rate<got.rate) {
|
||||
sPreview.rateMul<<=1;
|
||||
rate*=2.0;
|
||||
}
|
||||
blip_set_rates(samp_bb,rate,got.rate);
|
||||
samp_prevSample=0;
|
||||
sPreview.rate=rate;
|
||||
sPreview.rate=rateOrig;
|
||||
sPreview.pos=0;
|
||||
sPreview.posSub=0;
|
||||
sPreview.sample=-1;
|
||||
sPreview.wave=wave;
|
||||
sPreview.dir=false;
|
||||
|
@ -2904,6 +3068,7 @@ int DivEngine::addInstrument(int refChan, DivInstrumentType fallbackType) {
|
|||
saveLock.lock();
|
||||
song.ins.push_back(ins);
|
||||
song.insLen=insCount+1;
|
||||
checkAssetDir(song.insDir,song.ins.size());
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return insCount;
|
||||
|
@ -2918,6 +3083,7 @@ int DivEngine::addInstrumentPtr(DivInstrument* which) {
|
|||
saveLock.lock();
|
||||
song.ins.push_back(which);
|
||||
song.insLen=song.ins.size();
|
||||
checkAssetDir(song.insDir,song.ins.size());
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return song.insLen;
|
||||
|
@ -2954,6 +3120,8 @@ void DivEngine::delInstrument(int index) {
|
|||
}
|
||||
}
|
||||
}
|
||||
removeAsset(song.insDir,index);
|
||||
checkAssetDir(song.insDir,song.ins.size());
|
||||
}
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
@ -2970,6 +3138,7 @@ int DivEngine::addWave() {
|
|||
int waveCount=(int)song.wave.size();
|
||||
song.wave.push_back(wave);
|
||||
song.waveLen=waveCount+1;
|
||||
checkAssetDir(song.waveDir,song.wave.size());
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return waveCount;
|
||||
|
@ -2986,6 +3155,7 @@ int DivEngine::addWavePtr(DivWavetable* which) {
|
|||
int waveCount=(int)song.wave.size();
|
||||
song.wave.push_back(which);
|
||||
song.waveLen=waveCount+1;
|
||||
checkAssetDir(song.waveDir,song.wave.size());
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return song.waveLen;
|
||||
|
@ -3065,15 +3235,17 @@ DivWavetable* DivEngine::waveFromFile(const char* path, bool addRaw) {
|
|||
// read as .dmw
|
||||
reader.seek(0,SEEK_SET);
|
||||
int len=reader.readI();
|
||||
logD("wave length %d",len);
|
||||
if (len<=0 || len>256) {
|
||||
throw EndOfFileException(&reader,reader.size());
|
||||
}
|
||||
wave->len=len;
|
||||
wave->max=(unsigned char)reader.readC();
|
||||
if (wave->max==255) { // new wavetable format
|
||||
unsigned char waveVersion=reader.readC();
|
||||
logI("reading modern .dmw...");
|
||||
logD("wave version %d",waveVersion);
|
||||
wave->max=reader.readC();
|
||||
wave->max=(unsigned char)reader.readC();
|
||||
for (int i=0; i<len; i++) {
|
||||
wave->data[i]=reader.readI();
|
||||
}
|
||||
|
@ -3138,6 +3310,8 @@ void DivEngine::delWave(int index) {
|
|||
delete song.wave[index];
|
||||
song.wave.erase(song.wave.begin()+index);
|
||||
song.waveLen=song.wave.size();
|
||||
removeAsset(song.waveDir,index);
|
||||
checkAssetDir(song.waveDir,song.wave.size());
|
||||
}
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
@ -3158,6 +3332,7 @@ int DivEngine::addSample() {
|
|||
sPreview.sample=-1;
|
||||
sPreview.pos=0;
|
||||
sPreview.dir=false;
|
||||
checkAssetDir(song.sampleDir,song.sample.size());
|
||||
saveLock.unlock();
|
||||
renderSamples();
|
||||
BUSY_END;
|
||||
|
@ -3175,6 +3350,7 @@ int DivEngine::addSamplePtr(DivSample* which) {
|
|||
saveLock.lock();
|
||||
song.sample.push_back(which);
|
||||
song.sampleLen=sampleCount+1;
|
||||
checkAssetDir(song.sampleDir,song.sample.size());
|
||||
saveLock.unlock();
|
||||
renderSamples();
|
||||
BUSY_END;
|
||||
|
@ -3644,6 +3820,8 @@ void DivEngine::delSample(int index) {
|
|||
delete song.sample[index];
|
||||
song.sample.erase(song.sample.begin()+index);
|
||||
song.sampleLen=song.sample.size();
|
||||
removeAsset(song.sampleDir,index);
|
||||
checkAssetDir(song.sampleDir,song.sample.size());
|
||||
renderSamples();
|
||||
}
|
||||
saveLock.unlock();
|
||||
|
@ -3842,6 +4020,7 @@ bool DivEngine::moveInsUp(int which) {
|
|||
saveLock.lock();
|
||||
song.ins[which]=song.ins[which-1];
|
||||
song.ins[which-1]=prev;
|
||||
moveAsset(song.insDir,which,which-1);
|
||||
exchangeIns(which,which-1);
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
@ -3855,6 +4034,7 @@ bool DivEngine::moveWaveUp(int which) {
|
|||
saveLock.lock();
|
||||
song.wave[which]=song.wave[which-1];
|
||||
song.wave[which-1]=prev;
|
||||
moveAsset(song.waveDir,which,which-1);
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return true;
|
||||
|
@ -3870,6 +4050,7 @@ bool DivEngine::moveSampleUp(int which) {
|
|||
saveLock.lock();
|
||||
song.sample[which]=song.sample[which-1];
|
||||
song.sample[which-1]=prev;
|
||||
moveAsset(song.sampleDir,which,which-1);
|
||||
saveLock.unlock();
|
||||
renderSamples();
|
||||
BUSY_END;
|
||||
|
@ -3884,6 +4065,7 @@ bool DivEngine::moveInsDown(int which) {
|
|||
song.ins[which]=song.ins[which+1];
|
||||
song.ins[which+1]=prev;
|
||||
exchangeIns(which,which+1);
|
||||
moveAsset(song.insDir,which,which+1);
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return true;
|
||||
|
@ -3896,6 +4078,7 @@ bool DivEngine::moveWaveDown(int which) {
|
|||
saveLock.lock();
|
||||
song.wave[which]=song.wave[which+1];
|
||||
song.wave[which+1]=prev;
|
||||
moveAsset(song.waveDir,which,which+1);
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return true;
|
||||
|
@ -3911,6 +4094,7 @@ bool DivEngine::moveSampleDown(int which) {
|
|||
saveLock.lock();
|
||||
song.sample[which]=song.sample[which+1];
|
||||
song.sample[which+1]=prev;
|
||||
moveAsset(song.sampleDir,which,which+1);
|
||||
saveLock.unlock();
|
||||
renderSamples();
|
||||
BUSY_END;
|
||||
|
@ -3955,6 +4139,10 @@ void DivEngine::autoPatchbayP() {
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::recalcPatchbay() {
|
||||
|
||||
}
|
||||
|
||||
bool DivEngine::patchConnect(unsigned int src, unsigned int dest) {
|
||||
unsigned int armed=(src<<16)|(dest&0xffff);
|
||||
for (unsigned int i: song.patchbay) {
|
||||
|
@ -4340,6 +4528,10 @@ void DivEngine::quitDispatch() {
|
|||
}
|
||||
cycles=0;
|
||||
clockDrift=0;
|
||||
midiClockCycles=0;
|
||||
midiClockDrift=0;
|
||||
midiTimeCycles=0;
|
||||
midiTimeDrift=0;
|
||||
chans=0;
|
||||
playing=false;
|
||||
curSpeed=0;
|
||||
|
@ -4356,6 +4548,10 @@ void DivEngine::quitDispatch() {
|
|||
totalTicks=0;
|
||||
totalSeconds=0;
|
||||
totalTicksR=0;
|
||||
curMidiClock=0;
|
||||
curMidiTime=0;
|
||||
curMidiTimeCode=0;
|
||||
curMidiTimePiece=0;
|
||||
totalCmds=0;
|
||||
lastCmds=0;
|
||||
cmdsPerSecond=0;
|
||||
|
@ -4382,6 +4578,8 @@ bool DivEngine::initAudioBackend() {
|
|||
lowLatency=getConfInt("lowLatency",0);
|
||||
metroVol=(float)(getConfInt("metroVol",100))/100.0f;
|
||||
midiOutClock=getConfInt("midiOutClock",0);
|
||||
midiOutTime=getConfInt("midiOutTime",0);
|
||||
midiOutTimeRate=getConfInt("midiOutTimeRate",0);
|
||||
midiOutProgramChange = getConfInt("midiOutProgramChange",0);
|
||||
midiOutMode=getConfInt("midiOutMode",DIV_MIDI_MODE_NOTE);
|
||||
if (metroVol<0.0f) metroVol=0.0f;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "instrument.h"
|
||||
#include "song.h"
|
||||
#include "dispatch.h"
|
||||
#include "effect.h"
|
||||
#include "export.h"
|
||||
#include "dataErrors.h"
|
||||
#include "safeWriter.h"
|
||||
|
@ -53,8 +54,8 @@
|
|||
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
|
||||
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev152"
|
||||
#define DIV_ENGINE_VERSION 152
|
||||
#define DIV_VERSION "0.6pre5"
|
||||
#define DIV_ENGINE_VERSION 158
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
@ -105,7 +106,7 @@ struct DivChannelState {
|
|||
int delayOrder, delayRow, retrigSpeed, retrigTick;
|
||||
int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoDir, vibratoFine;
|
||||
int tremoloDepth, tremoloRate, tremoloPos;
|
||||
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR;
|
||||
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR, lastVibrato, lastPorta;
|
||||
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
|
||||
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp;
|
||||
bool wentThroughNote, goneThroughNote;
|
||||
|
@ -146,6 +147,8 @@ struct DivChannelState {
|
|||
panR(255),
|
||||
panRL(0),
|
||||
panRR(0),
|
||||
lastVibrato(0),
|
||||
lastPorta(0),
|
||||
doNote(false),
|
||||
legato(false),
|
||||
portaStop(false),
|
||||
|
@ -220,6 +223,25 @@ struct DivDispatchContainer {
|
|||
}
|
||||
};
|
||||
|
||||
struct DivEffectContainer {
|
||||
DivEffect* effect;
|
||||
float* in[DIV_MAX_OUTPUTS];
|
||||
float* out[DIV_MAX_OUTPUTS];
|
||||
size_t inLen, outLen;
|
||||
|
||||
void preAcquire(size_t count);
|
||||
void acquire(size_t count);
|
||||
bool init(DivEffectType effectType, DivEngine* eng, double rate, unsigned short version, const unsigned char* data, size_t len);
|
||||
void quit();
|
||||
DivEffectContainer():
|
||||
effect(NULL),
|
||||
inLen(0),
|
||||
outLen(0) {
|
||||
memset(in,0,DIV_MAX_OUTPUTS*sizeof(float*));
|
||||
memset(out,0,DIV_MAX_OUTPUTS*sizeof(float*));
|
||||
}
|
||||
};
|
||||
|
||||
typedef int EffectValConversion(unsigned char,unsigned char);
|
||||
|
||||
struct EffectHandler {
|
||||
|
@ -367,8 +389,10 @@ class DivEngine {
|
|||
bool systemsRegistered;
|
||||
bool hasLoadedSomething;
|
||||
bool midiOutClock;
|
||||
bool midiOutTime;
|
||||
bool midiOutProgramChange;
|
||||
int midiOutMode;
|
||||
int midiOutTimeRate;
|
||||
int softLockCount;
|
||||
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed;
|
||||
size_t curSubSongIndex;
|
||||
|
@ -376,8 +400,13 @@ class DivEngine {
|
|||
double divider;
|
||||
int cycles;
|
||||
double clockDrift;
|
||||
int midiClockCycles;
|
||||
double midiClockDrift;
|
||||
int midiTimeCycles;
|
||||
double midiTimeDrift;
|
||||
int stepPlay;
|
||||
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
|
||||
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, curMidiClock, curMidiTime, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
|
||||
int curMidiTimePiece, curMidiTimeCode;
|
||||
unsigned char extValue, pendingMetroTick;
|
||||
DivGroovePattern speeds;
|
||||
short tempoAccum;
|
||||
|
@ -402,6 +431,7 @@ class DivEngine {
|
|||
std::vector<String> midiOuts;
|
||||
std::vector<DivCommand> cmdStream;
|
||||
std::vector<DivInstrumentType> possibleInsTypes;
|
||||
std::vector<DivEffectContainer> effectInst;
|
||||
static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS];
|
||||
static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS];
|
||||
static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS];
|
||||
|
@ -414,6 +444,7 @@ class DivEngine {
|
|||
int wave;
|
||||
int pos;
|
||||
int pBegin, pEnd;
|
||||
int rateMul, posSub;
|
||||
bool dir;
|
||||
SamplePreview():
|
||||
rate(0.0),
|
||||
|
@ -422,6 +453,8 @@ class DivEngine {
|
|||
pos(0),
|
||||
pBegin(-1),
|
||||
pEnd(-1),
|
||||
rateMul(1),
|
||||
posSub(0),
|
||||
dir(false) {}
|
||||
} sPreview;
|
||||
|
||||
|
@ -429,6 +462,7 @@ class DivEngine {
|
|||
short tremTable[128];
|
||||
int reversePitchTable[4096];
|
||||
int pitchTable[4096];
|
||||
short effectSlotMap[4096];
|
||||
char c163NameCS[1024];
|
||||
int midiBaseChan;
|
||||
bool midiPoly;
|
||||
|
@ -463,6 +497,8 @@ class DivEngine {
|
|||
void recalcChans();
|
||||
void reset();
|
||||
void playSub(bool preserveDrift, int goalRow=0);
|
||||
void runMidiClock(int totalCycles=1);
|
||||
void runMidiTime(int totalCycles=1);
|
||||
|
||||
void testFunction();
|
||||
|
||||
|
@ -500,11 +536,21 @@ class DivEngine {
|
|||
void swapChannels(int src, int dest);
|
||||
void stompChannel(int ch);
|
||||
|
||||
// recalculate patchbay (UNSAFE)
|
||||
void recalcPatchbay();
|
||||
|
||||
// change song (UNSAFE)
|
||||
void changeSong(size_t songIndex);
|
||||
|
||||
// check whether an asset directory is complete
|
||||
void checkAssetDir(std::vector<DivAssetDir>& dir, size_t entries);
|
||||
// move an asset
|
||||
void moveAsset(std::vector<DivAssetDir>& dir, int before, int after);
|
||||
|
||||
// remove an asset
|
||||
void removeAsset(std::vector<DivAssetDir>& dir, int entry);
|
||||
|
||||
// read/write asset dir
|
||||
void putAssetDirData(SafeWriter* w, std::vector<DivAssetDir>& dir);
|
||||
DivDataErrors readAssetDirData(SafeReader& reader, std::vector<DivAssetDir>& dir);
|
||||
|
||||
// add every export method here
|
||||
friend class DivROMExport;
|
||||
|
@ -545,7 +591,7 @@ class DivEngine {
|
|||
SafeWriter* saveDMF(unsigned char version);
|
||||
// save as .fur.
|
||||
// if notPrimary is true then the song will not be altered
|
||||
SafeWriter* saveFur(bool notPrimary=false);
|
||||
SafeWriter* saveFur(bool notPrimary=false, bool newPatternFormat=true);
|
||||
// build a ROM file (TODO).
|
||||
// specify system to build ROM for.
|
||||
std::vector<DivROMExportOutput> buildROM(DivROMExportOptions sys);
|
||||
|
@ -583,6 +629,8 @@ class DivEngine {
|
|||
// convert old flags
|
||||
static void convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivSystem sys);
|
||||
|
||||
// check whether an asset directory is complete (UNSAFE)
|
||||
void checkAssetDir(std::vector<DivAssetDir>& dir, size_t entries);
|
||||
|
||||
// benchmark (returns time in seconds)
|
||||
double benchmarkPlayback();
|
||||
|
@ -1026,6 +1074,12 @@ class DivEngine {
|
|||
// move system
|
||||
bool swapSystem(int src, int dest, bool preserveOrder=true);
|
||||
|
||||
// add effect
|
||||
bool addEffect(DivEffectType which);
|
||||
|
||||
// remove effect
|
||||
bool removeEffect(int index);
|
||||
|
||||
// write to register on system
|
||||
void poke(int sys, unsigned int addr, unsigned short val);
|
||||
|
||||
|
@ -1119,8 +1173,10 @@ class DivEngine {
|
|||
systemsRegistered(false),
|
||||
hasLoadedSomething(false),
|
||||
midiOutClock(false),
|
||||
midiOutTime(false),
|
||||
midiOutProgramChange(false),
|
||||
midiOutMode(DIV_MIDI_MODE_NOTE),
|
||||
midiOutTimeRate(0),
|
||||
softLockCount(0),
|
||||
subticks(0),
|
||||
ticks(0),
|
||||
|
@ -1141,16 +1197,24 @@ class DivEngine {
|
|||
divider(60),
|
||||
cycles(0),
|
||||
clockDrift(0),
|
||||
midiClockCycles(0),
|
||||
midiClockDrift(0),
|
||||
midiTimeCycles(0),
|
||||
midiTimeDrift(0),
|
||||
stepPlay(0),
|
||||
changeOrd(-1),
|
||||
changePos(0),
|
||||
totalSeconds(0),
|
||||
totalTicks(0),
|
||||
totalTicksR(0),
|
||||
curMidiClock(0),
|
||||
curMidiTime(0),
|
||||
totalCmds(0),
|
||||
lastCmds(0),
|
||||
cmdsPerSecond(0),
|
||||
globalPitch(0),
|
||||
curMidiTimePiece(0),
|
||||
curMidiTimeCode(0),
|
||||
extValue(0),
|
||||
pendingMetroTick(0),
|
||||
tempoAccum(0),
|
||||
|
@ -1198,6 +1262,7 @@ class DivEngine {
|
|||
memset(tremTable,0,128*sizeof(short));
|
||||
memset(reversePitchTable,0,4096*sizeof(int));
|
||||
memset(pitchTable,0,4096*sizeof(int));
|
||||
memset(effectSlotMap,-1,4096*sizeof(short));
|
||||
memset(sysDefs,0,DIV_MAX_CHIP_DEFS*sizeof(void*));
|
||||
memset(walked,0,8192);
|
||||
memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*)));
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "dataErrors.h"
|
||||
#include "engine.h"
|
||||
#include "../ta-log.h"
|
||||
#include "instrument.h"
|
||||
|
@ -1040,6 +1041,11 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ds.systemFlags[0].set("noEasyNoise",true);
|
||||
}
|
||||
|
||||
// NES PCM
|
||||
if (ds.system[0]==DIV_SYSTEM_NES) {
|
||||
ds.systemFlags[0].set("dpcmMode",false);
|
||||
}
|
||||
|
||||
ds.systemName=getSongSystemLegacyName(ds,!getConfInt("noMultiSystem",0));
|
||||
|
||||
if (active) quitDispatch();
|
||||
|
@ -1644,18 +1650,58 @@ void DivEngine::convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivS
|
|||
}
|
||||
}
|
||||
|
||||
short newFormatNotes[180]={
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -5
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -4
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -3
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -2
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -1
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 0
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 1
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 2
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 3
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 4
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 5
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 6
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 7
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 8
|
||||
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 // 9
|
||||
};
|
||||
|
||||
short newFormatOctaves[180]={
|
||||
250, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, // -5
|
||||
251, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, // -4
|
||||
252, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, // -3
|
||||
253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, // -2
|
||||
254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // -1
|
||||
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
|
||||
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 2
|
||||
2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 3
|
||||
3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 4
|
||||
4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 5
|
||||
5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 6
|
||||
6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 7
|
||||
7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 8
|
||||
8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 9
|
||||
};
|
||||
|
||||
bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||
unsigned int insPtr[256];
|
||||
unsigned int wavePtr[256];
|
||||
unsigned int samplePtr[256];
|
||||
unsigned int subSongPtr[256];
|
||||
unsigned int sysFlagsPtr[DIV_MAX_CHIPS];
|
||||
std::vector<int> patPtr;
|
||||
unsigned int assetDirPtr[3];
|
||||
std::vector<unsigned int> patPtr;
|
||||
int numberOfSubSongs=0;
|
||||
char magic[5];
|
||||
memset(magic,0,5);
|
||||
SafeReader reader=SafeReader(file,len);
|
||||
warnings="";
|
||||
assetDirPtr[0]=0;
|
||||
assetDirPtr[1]=0;
|
||||
assetDirPtr[2]=0;
|
||||
try {
|
||||
DivSong ds;
|
||||
DivSubSong* subSong=ds.subsong[0];
|
||||
|
@ -1787,6 +1833,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
if (ds.version<138) {
|
||||
ds.brokenPortaLegato=true;
|
||||
}
|
||||
if (ds.version<155) {
|
||||
ds.brokenFMOff=true;
|
||||
}
|
||||
ds.isDMF=false;
|
||||
|
||||
reader.readS(); // reserved
|
||||
|
@ -2295,7 +2344,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
if (ds.version>=138) {
|
||||
ds.brokenPortaLegato=reader.readC();
|
||||
for (int i=0; i<7; i++) {
|
||||
if (ds.version>=155) {
|
||||
ds.brokenFMOff=reader.readC();
|
||||
} else {
|
||||
reader.readC();
|
||||
}
|
||||
for (int i=0; i<6; i++) {
|
||||
reader.readC();
|
||||
}
|
||||
}
|
||||
|
@ -2319,6 +2373,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
if (ds.version>=156) {
|
||||
assetDirPtr[0]=reader.readI();
|
||||
assetDirPtr[1]=reader.readI();
|
||||
assetDirPtr[2]=reader.readI();
|
||||
}
|
||||
|
||||
// read system flags
|
||||
if (ds.version>=119) {
|
||||
logD("reading chip flags...");
|
||||
|
@ -2353,6 +2413,53 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
// read asset directories
|
||||
if (ds.version>=156) {
|
||||
logD("reading asset directories...");
|
||||
|
||||
if (!reader.seek(assetDirPtr[0],SEEK_SET)) {
|
||||
logE("couldn't seek to ins dir!");
|
||||
lastError=fmt::sprintf("couldn't read instrument directory");
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (readAssetDirData(reader,ds.insDir)!=DIV_DATA_SUCCESS) {
|
||||
lastError="invalid instrument directory data!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!reader.seek(assetDirPtr[1],SEEK_SET)) {
|
||||
logE("couldn't seek to wave dir!");
|
||||
lastError=fmt::sprintf("couldn't read wavetable directory");
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (readAssetDirData(reader,ds.waveDir)!=DIV_DATA_SUCCESS) {
|
||||
lastError="invalid wavetable directory data!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!reader.seek(assetDirPtr[2],SEEK_SET)) {
|
||||
logE("couldn't seek to sample dir!");
|
||||
lastError=fmt::sprintf("couldn't read sample directory");
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (readAssetDirData(reader,ds.sampleDir)!=DIV_DATA_SUCCESS) {
|
||||
lastError="invalid sample directory data!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// read subsongs
|
||||
if (ds.version>=95) {
|
||||
for (int i=0; i<numberOfSubSongs; i++) {
|
||||
|
@ -2510,7 +2617,8 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
// read patterns
|
||||
for (int i: patPtr) {
|
||||
for (unsigned int i: patPtr) {
|
||||
bool isNewFormat=false;
|
||||
if (!reader.seek(i,SEEK_SET)) {
|
||||
logE("couldn't seek to pattern in %x!",i);
|
||||
lastError=fmt::sprintf("couldn't seek to pattern in %x!",i);
|
||||
|
@ -2521,62 +2629,151 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
reader.read(magic,4);
|
||||
logD("reading pattern in %x...",i);
|
||||
if (strcmp(magic,"PATR")!=0) {
|
||||
logE("%x: invalid pattern header!",i);
|
||||
lastError="invalid pattern header!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
if (strcmp(magic,"PATN")!=0 || ds.version<157) {
|
||||
logE("%x: invalid pattern header!",i);
|
||||
lastError="invalid pattern header!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
} else {
|
||||
isNewFormat=true;
|
||||
}
|
||||
}
|
||||
reader.readI();
|
||||
|
||||
int chan=reader.readS();
|
||||
int index=reader.readS();
|
||||
int subs=0;
|
||||
if (ds.version>=95) {
|
||||
subs=reader.readS();
|
||||
} else {
|
||||
reader.readS();
|
||||
}
|
||||
reader.readS();
|
||||
if (isNewFormat) {
|
||||
int subs=(unsigned char)reader.readC();
|
||||
int chan=(unsigned char)reader.readC();
|
||||
int index=reader.readS();
|
||||
|
||||
logD("- %d, %d, %d",subs,chan,index);
|
||||
logD("- %d, %d, %d (new)",subs,chan,index);
|
||||
|
||||
if (chan<0 || chan>=tchans) {
|
||||
logE("pattern channel out of range!",i);
|
||||
lastError="pattern channel out of range!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (index<0 || index>(DIV_MAX_PATTERNS-1)) {
|
||||
logE("pattern index out of range!",i);
|
||||
lastError="pattern index out of range!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (subs<0 || subs>=(int)ds.subsong.size()) {
|
||||
logE("pattern subsong out of range!",i);
|
||||
lastError="pattern subsong out of range!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true);
|
||||
for (int j=0; j<ds.subsong[subs]->patLen; j++) {
|
||||
pat->data[j][0]=reader.readS();
|
||||
pat->data[j][1]=reader.readS();
|
||||
pat->data[j][2]=reader.readS();
|
||||
pat->data[j][3]=reader.readS();
|
||||
for (int k=0; k<ds.subsong[subs]->pat[chan].effectCols; k++) {
|
||||
pat->data[j][4+(k<<1)]=reader.readS();
|
||||
pat->data[j][5+(k<<1)]=reader.readS();
|
||||
if (chan<0 || chan>=tchans) {
|
||||
logE("pattern channel out of range!",i);
|
||||
lastError="pattern channel out of range!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (index<0 || index>(DIV_MAX_PATTERNS-1)) {
|
||||
logE("pattern index out of range!",i);
|
||||
lastError="pattern index out of range!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (subs<0 || subs>=(int)ds.subsong.size()) {
|
||||
logE("pattern subsong out of range!",i);
|
||||
lastError="pattern subsong out of range!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ds.version>=51) {
|
||||
DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true);
|
||||
pat->name=reader.readString();
|
||||
|
||||
// read new pattern
|
||||
for (int j=0; j<ds.subsong[subs]->patLen; j++) {
|
||||
unsigned char mask=reader.readC();
|
||||
unsigned short effectMask=0;
|
||||
|
||||
if (mask==0xff) break;
|
||||
if (mask&128) {
|
||||
j+=(mask&127)+1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mask&32) {
|
||||
effectMask|=(unsigned char)reader.readC();
|
||||
}
|
||||
if (mask&64) {
|
||||
effectMask|=((unsigned short)reader.readC()&0xff)<<8;
|
||||
}
|
||||
if (mask&8) effectMask|=1;
|
||||
if (mask&16) effectMask|=2;
|
||||
|
||||
if (mask&1) { // note
|
||||
unsigned char note=reader.readC();
|
||||
if (note==180) {
|
||||
pat->data[j][0]=100;
|
||||
pat->data[j][1]=0;
|
||||
} else if (note==181) {
|
||||
pat->data[j][0]=101;
|
||||
pat->data[j][1]=0;
|
||||
} else if (note==182) {
|
||||
pat->data[j][0]=102;
|
||||
pat->data[j][1]=0;
|
||||
} else if (note<180) {
|
||||
pat->data[j][0]=newFormatNotes[note];
|
||||
pat->data[j][1]=newFormatOctaves[note];
|
||||
} else {
|
||||
pat->data[j][0]=0;
|
||||
pat->data[j][1]=0;
|
||||
}
|
||||
}
|
||||
if (mask&2) { // instrument
|
||||
pat->data[j][2]=(unsigned char)reader.readC();
|
||||
}
|
||||
if (mask&4) { // volume
|
||||
pat->data[j][3]=(unsigned char)reader.readC();
|
||||
}
|
||||
for (unsigned char k=0; k<16; k++) {
|
||||
if (effectMask&(1<<k)) {
|
||||
pat->data[j][4+k]=(unsigned char)reader.readC();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int chan=reader.readS();
|
||||
int index=reader.readS();
|
||||
int subs=0;
|
||||
if (ds.version>=95) {
|
||||
subs=reader.readS();
|
||||
} else {
|
||||
reader.readS();
|
||||
}
|
||||
reader.readS();
|
||||
|
||||
logD("- %d, %d, %d (old)",subs,chan,index);
|
||||
|
||||
if (chan<0 || chan>=tchans) {
|
||||
logE("pattern channel out of range!",i);
|
||||
lastError="pattern channel out of range!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (index<0 || index>(DIV_MAX_PATTERNS-1)) {
|
||||
logE("pattern index out of range!",i);
|
||||
lastError="pattern index out of range!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (subs<0 || subs>=(int)ds.subsong.size()) {
|
||||
logE("pattern subsong out of range!",i);
|
||||
lastError="pattern subsong out of range!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true);
|
||||
for (int j=0; j<ds.subsong[subs]->patLen; j++) {
|
||||
pat->data[j][0]=reader.readS();
|
||||
pat->data[j][1]=reader.readS();
|
||||
pat->data[j][2]=reader.readS();
|
||||
pat->data[j][3]=reader.readS();
|
||||
for (int k=0; k<ds.subsong[subs]->pat[chan].effectCols; k++) {
|
||||
pat->data[j][4+(k<<1)]=reader.readS();
|
||||
pat->data[j][5+(k<<1)]=reader.readS();
|
||||
}
|
||||
}
|
||||
|
||||
if (ds.version>=51) {
|
||||
pat->name=reader.readString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2715,6 +2912,24 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
// SrgaPCM slide compat
|
||||
if (ds.version<153) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_SEGAPCM || ds.system[i]==DIV_SYSTEM_SEGAPCM_COMPAT) {
|
||||
ds.systemFlags[i].set("oldSlides",true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NES PCM compat
|
||||
if (ds.version<154) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_NES) {
|
||||
ds.systemFlags[i].set("dpcmMode",false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (active) quitDispatch();
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
|
@ -4812,7 +5027,57 @@ struct PatToWrite {
|
|||
pat(p) {}
|
||||
};
|
||||
|
||||
SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||
void DivEngine::putAssetDirData(SafeWriter* w, std::vector<DivAssetDir>& dir) {
|
||||
size_t blockStartSeek, blockEndSeek;
|
||||
|
||||
w->write("ADIR",4);
|
||||
blockStartSeek=w->tell();
|
||||
w->writeI(0);
|
||||
|
||||
w->writeI(dir.size());
|
||||
|
||||
for (DivAssetDir& i: dir) {
|
||||
w->writeString(i.name,false);
|
||||
w->writeS(i.entries.size());
|
||||
for (int j: i.entries) {
|
||||
w->writeC(j);
|
||||
}
|
||||
}
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
w->seek(0,SEEK_END);
|
||||
}
|
||||
|
||||
DivDataErrors DivEngine::readAssetDirData(SafeReader& reader, std::vector<DivAssetDir>& dir) {
|
||||
char magic[4];
|
||||
reader.read(magic,4);
|
||||
if (memcmp(magic,"ADIR",4)!=0) {
|
||||
logV("header is invalid: %c%c%c%c",magic[0],magic[1],magic[2],magic[3]);
|
||||
return DIV_DATA_INVALID_HEADER;
|
||||
}
|
||||
reader.readI(); // reserved
|
||||
|
||||
unsigned int numDirs=reader.readI();
|
||||
|
||||
for (unsigned int i=0; i<numDirs; i++) {
|
||||
DivAssetDir d;
|
||||
|
||||
d.name=reader.readString();
|
||||
unsigned short numEntries=reader.readS();
|
||||
|
||||
for (unsigned short j=0; j<numEntries; j++) {
|
||||
d.entries.push_back(((unsigned char)reader.readC()));
|
||||
}
|
||||
|
||||
dir.push_back(d);
|
||||
}
|
||||
|
||||
return DIV_DATA_SUCCESS;
|
||||
}
|
||||
|
||||
SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
|
||||
saveLock.lock();
|
||||
std::vector<int> subSongPtr;
|
||||
std::vector<int> sysFlagsPtr;
|
||||
|
@ -4820,7 +5085,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
std::vector<int> wavePtr;
|
||||
std::vector<int> samplePtr;
|
||||
std::vector<int> patPtr;
|
||||
size_t ptrSeek, subSongPtrSeek, sysFlagsPtrSeek, blockStartSeek, blockEndSeek;
|
||||
int assetDirPtr[3];
|
||||
size_t ptrSeek, subSongPtrSeek, sysFlagsPtrSeek, blockStartSeek, blockEndSeek, assetDirPtrSeek;
|
||||
size_t subSongIndex=0;
|
||||
DivSubSong* subSong=song.subsong[subSongIndex];
|
||||
warnings="";
|
||||
|
@ -5119,6 +5385,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
}
|
||||
}
|
||||
|
||||
// asset dir pointers (we'll seek here later)
|
||||
assetDirPtrSeek=w->tell();
|
||||
w->writeI(0);
|
||||
w->writeI(0);
|
||||
w->writeI(0);
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
|
@ -5206,6 +5478,14 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->seek(0,SEEK_END);
|
||||
}
|
||||
|
||||
/// ASSET DIRECTORIES
|
||||
assetDirPtr[0]=w->tell();
|
||||
putAssetDirData(w,song.insDir);
|
||||
assetDirPtr[1]=w->tell();
|
||||
putAssetDirData(w,song.waveDir);
|
||||
assetDirPtr[2]=w->tell();
|
||||
putAssetDirData(w,song.sampleDir);
|
||||
|
||||
/// INSTRUMENT
|
||||
for (int i=0; i<song.insLen; i++) {
|
||||
DivInstrument* ins=song.ins[i];
|
||||
|
@ -5231,31 +5511,133 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
for (PatToWrite& i: patsToWrite) {
|
||||
DivPattern* pat=song.subsong[i.subsong]->pat[i.chan].getPattern(i.pat,false);
|
||||
patPtr.push_back(w->tell());
|
||||
w->write("PATR",4);
|
||||
blockStartSeek=w->tell();
|
||||
w->writeI(0);
|
||||
|
||||
w->writeS(i.chan);
|
||||
w->writeS(i.pat);
|
||||
w->writeS(i.subsong);
|
||||
if (newPatternFormat) {
|
||||
w->write("PATN",4);
|
||||
blockStartSeek=w->tell();
|
||||
w->writeI(0);
|
||||
|
||||
w->writeS(0); // reserved
|
||||
w->writeC(i.subsong);
|
||||
w->writeC(i.chan);
|
||||
w->writeS(i.pat);
|
||||
w->writeString(pat->name,false);
|
||||
|
||||
for (int j=0; j<song.subsong[i.subsong]->patLen; j++) {
|
||||
w->writeS(pat->data[j][0]); // note
|
||||
w->writeS(pat->data[j][1]); // octave
|
||||
w->writeS(pat->data[j][2]); // instrument
|
||||
w->writeS(pat->data[j][3]); // volume
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
for (int k=0; k<song.subsong[i.subsong]->pat[i.chan].effectCols*2; k++) {
|
||||
w->writeS(pat->data[j][4+k]);
|
||||
unsigned char emptyRows=0;
|
||||
|
||||
for (int j=0; j<song.subsong[i.subsong]->patLen; j++) {
|
||||
unsigned char mask=0;
|
||||
unsigned char finalNote=255;
|
||||
unsigned short effectMask=0;
|
||||
|
||||
if (pat->data[j][0]==100) {
|
||||
finalNote=180;
|
||||
} else if (pat->data[j][0]==101) { // note release
|
||||
finalNote=181;
|
||||
} else if (pat->data[j][0]==102) { // macro release
|
||||
finalNote=182;
|
||||
} else if (pat->data[j][1]==0 && pat->data[j][0]==0) {
|
||||
finalNote=255;
|
||||
} else {
|
||||
int seek=(pat->data[j][0]+(signed char)pat->data[j][1]*12)+60;
|
||||
if (seek<0 || seek>=180) {
|
||||
finalNote=255;
|
||||
} else {
|
||||
finalNote=seek;
|
||||
}
|
||||
}
|
||||
|
||||
if (finalNote!=255) mask|=1; // note
|
||||
if (pat->data[j][2]!=-1) mask|=2; // instrument
|
||||
if (pat->data[j][3]!=-1) mask|=4; // volume
|
||||
for (int k=0; k<song.subsong[i.subsong]->pat[i.chan].effectCols*2; k+=2) {
|
||||
if (k==0) {
|
||||
if (pat->data[j][4+k]!=-1) mask|=8;
|
||||
if (pat->data[j][5+k]!=-1) mask|=16;
|
||||
} else if (k<8) {
|
||||
if (pat->data[j][4+k]!=-1 || pat->data[j][5+k]!=-1) mask|=32;
|
||||
} else {
|
||||
if (pat->data[j][4+k]!=-1 || pat->data[j][5+k]!=-1) mask|=64;
|
||||
}
|
||||
|
||||
if (pat->data[j][4+k]!=-1) effectMask|=(1<<k);
|
||||
if (pat->data[j][5+k]!=-1) effectMask|=(2<<k);
|
||||
}
|
||||
|
||||
if (mask==0) {
|
||||
emptyRows++;
|
||||
if (emptyRows>127) {
|
||||
w->writeC(128|(emptyRows-2));
|
||||
emptyRows=0;
|
||||
}
|
||||
} else {
|
||||
if (emptyRows>1) {
|
||||
w->writeC(128|(emptyRows-2));
|
||||
emptyRows=0;
|
||||
} else if (emptyRows) {
|
||||
w->writeC(0);
|
||||
emptyRows=0;
|
||||
}
|
||||
|
||||
w->writeC(mask);
|
||||
|
||||
if (mask&32) w->writeC(effectMask&0xff);
|
||||
if (mask&64) w->writeC((effectMask>>8)&0xff);
|
||||
|
||||
if (mask&1) w->writeC(finalNote);
|
||||
if (mask&2) w->writeC(pat->data[j][2]);
|
||||
if (mask&4) w->writeC(pat->data[j][3]);
|
||||
if (mask&8) w->writeC(pat->data[j][4]);
|
||||
if (mask&16) w->writeC(pat->data[j][5]);
|
||||
if (mask&32) {
|
||||
if (effectMask&4) w->writeC(pat->data[j][6]);
|
||||
if (effectMask&8) w->writeC(pat->data[j][7]);
|
||||
if (effectMask&16) w->writeC(pat->data[j][8]);
|
||||
if (effectMask&32) w->writeC(pat->data[j][9]);
|
||||
if (effectMask&64) w->writeC(pat->data[j][10]);
|
||||
if (effectMask&128) w->writeC(pat->data[j][11]);
|
||||
}
|
||||
if (mask&64) {
|
||||
if (effectMask&256) w->writeC(pat->data[j][12]);
|
||||
if (effectMask&512) w->writeC(pat->data[j][13]);
|
||||
if (effectMask&1024) w->writeC(pat->data[j][14]);
|
||||
if (effectMask&2048) w->writeC(pat->data[j][15]);
|
||||
if (effectMask&4096) w->writeC(pat->data[j][16]);
|
||||
if (effectMask&8192) w->writeC(pat->data[j][17]);
|
||||
if (effectMask&16384) w->writeC(pat->data[j][18]);
|
||||
if (effectMask&32768) w->writeC(pat->data[j][19]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
w->write(&pat->data[j][4],2*song.subsong[i.subsong]->pat[i.chan].effectCols*2); // effects
|
||||
#endif
|
||||
}
|
||||
|
||||
w->writeString(pat->name,false);
|
||||
// stop
|
||||
w->writeC(0xff);
|
||||
} else {
|
||||
w->write("PATR",4);
|
||||
blockStartSeek=w->tell();
|
||||
w->writeI(0);
|
||||
|
||||
w->writeS(i.chan);
|
||||
w->writeS(i.pat);
|
||||
w->writeS(i.subsong);
|
||||
|
||||
w->writeS(0); // reserved
|
||||
|
||||
for (int j=0; j<song.subsong[i.subsong]->patLen; j++) {
|
||||
w->writeS(pat->data[j][0]); // note
|
||||
w->writeS(pat->data[j][1]); // octave
|
||||
w->writeS(pat->data[j][2]); // instrument
|
||||
w->writeS(pat->data[j][3]); // volume
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
for (int k=0; k<song.subsong[i.subsong]->pat[i.chan].effectCols*2; k++) {
|
||||
w->writeS(pat->data[j][4+k]);
|
||||
}
|
||||
#else
|
||||
w->write(&pat->data[j][4],2*song.subsong[i.subsong]->pat[i.chan].effectCols*2); // effects
|
||||
#endif
|
||||
}
|
||||
|
||||
w->writeString(pat->name,false);
|
||||
}
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
|
@ -5297,6 +5679,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeI(sysFlagsPtr[i]);
|
||||
}
|
||||
|
||||
// asset dir pointers
|
||||
w->seek(assetDirPtrSeek,SEEK_SET);
|
||||
for (size_t i=0; i<3; i++) {
|
||||
w->writeI(assetDirPtr[i]);
|
||||
}
|
||||
|
||||
saveLock.unlock();
|
||||
return w;
|
||||
}
|
||||
|
|
|
@ -23,11 +23,7 @@
|
|||
static DivPattern emptyPat;
|
||||
|
||||
DivPattern::DivPattern() {
|
||||
memset(data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
|
||||
for (int i=0; i<DIV_MAX_ROWS; i++) {
|
||||
data[i][0]=0;
|
||||
data[i][1]=0;
|
||||
}
|
||||
clear();
|
||||
}
|
||||
|
||||
DivPattern* DivChannelData::getPattern(int index, bool create) {
|
||||
|
@ -93,6 +89,14 @@ void DivPattern::copyOn(DivPattern* dest) {
|
|||
memcpy(dest->data,data,sizeof(data));
|
||||
}
|
||||
|
||||
void DivPattern::clear() {
|
||||
memset(data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
|
||||
for (int i=0; i<DIV_MAX_ROWS; i++) {
|
||||
data[i][0]=0;
|
||||
data[i][1]=0;
|
||||
}
|
||||
}
|
||||
|
||||
DivChannelData::DivChannelData():
|
||||
effectCols(1) {
|
||||
memset(data,0,DIV_MAX_PATTERNS*sizeof(void*));
|
||||
|
|
|
@ -24,6 +24,11 @@ struct DivPattern {
|
|||
String name;
|
||||
short data[DIV_MAX_ROWS][DIV_MAX_COLS];
|
||||
|
||||
/**
|
||||
* clear the pattern.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* copy this pattern to another.
|
||||
* @param dest the destination pattern.
|
||||
|
|
|
@ -120,7 +120,7 @@ void DivPlatformAY8910::runDAC() {
|
|||
bool end=false;
|
||||
bool changed=false;
|
||||
int prevOut=chan[i].dac.out;
|
||||
while (chan[i].dac.period>rate && !end) {
|
||||
while (chan[i].dac.period>dacRate && !end) {
|
||||
DivSample* s=parent->getSample(chan[i].dac.sample);
|
||||
if (s->samples<=0) {
|
||||
chan[i].dac.sample=-1;
|
||||
|
@ -143,7 +143,7 @@ void DivPlatformAY8910::runDAC() {
|
|||
end=true;
|
||||
break;
|
||||
}
|
||||
chan[i].dac.period-=rate;
|
||||
chan[i].dac.period-=dacRate;
|
||||
}
|
||||
if (changed && !end) {
|
||||
if (!isMuted[i]) {
|
||||
|
@ -187,9 +187,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) {
|
|||
buf[0][i]=ayBuf[0][0];
|
||||
buf[1][i]=buf[0][i];
|
||||
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=sunsoftVolTable[31-(ay->lastIndx&31)]>>3;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>5)&31)]>>3;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>10)&31)]>>3;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=sunsoftVolTable[31-(ay->lastIndx&31)]<<3;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>5)&31)]<<3;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<3;
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<len; i++) {
|
||||
|
@ -687,6 +687,7 @@ void DivPlatformAY8910::muteChannel(int ch, bool mute) {
|
|||
void DivPlatformAY8910::forceIns() {
|
||||
for (int i=0; i<3; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
immWrite(0x0b,ayEnvPeriod);
|
||||
immWrite(0x0c,ayEnvPeriod>>8);
|
||||
|
@ -796,6 +797,7 @@ void DivPlatformAY8910::setFlags(const DivConfig& flags) {
|
|||
chipClock=extClock;
|
||||
rate=chipClock/extDiv;
|
||||
clockSel=false;
|
||||
dacRate=chipClock/dacRateDiv;
|
||||
} else {
|
||||
clockSel=flags.getBool("halfClock",false);
|
||||
switch (flags.getInt("clockSel",0)) {
|
||||
|
@ -850,6 +852,7 @@ void DivPlatformAY8910::setFlags(const DivConfig& flags) {
|
|||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/8;
|
||||
dacRate=rate;
|
||||
}
|
||||
for (int i=0; i<3; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
|
|
|
@ -104,7 +104,9 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
|
||||
bool extMode;
|
||||
unsigned int extClock;
|
||||
int dacRate;
|
||||
unsigned char extDiv;
|
||||
unsigned char dacRateDiv;
|
||||
|
||||
bool stereo, sunsoft, intellivision, clockSel;
|
||||
bool ioPortA, ioPortB;
|
||||
|
@ -119,7 +121,6 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
short* ayBuf[3];
|
||||
size_t ayBufLen;
|
||||
|
||||
void runDAC();
|
||||
void checkWrites();
|
||||
void updateOutSel(bool immediate=false);
|
||||
|
||||
|
@ -127,6 +128,7 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void runDAC();
|
||||
void setExtClockDiv(unsigned int eclk=COLOR_NTSC, unsigned char ediv=8);
|
||||
void acquire(short** buf, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
|
@ -151,10 +153,11 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
const char** getRegisterSheet();
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
DivPlatformAY8910(bool useExtMode=false, unsigned int eclk=COLOR_NTSC, unsigned char ediv=8):
|
||||
DivPlatformAY8910(bool useExtMode=false, unsigned int eclk=COLOR_NTSC, unsigned char ediv=8, unsigned char ddiv=24):
|
||||
DivDispatch(),
|
||||
extMode(useExtMode),
|
||||
extClock(eclk),
|
||||
extDiv(ediv) {}
|
||||
extDiv(ediv),
|
||||
dacRateDiv(ddiv) {}
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -105,6 +105,7 @@ void DivPlatformC64::updateFilter() {
|
|||
}
|
||||
|
||||
void DivPlatformC64::tick(bool sysTick) {
|
||||
bool willUpdateFilter=false;
|
||||
for (int i=0; i<3; i++) {
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
|
@ -117,10 +118,10 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
if (filtCut>2047) filtCut=2047;
|
||||
if (filtCut<0) filtCut=0;
|
||||
}
|
||||
updateFilter();
|
||||
willUpdateFilter=true;
|
||||
} else {
|
||||
vol=MIN(15,chan[i].std.vol.val);
|
||||
updateFilter();
|
||||
willUpdateFilter=true;
|
||||
}
|
||||
}
|
||||
if (NEW_ARP_STRAT) {
|
||||
|
@ -156,11 +157,11 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
}
|
||||
if (chan[i].std.ex1.had) {
|
||||
filtControl=chan[i].std.ex1.val&15;
|
||||
updateFilter();
|
||||
willUpdateFilter=true;
|
||||
}
|
||||
if (chan[i].std.ex2.had) {
|
||||
filtRes=chan[i].std.ex2.val&15;
|
||||
updateFilter();
|
||||
willUpdateFilter=true;
|
||||
}
|
||||
if (chan[i].std.ex3.had) {
|
||||
chan[i].sync=chan[i].std.ex3.val&1;
|
||||
|
@ -207,6 +208,7 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
chan[i].freqChanged=false;
|
||||
}
|
||||
}
|
||||
if (willUpdateFilter) updateFilter();
|
||||
}
|
||||
|
||||
int DivPlatformC64::dispatch(DivCommand c) {
|
||||
|
|
|
@ -82,8 +82,12 @@ void DivPlatformGB::acquire(short** buf, size_t len) {
|
|||
void DivPlatformGB::updateWave() {
|
||||
rWrite(0x1a,0);
|
||||
for (int i=0; i<16; i++) {
|
||||
int nibble1=15-ws.output[((i<<1)+antiClickWavePos-1)&31];
|
||||
int nibble2=15-ws.output[((1+(i<<1))+antiClickWavePos-1)&31];
|
||||
int nibble1=ws.output[((i<<1)+antiClickWavePos)&31];
|
||||
int nibble2=ws.output[((1+(i<<1))+antiClickWavePos)&31];
|
||||
if (invertWave) {
|
||||
nibble1^=15;
|
||||
nibble2^=15;
|
||||
}
|
||||
rWrite(0x30+i,(nibble1<<4)|nibble2);
|
||||
}
|
||||
antiClickWavePos&=31;
|
||||
|
@ -658,6 +662,7 @@ void DivPlatformGB::setFlags(const DivConfig& flags) {
|
|||
model=GB_MODEL_AGB;
|
||||
break;
|
||||
}
|
||||
invertWave=flags.getBool("invertWave",true);
|
||||
enoughAlready=flags.getBool("enoughAlready",false);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ class DivPlatformGB: public DivDispatch {
|
|||
DivDispatchOscBuffer* oscBuf[4];
|
||||
bool isMuted[4];
|
||||
bool antiClickEnabled;
|
||||
bool invertWave;
|
||||
bool enoughAlready;
|
||||
unsigned char lastPan;
|
||||
DivWaveSynth ws;
|
||||
|
|
|
@ -211,7 +211,7 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
|
||||
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||
if (i==2) { // triangle
|
||||
rWrite(0x4000+i*4,(chan[i].outVol==0)?0:255);
|
||||
rWrite(0x4000+i*4,(chan[i].outVol==0)?0:linearCount);
|
||||
chan[i].freqChanged=true;
|
||||
} else {
|
||||
rWrite(0x4000+i*4,(chan[i].envMode<<4)|chan[i].outVol|((chan[i].duty&3)<<6));
|
||||
|
@ -262,7 +262,7 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
//rWrite(16+i*5,chan[i].sweep);
|
||||
}
|
||||
}
|
||||
if (i<2) if (chan[i].std.phaseReset.had) {
|
||||
if (i<3) if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].prevFreq=-1;
|
||||
|
@ -337,14 +337,22 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
goingToLoop=parent->getSample(dacSample)->isLoopable();
|
||||
// write DPCM
|
||||
rWrite(0x4015,15);
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
if (nextDPCMFreq>=0) {
|
||||
rWrite(0x4010,nextDPCMFreq|(goingToLoop?0x40:0));
|
||||
nextDPCMFreq=-1;
|
||||
} else {
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
}
|
||||
rWrite(0x4012,(dpcmAddr>>6)&0xff);
|
||||
rWrite(0x4013,dpcmLen&0xff);
|
||||
rWrite(0x4015,31);
|
||||
dpcmBank=dpcmAddr>>14;
|
||||
}
|
||||
} else {
|
||||
if (dpcmMode) {
|
||||
if (nextDPCMFreq>=0) {
|
||||
rWrite(0x4010,nextDPCMFreq|(goingToLoop?0x40:0));
|
||||
nextDPCMFreq=-1;
|
||||
} else {
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
}
|
||||
}
|
||||
|
@ -353,6 +361,8 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
if (chan[4].keyOn) chan[4].keyOn=false;
|
||||
chan[4].freqChanged=false;
|
||||
}
|
||||
|
||||
nextDPCMFreq=-1;
|
||||
}
|
||||
|
||||
int DivPlatformNES::dispatch(DivCommand c) {
|
||||
|
@ -401,12 +411,17 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
chan[c.chan].furnaceDac=false;
|
||||
if (dpcmMode && !skipRegisterWrites) {
|
||||
unsigned int dpcmAddr=sampleOffDPCM[dacSample];
|
||||
unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4;
|
||||
unsigned int dpcmLen=parent->getSample(dacSample)->lengthDPCM>>4;
|
||||
if (dpcmLen>255) dpcmLen=255;
|
||||
goingToLoop=parent->getSample(dacSample)->isLoopable();
|
||||
// write DPCM
|
||||
rWrite(0x4015,15);
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
if (nextDPCMFreq>=0) {
|
||||
rWrite(0x4010,nextDPCMFreq|(goingToLoop?0x40:0));
|
||||
nextDPCMFreq=-1;
|
||||
} else {
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
}
|
||||
rWrite(0x4012,(dpcmAddr>>6)&0xff);
|
||||
rWrite(0x4013,dpcmLen&0xff);
|
||||
rWrite(0x4015,31);
|
||||
|
@ -434,7 +449,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
if (c.chan==2) {
|
||||
rWrite(0x4000+c.chan*4,0xff);
|
||||
rWrite(0x4000+c.chan*4,linearCount);
|
||||
} else if (!parent->song.brokenOutVol2) {
|
||||
rWrite(0x4000+c.chan*4,(chan[c.chan].envMode<<4)|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
|
||||
}
|
||||
|
@ -466,7 +481,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
}
|
||||
if (chan[c.chan].active) {
|
||||
if (c.chan==2) {
|
||||
rWrite(0x4000+c.chan*4,0xff);
|
||||
rWrite(0x4000+c.chan*4,linearCount);
|
||||
} else {
|
||||
rWrite(0x4000+c.chan*4,(chan[c.chan].envMode<<4)|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
|
||||
}
|
||||
|
@ -542,6 +557,16 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
countMode=c.value;
|
||||
rWrite(0x4017,countMode?0x80:0);
|
||||
break;
|
||||
case DIV_CMD_NES_LINEAR_LENGTH:
|
||||
if (c.chan==2) {
|
||||
linearCount=c.value;
|
||||
if (chan[c.chan].active) {
|
||||
rWrite(0x4000+c.chan*4,(chan[c.chan].outVol==0)?0:linearCount);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].prevFreq=-1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_NES_DMC:
|
||||
rWrite(0x4011,c.value&0x7f);
|
||||
break;
|
||||
|
@ -555,6 +580,14 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
rWrite(0x4013,0);
|
||||
rWrite(0x4015,31);
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_FREQ: {
|
||||
bool goingToLoop=parent->getSample(dacSample)->isLoopable();
|
||||
if (dpcmMode) {
|
||||
nextDPCMFreq=c.value&15;
|
||||
rWrite(0x4010,(c.value&15)|(goingToLoop?0x40:0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
sampleBank=c.value;
|
||||
if (sampleBank>(parent->song.sample.size()/12)) {
|
||||
|
@ -655,9 +688,11 @@ void DivPlatformNES::reset() {
|
|||
dacSample=-1;
|
||||
sampleBank=0;
|
||||
dpcmBank=0;
|
||||
dpcmMode=false;
|
||||
dpcmMode=dpcmModeDefault;
|
||||
goingToLoop=false;
|
||||
countMode=false;
|
||||
nextDPCMFreq=-1;
|
||||
linearCount=255;
|
||||
|
||||
if (useNP) {
|
||||
nes1_NP->Reset();
|
||||
|
@ -709,6 +744,8 @@ void DivPlatformNES::setFlags(const DivConfig& flags) {
|
|||
for (int i=0; i<5; i++) {
|
||||
oscBuf[i]->rate=rate/32;
|
||||
}
|
||||
|
||||
dpcmModeDefault=flags.getBool("dpcmMode",true);
|
||||
}
|
||||
|
||||
void DivPlatformNES::notifyInsDeletion(void* ins) {
|
||||
|
|
|
@ -52,7 +52,10 @@ class DivPlatformNES: public DivDispatch {
|
|||
unsigned char sampleBank;
|
||||
unsigned char writeOscBuf;
|
||||
unsigned char apuType;
|
||||
unsigned char linearCount;
|
||||
signed char nextDPCMFreq;
|
||||
bool dpcmMode;
|
||||
bool dpcmModeDefault;
|
||||
bool dacAntiClickOn;
|
||||
bool useNP;
|
||||
bool goingToLoop;
|
||||
|
|
|
@ -1558,7 +1558,7 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) {
|
|||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
|
||||
if (oplType==759) {
|
||||
if (oplType==759 || chipType==8950) {
|
||||
if (ch>=totalChans+1) return NULL;
|
||||
} else {
|
||||
if (ch>=totalChans) return NULL;
|
||||
|
|
|
@ -409,10 +409,6 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
|
|||
case 8: case 9:
|
||||
chan[c.chan].fixedFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9);
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].fixedFreq=0;
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
|
|
|
@ -42,12 +42,65 @@ void DivPlatformPCMDAC::acquire(short** buf, size_t len) {
|
|||
while (chan[0].audSub>=0x10000) {
|
||||
chan[0].audSub-=0x10000;
|
||||
chan[0].audPos+=((!chan[0].useWave) && chan[0].audDir)?-1:1;
|
||||
if (chan[0].audPos>=(int)chan[0].audLen) {
|
||||
chan[0].audPos%=chan[0].audLen;
|
||||
chan[0].audDir=false;
|
||||
}
|
||||
chan[0].audDat[0]=chan[0].audDat[1];
|
||||
chan[0].audDat[1]=chan[0].audDat[2];
|
||||
chan[0].audDat[2]=chan[0].audDat[3];
|
||||
chan[0].audDat[3]=chan[0].audDat[4];
|
||||
chan[0].audDat[4]=chan[0].audDat[5];
|
||||
chan[0].audDat[5]=chan[0].audDat[6];
|
||||
chan[0].audDat[6]=chan[0].audDat[7];
|
||||
chan[0].audDat[7]=(chan[0].ws.output[chan[0].audPos]-0x80)<<8;
|
||||
}
|
||||
if (chan[0].audPos>=(int)chan[0].audLen) {
|
||||
chan[0].audPos%=chan[0].audLen;
|
||||
chan[0].audDir=false;
|
||||
|
||||
const short s0=chan[0].audDat[0];
|
||||
const short s1=chan[0].audDat[1];
|
||||
const short s2=chan[0].audDat[2];
|
||||
const short s3=chan[0].audDat[3];
|
||||
const short s4=chan[0].audDat[4];
|
||||
const short s5=chan[0].audDat[5];
|
||||
const short s6=chan[0].audDat[6];
|
||||
const short s7=chan[0].audDat[7];
|
||||
|
||||
switch (interp) {
|
||||
case 1: // linear
|
||||
output=s6+((s7-s6)*(chan[0].audSub&0xffff)>>16);
|
||||
break;
|
||||
case 2: { // cubic
|
||||
float* cubicTable=DivFilterTables::getCubicTable();
|
||||
float* t=&cubicTable[((chan[0].audSub&0xffff)>>6)<<2];
|
||||
float result=(float)s4*t[0]+(float)s5*t[1]+(float)s6*t[2]+(float)s7*t[3];
|
||||
if (result<-32768) result=-32768;
|
||||
if (result>32767) result=32767;
|
||||
output=result;
|
||||
break;
|
||||
}
|
||||
case 3: { // sinc
|
||||
float* sincTable=DivFilterTables::getSincTable8();
|
||||
float* t1=&sincTable[(8191-((chan[0].audSub&0xffff)>>3))<<2];
|
||||
float* t2=&sincTable[((chan[0].audSub&0xffff)>>3)<<2];
|
||||
float result=(
|
||||
s0*t2[3]+
|
||||
s1*t2[2]+
|
||||
s2*t2[1]+
|
||||
s3*t2[0]+
|
||||
s4*t1[0]+
|
||||
s5*t1[1]+
|
||||
s6*t1[2]+
|
||||
s7*t1[3]
|
||||
);
|
||||
if (result<-32768) result=-32768;
|
||||
if (result>32767) result=32767;
|
||||
output=result;
|
||||
break;
|
||||
}
|
||||
default: // none
|
||||
output=s7;
|
||||
break;
|
||||
}
|
||||
output=(chan[0].ws.output[chan[0].audPos]-0x80)<<8;
|
||||
} else {
|
||||
DivSample* s=parent->getSample(chan[0].sample);
|
||||
if (s->samples>0) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue