Merge branch 'master' into getSampleMemOffset
This commit is contained in:
commit
9ddcb0bcdc
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
||||||
.vscode/
|
.vscode/
|
||||||
build/
|
build/
|
||||||
clangbuild/
|
clangbuild/
|
||||||
|
oldwinbuild/
|
||||||
nosdl/
|
nosdl/
|
||||||
release/
|
release/
|
||||||
t/
|
t/
|
||||||
|
|
|
@ -139,6 +139,7 @@ option(SHOW_OPEN_ASSETS_MENU_ENTRY "Show option to open built-in assets director
|
||||||
option(CONSOLE_SUBSYSTEM "Build Furnace with Console subsystem on Windows" OFF)
|
option(CONSOLE_SUBSYSTEM "Build Furnace with Console subsystem on Windows" OFF)
|
||||||
option(FORCE_CODEVIEW "Force -gcodeview on MinGW GCC" OFF)
|
option(FORCE_CODEVIEW "Force -gcodeview on MinGW GCC" OFF)
|
||||||
option(FLATPAK_WORKAROUNDS "Enable Flatpak-specific workaround for system file picker" OFF)
|
option(FLATPAK_WORKAROUNDS "Enable Flatpak-specific workaround for system file picker" OFF)
|
||||||
|
option(NO_INTRO "Disable intro animation entirely" OFF)
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
option(FORCE_APPLE_BIN "Force enable binary installation to /bin" OFF)
|
option(FORCE_APPLE_BIN "Force enable binary installation to /bin" OFF)
|
||||||
option(MAKE_BUNDLE "Make a bundle" OFF)
|
option(MAKE_BUNDLE "Make a bundle" OFF)
|
||||||
|
@ -821,6 +822,8 @@ src/engine/export/amigaValidation.cpp
|
||||||
src/engine/export/sapr.cpp
|
src/engine/export/sapr.cpp
|
||||||
src/engine/export/tiuna.cpp
|
src/engine/export/tiuna.cpp
|
||||||
src/engine/export/zsm.cpp
|
src/engine/export/zsm.cpp
|
||||||
|
src/engine/export/ipod.cpp
|
||||||
|
src/engine/export/grub.cpp
|
||||||
|
|
||||||
src/engine/effect/abstract.cpp
|
src/engine/effect/abstract.cpp
|
||||||
src/engine/effect/dummy.cpp
|
src/engine/effect/dummy.cpp
|
||||||
|
@ -879,12 +882,6 @@ src/gui/fonts.cpp
|
||||||
src/gui/fontzlib.cpp
|
src/gui/fontzlib.cpp
|
||||||
|
|
||||||
src/gui/image_icon.cpp
|
src/gui/image_icon.cpp
|
||||||
src/gui/image_talogo.cpp
|
|
||||||
src/gui/image_tachip.cpp
|
|
||||||
src/gui/image_logo.cpp
|
|
||||||
src/gui/image_wordmark.cpp
|
|
||||||
src/gui/image_introbg.cpp
|
|
||||||
src/gui/image_pat.cpp
|
|
||||||
src/gui/image.cpp
|
src/gui/image.cpp
|
||||||
|
|
||||||
src/gui/debug.cpp
|
src/gui/debug.cpp
|
||||||
|
@ -893,8 +890,6 @@ src/gui/fileDialog.cpp
|
||||||
src/gui/intConst.cpp
|
src/gui/intConst.cpp
|
||||||
src/gui/guiConst.cpp
|
src/gui/guiConst.cpp
|
||||||
|
|
||||||
src/gui/introTune.cpp
|
|
||||||
|
|
||||||
src/gui/about.cpp
|
src/gui/about.cpp
|
||||||
src/gui/channels.cpp
|
src/gui/channels.cpp
|
||||||
src/gui/chanOsc.cpp
|
src/gui/chanOsc.cpp
|
||||||
|
@ -914,7 +909,6 @@ src/gui/fmPreview.cpp
|
||||||
src/gui/gradient.cpp
|
src/gui/gradient.cpp
|
||||||
src/gui/grooves.cpp
|
src/gui/grooves.cpp
|
||||||
src/gui/insEdit.cpp
|
src/gui/insEdit.cpp
|
||||||
src/gui/intro.cpp
|
|
||||||
src/gui/log.cpp
|
src/gui/log.cpp
|
||||||
src/gui/memory.cpp
|
src/gui/memory.cpp
|
||||||
src/gui/mixer.cpp
|
src/gui/mixer.cpp
|
||||||
|
@ -951,6 +945,21 @@ src/gui/xyOsc.cpp
|
||||||
src/gui/gui.cpp
|
src/gui/gui.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (NOT NO_INTRO)
|
||||||
|
list(APPEND GUI_SOURCES src/gui/introTune.cpp)
|
||||||
|
list(APPEND GUI_SOURCES src/gui/intro.cpp)
|
||||||
|
list(APPEND GUI_SOURCES
|
||||||
|
src/gui/image_talogo.cpp
|
||||||
|
src/gui/image_tachip.cpp
|
||||||
|
src/gui/image_logo.cpp
|
||||||
|
src/gui/image_wordmark.cpp
|
||||||
|
src/gui/image_introbg.cpp
|
||||||
|
src/gui/image_pat.cpp
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
list(APPEND DEPENDENCIES_DEFINES NO_INTRO)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (WIN32 AND NOT SUPPORT_XP)
|
if (WIN32 AND NOT SUPPORT_XP)
|
||||||
list(APPEND GUI_SOURCES extern/nfd-modified/src/nfd_common.cpp)
|
list(APPEND GUI_SOURCES extern/nfd-modified/src/nfd_common.cpp)
|
||||||
list(APPEND GUI_SOURCES extern/nfd-modified/src/nfd_win.cpp)
|
list(APPEND GUI_SOURCES extern/nfd-modified/src/nfd_win.cpp)
|
||||||
|
|
|
@ -159,6 +159,8 @@ for other operating systems, you may [build the source](#developer-info).
|
||||||
- [documentation](doc/README.md).
|
- [documentation](doc/README.md).
|
||||||
- [frequently asked questions (FAQ)](doc/1-intro/faq.md).
|
- [frequently asked questions (FAQ)](doc/1-intro/faq.md).
|
||||||
- **discussion**: see the [Discussions](https://github.com/tildearrow/furnace/discussions) section, or the [Discord](https://discord.gg/QhA26dXD23).
|
- **discussion**: see the [Discussions](https://github.com/tildearrow/furnace/discussions) section, or the [Discord](https://discord.gg/QhA26dXD23).
|
||||||
|
- these are the only **official** discussion channels for Furnace. **any other places are not official and not managed by me (tildearrow).**
|
||||||
|
- no, there isn't an official Furnace Facebook group. the one that seemingly exists isn't mine.
|
||||||
|
|
||||||
## packages
|
## packages
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||||
private static final String TAG = "SDL";
|
private static final String TAG = "SDL";
|
||||||
private static final int SDL_MAJOR_VERSION = 2;
|
private static final int SDL_MAJOR_VERSION = 2;
|
||||||
private static final int SDL_MINOR_VERSION = 32;
|
private static final int SDL_MINOR_VERSION = 32;
|
||||||
private static final int SDL_MICRO_VERSION = 4;
|
private static final int SDL_MICRO_VERSION = 8;
|
||||||
/*
|
/*
|
||||||
// Display InputType.SOURCE/CLASS of events and devices
|
// Display InputType.SOURCE/CLASS of events and devices
|
||||||
//
|
//
|
||||||
|
|
|
@ -19,7 +19,6 @@ these demo songs are not under the GPL. all rights are reserved to the original
|
||||||
- AquaDoesStuff
|
- AquaDoesStuff
|
||||||
- asikwus
|
- asikwus
|
||||||
- AstralBlue
|
- AstralBlue
|
||||||
- Background2982
|
|
||||||
- battybeats
|
- battybeats
|
||||||
- bbqzzd
|
- bbqzzd
|
||||||
- Bernie
|
- Bernie
|
||||||
|
@ -55,7 +54,7 @@ these demo songs are not under the GPL. all rights are reserved to the original
|
||||||
- Forte
|
- Forte
|
||||||
- Fragmare
|
- Fragmare
|
||||||
- freq-mod
|
- freq-mod
|
||||||
- Fur
|
- Furmilion
|
||||||
- gtr3qq
|
- gtr3qq
|
||||||
- halberd (lordlydumbass)
|
- halberd (lordlydumbass)
|
||||||
- Heckett Heriot
|
- Heckett Heriot
|
||||||
|
@ -63,6 +62,7 @@ these demo songs are not under the GPL. all rights are reserved to the original
|
||||||
- Hortus
|
- Hortus
|
||||||
- ifrit05
|
- ifrit05
|
||||||
- iyatemu
|
- iyatemu
|
||||||
|
- jaezu
|
||||||
- JayBOB18
|
- JayBOB18
|
||||||
- Jimmy-DS
|
- Jimmy-DS
|
||||||
- jvsTSX
|
- jvsTSX
|
||||||
|
@ -71,7 +71,6 @@ these demo songs are not under the GPL. all rights are reserved to the original
|
||||||
- kleeder
|
- kleeder
|
||||||
- Korbo
|
- Korbo
|
||||||
- KungFuFurby
|
- KungFuFurby
|
||||||
- jaezu
|
|
||||||
- Laggy
|
- Laggy
|
||||||
- leejh20
|
- leejh20
|
||||||
- LovelyA72
|
- LovelyA72
|
||||||
|
@ -84,6 +83,7 @@ these demo songs are not under the GPL. all rights are reserved to the original
|
||||||
- Miker
|
- Miker
|
||||||
- Molkirill
|
- Molkirill
|
||||||
- MrCoolDude
|
- MrCoolDude
|
||||||
|
- Mr. Saturn
|
||||||
- NeoWar
|
- NeoWar
|
||||||
- Nerreave
|
- Nerreave
|
||||||
- niffuM
|
- niffuM
|
||||||
|
@ -92,6 +92,7 @@ these demo songs are not under the GPL. all rights are reserved to the original
|
||||||
- Notakin
|
- Notakin
|
||||||
- nwcr
|
- nwcr
|
||||||
- NyaongI
|
- NyaongI
|
||||||
|
- OddPandemonium
|
||||||
- PeyPey
|
- PeyPey
|
||||||
- PichuMario
|
- PichuMario
|
||||||
- pixelated
|
- pixelated
|
||||||
|
@ -129,11 +130,11 @@ these demo songs are not under the GPL. all rights are reserved to the original
|
||||||
- TCORPStudios
|
- TCORPStudios
|
||||||
- Teuthida
|
- Teuthida
|
||||||
- ThaCuber
|
- ThaCuber
|
||||||
|
- The Beesh-Spweesh!
|
||||||
- The Blender Fiddler
|
- The Blender Fiddler
|
||||||
- TheDuccinator
|
- TheDuccinator
|
||||||
- theloredev
|
|
||||||
- The Beesh-Spweesh!
|
|
||||||
- The Goofy-Mouse
|
- The Goofy-Mouse
|
||||||
|
- theloredev
|
||||||
- TheRealHedgehogSonic
|
- TheRealHedgehogSonic
|
||||||
- tildearrow
|
- tildearrow
|
||||||
- tom\_atom
|
- tom\_atom
|
||||||
|
@ -145,7 +146,6 @@ these demo songs are not under the GPL. all rights are reserved to the original
|
||||||
- Wegfrei
|
- Wegfrei
|
||||||
- Xan
|
- Xan
|
||||||
- Yuzugure!
|
- Yuzugure!
|
||||||
- z1cmr40
|
|
||||||
- Zabir
|
- Zabir
|
||||||
- Zaxinoth Digital
|
- Zaxinoth Digital
|
||||||
- Zaxolotl
|
- Zaxolotl
|
||||||
|
|
BIN
demos/blank/Exquisite Invitation.fur
Normal file
BIN
demos/blank/Exquisite Invitation.fur
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
demos/blank/map01.fur
Normal file
BIN
demos/blank/map01.fur
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -53,6 +53,9 @@ the following settings exist:
|
||||||
- **chips to export**: select which chips are going to be exported.
|
- **chips to export**: select which chips are going to be exported.
|
||||||
- due to VGM format limitations, you can only select up to two of each chip type.
|
- due to VGM format limitations, you can only select up to two of each chip type.
|
||||||
- some chips will not be available, either because VGM doesn't support these yet, or because you selected an old format version.
|
- some chips will not be available, either because VGM doesn't support these yet, or because you selected an old format version.
|
||||||
|
- **speed drift compensation**:
|
||||||
|
- **none**: normal export.
|
||||||
|
- **DeadFish VgmPlay (1.02×)**: adjusts speed to account for inaccuracy in [VgmPlay](https://www.mjsstuf.x10host.com/pages/vgmPlay/vgmPlay.htm), a Sega Genesis VGM player by DeadFish.
|
||||||
|
|
||||||
## ZSM
|
## ZSM
|
||||||
|
|
||||||
|
@ -72,8 +75,10 @@ the following settings are available:
|
||||||
|
|
||||||
depending on the system, this option may appear to allow you to export your song to a working ROM image or code that can be built into one. export options are explained in the system's accompanying documentation.
|
depending on the system, this option may appear to allow you to export your song to a working ROM image or code that can be built into one. export options are explained in the system's accompanying documentation.
|
||||||
|
|
||||||
currently, only one system can be exported this way:
|
the following formats and systems are supported:
|
||||||
- [Atari 2600 (TIunA)](../7-systems/tia.md)
|
- TIunA assembly, using [Atari 2600 (with software pitch driver)](../7-systems/tia.md).
|
||||||
|
- iPod .tone alarm, using [PC Speaker](../7-systems/pcspkr.md).
|
||||||
|
- GRUB_INIT_TUNE, using [PC Speaker](../7-systems/pcspkr.md).
|
||||||
|
|
||||||
## text
|
## text
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ the keys in the "Global hotkeys" section can be used in any window, although not
|
||||||
| Samples (Palette) | — |
|
| Samples (Palette) | — |
|
||||||
| | |
|
| | |
|
||||||
| **Note input** | |
|
| **Note input** | |
|
||||||
| _see "note input" section after table_ | |
|
| _see [settings](./settings.md#note-input)._ | |
|
||||||
| | |
|
| | |
|
||||||
| **Pattern** | |
|
| **Pattern** | |
|
||||||
| Transpose (+1) | `Ctrl-F2` |
|
| Transpose (+1) | `Ctrl-F2` |
|
||||||
|
|
|
@ -356,6 +356,10 @@ below all the binds, select a key from the dropdown list to add it. it will appe
|
||||||
- **No**
|
- **No**
|
||||||
- **Yes**
|
- **Yes**
|
||||||
- **Inverted**
|
- **Inverted**
|
||||||
|
- **How many steps to move with each scroll wheel step?**: only appears when "Move cursor with scroll wheel" is set to "Yes" or "Inverted".
|
||||||
|
- **One**
|
||||||
|
- **Edit Step**
|
||||||
|
- **Coarse Step**
|
||||||
|
|
||||||
### Assets
|
### Assets
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ Bifurcator instrument editor consists of these macros:
|
||||||
|
|
||||||
- **Volume**: volume sequence.
|
- **Volume**: volume sequence.
|
||||||
- **Arpeggio**: pitch sequence.
|
- **Arpeggio**: pitch sequence.
|
||||||
- **Parametet**: set parameter of logistic map function.
|
- **Parameter**: set parameter of logistic map function.
|
||||||
- **Panning (left)**: output level for left channel.
|
- **Panning (left)**: output level for left channel.
|
||||||
- **Panning (right)**: output level for right channel.
|
- **Panning (right)**: output level for right channel.
|
||||||
- **Pitch**: fine pitch.
|
- **Pitch**: fine pitch.
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 237 KiB |
|
@ -34,6 +34,17 @@ ha! effects...
|
||||||
|
|
||||||
this chip uses the [Beeper](../4-instrument/beeper.md) instrument editor.
|
this chip uses the [Beeper](../4-instrument/beeper.md) instrument editor.
|
||||||
|
|
||||||
|
## ROM export
|
||||||
|
|
||||||
|
two ROM export options exist:
|
||||||
|
|
||||||
|
- **iPod .tone alarm**: with the iPod _in disk mode,_ drag the export file into the `iPod_Control/Tones` folder.
|
||||||
|
- **GRUB_INIT_TUNE**: use with the GRUB bootloader.
|
||||||
|
- into the file `/etc/default/grub` add the following line with the text output copied and pasted where `text` is:\
|
||||||
|
`GRUB_INIT_TUNE="text"`\
|
||||||
|
then regenerate GRUB config.
|
||||||
|
- **export binary file**: creates a binary file instead of text. in either the GRUB shell or the GRUB config file, use the `play` command followed by the exported file's name.
|
||||||
|
|
||||||
## chip config
|
## chip config
|
||||||
|
|
||||||
the following options are available in the Chip Manager window:
|
the following options are available in the Chip Manager window:
|
||||||
|
|
BIN
doc/7-systems/status-VBoy-env-error.png
Normal file
BIN
doc/7-systems/status-VBoy-env-error.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 730 B |
BIN
doc/7-systems/status-VBoy-env-none.png
Normal file
BIN
doc/7-systems/status-VBoy-env-none.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 553 B |
BIN
doc/7-systems/status-VBoy-env-warn.png
Normal file
BIN
doc/7-systems/status-VBoy-env-warn.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 754 B |
|
@ -22,12 +22,15 @@ additionally, channel 5 offers a modulation/sweep unit. the former is similar to
|
||||||
- `y` sets the speed and direction.
|
- `y` sets the speed and direction.
|
||||||
- `0-7`: down
|
- `0-7`: down
|
||||||
- `8-F`: up
|
- `8-F`: up
|
||||||
|
- IMPORTANT: if the envelope ends, you will not be able to trigger another envelope until you reset the channel's phase! (this is a Virtual Boy hardware bug)
|
||||||
|
- setting a phase reset macro at the beginning will do.
|
||||||
- `13xy`: **setup sweep.**
|
- `13xy`: **setup sweep.**
|
||||||
- `x` sets the speed.
|
- `x` sets the speed.
|
||||||
- `0` and `8` are "speed 0" - sweep is ineffective.
|
- `0` and `8` are "speed 0" - sweep is ineffective.
|
||||||
- `y` sets the shift (`0` to `7`).
|
- `y` sets the shift (`0` to `7`).
|
||||||
- `8` and higher will mute the channel.
|
- `8` and higher will mute the channel.
|
||||||
- only in channel 5.
|
- only in channel 5.
|
||||||
|
- if modulation ends, you cannot sweep until the next phase reset.
|
||||||
- `14xy`: **setup modulation.**
|
- `14xy`: **setup modulation.**
|
||||||
- `x` determines whether it's enabled or not.
|
- `x` determines whether it's enabled or not.
|
||||||
- 0: disabled
|
- 0: disabled
|
||||||
|
@ -38,6 +41,8 @@ additionally, channel 5 offers a modulation/sweep unit. the former is similar to
|
||||||
- `0` and `8` are "speed 0" - modulation is ineffective.
|
- `0` and `8` are "speed 0" - modulation is ineffective.
|
||||||
- no, you can't really do Yamaha FM using this.
|
- no, you can't really do Yamaha FM using this.
|
||||||
- only in channel 5.
|
- only in channel 5.
|
||||||
|
- IMPORTANT: if the modulation ends, you will not be able to trigger another envelope until you reset the channel's phase! (this is a Virtual Boy hardware bug)
|
||||||
|
- setting a phase reset macro at the beginning will do.
|
||||||
- `15xx`: **set modulation wave.**
|
- `15xx`: **set modulation wave.**
|
||||||
- `xx` points to a wavetable. range is `0` to `FF`.
|
- `xx` points to a wavetable. range is `0` to `FF`.
|
||||||
- this is an alternative to setting the modulation wave through the instrument.
|
- this is an alternative to setting the modulation wave through the instrument.
|
||||||
|
@ -46,6 +51,14 @@ additionally, channel 5 offers a modulation/sweep unit. the former is similar to
|
||||||
|
|
||||||
this chip uses the [Virtual Boy](../4-instrument/virtual-boy.md) instrument editor.
|
this chip uses the [Virtual Boy](../4-instrument/virtual-boy.md) instrument editor.
|
||||||
|
|
||||||
|
## channel status
|
||||||
|
|
||||||
|
the following icons are displayed when channel status is enabled in the pattern view:
|
||||||
|
|
||||||
|
-  hardware envelope is disabled or running OK
|
||||||
|
-  hardware envelope has finished - attempting to write a new envelope won't work without phase reset!
|
||||||
|
-  can't start hardware envelope - please reset phase
|
||||||
|
|
||||||
## chip config
|
## chip config
|
||||||
|
|
||||||
the following options are available in the Chip Manager window:
|
the following options are available in the Chip Manager window:
|
||||||
|
|
2
extern/SDL
vendored
2
extern/SDL
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit d9a31df26d9bb91486db15c3732e8fa04c4449e2
|
Subproject commit 6371fd44c8a3cdfb3166b36d4798f5daeca2eeee
|
BIN
instruments/SAA/EnvBass.fui
Normal file
BIN
instruments/SAA/EnvBass.fui
Normal file
Binary file not shown.
BIN
instruments/SAA/RanedomHatC.fui
Normal file
BIN
instruments/SAA/RanedomHatC.fui
Normal file
Binary file not shown.
BIN
instruments/SAA/RanedomKick.fui
Normal file
BIN
instruments/SAA/RanedomKick.fui
Normal file
Binary file not shown.
BIN
instruments/SAA/RanedomSnare.fui
Normal file
BIN
instruments/SAA/RanedomSnare.fui
Normal file
Binary file not shown.
|
@ -397,7 +397,7 @@ size | description
|
||||||
1 | legacy "always set volume" behavior (>=191)
|
1 | legacy "always set volume" behavior (>=191)
|
||||||
1 | legacy sample offset effect (>=200)
|
1 | legacy sample offset effect (>=200)
|
||||||
--- | **speed pattern of first song** (>=139)
|
--- | **speed pattern of first song** (>=139)
|
||||||
1 | length of speed pattern (fail if this is lower than 0 or higher than 16)
|
1 | length of speed pattern (fail if this is lower than 1 or higher than 16)
|
||||||
16 | speed pattern (this overrides speed 1 and speed 2 settings)
|
16 | speed pattern (this overrides speed 1 and speed 2 settings)
|
||||||
--- | **groove list** (>=139)
|
--- | **groove list** (>=139)
|
||||||
1 | number of entries
|
1 | number of entries
|
||||||
|
@ -478,7 +478,7 @@ size | description
|
||||||
S?? | channel short names
|
S?? | channel short names
|
||||||
| - same as above
|
| - same as above
|
||||||
--- | **speed pattern** (>=139)
|
--- | **speed pattern** (>=139)
|
||||||
1 | length of speed pattern (fail if this is lower than 0 or higher than 16)
|
1 | length of speed pattern (fail if this is lower than 1 or higher than 16)
|
||||||
16 | speed pattern (this overrides speed 1 and speed 2 settings)
|
16 | speed pattern (this overrides speed 1 and speed 2 settings)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
30817
po/furnace.pot
30817
po/furnace.pot
File diff suppressed because it is too large
Load diff
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.
34763
po/pt_BR.po
34763
po/pt_BR.po
File diff suppressed because it is too large
Load diff
31503
po/zh_HK.po
31503
po/zh_HK.po
File diff suppressed because it is too large
Load diff
|
@ -17,7 +17,7 @@ fi
|
||||||
cd winbuild
|
cd winbuild
|
||||||
|
|
||||||
# TODO: potential Arch-ism?
|
# 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" -DWITH_LOCALE=ON -DUSE_MOMO=ON -DUSE_BACKWARD=ON -DFORCE_CODEVIEW=ON -DSUPPORT_VISTA=ON .. || exit 1
|
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" -DWITH_LOCALE=ON -DUSE_MOMO=ON -DUSE_BACKWARD=ON -DFORCE_CODEVIEW=OFF -DSUPPORT_VISTA=ON .. || exit 1
|
||||||
make -j8 || exit 1
|
make -j8 || exit 1
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|
|
@ -16,7 +16,7 @@ fi
|
||||||
cd winCbuild
|
cd winCbuild
|
||||||
|
|
||||||
# TODO: potential Arch-ism?
|
# 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" -DUSE_BACKWARD=ON -DCONSOLE_SUBSYSTEM=ON -DWITH_LOCALE=ON -DUSE_MOMO=ON -DFORCE_CODEVIEW=ON .. || exit 1
|
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" -DUSE_BACKWARD=ON -DCONSOLE_SUBSYSTEM=ON -DWITH_LOCALE=ON -DUSE_MOMO=ON -DFORCE_CODEVIEW=OFF .. || exit 1
|
||||||
make -j8 || exit 1
|
make -j8 || exit 1
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|
|
@ -16,7 +16,7 @@ fi
|
||||||
cd xpbuild
|
cd xpbuild
|
||||||
|
|
||||||
# TODO: potential Arch-ism?
|
# 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" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON -DWITH_RENDER_DX11=OFF -DSDL_SSE=OFF -DSDL_SSE2=OFF -DSDL_SSE3=OFF -DENABLE_SSE=OFF -DENABLE_SSE2=OFF -DENABLE_AVX=OFF -DENABLE_AVX2=OFF -DUSE_BACKWARD=OFF -DCONSOLE_SUBSYSTEM=OFF -DWITH_LOCALE=ON -DUSE_MOMO=ON -DFORCE_CODEVIEW=OFF .. || exit 1
|
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" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON -DWITH_RENDER_DX11=OFF -DSDL_SSE=OFF -DSDL_SSE2=OFF -DSDL_SSE3=OFF -DENABLE_SSE=OFF -DENABLE_SSE2=OFF -DENABLE_AVX=OFF -DENABLE_AVX2=OFF -DUSE_BACKWARD=OFF -DCONSOLE_SUBSYSTEM=OFF -DWITH_LOCALE=ON -DUSE_MOMO=ON -DFORCE_CODEVIEW=OFF -DNO_INTRO=ON .. || exit 1
|
||||||
make -j8 || exit 1
|
make -j8 || exit 1
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|
|
@ -193,6 +193,4 @@ void DivEngine::factoryReset() {
|
||||||
if (i>0) path+=fmt::sprintf(".%d",i);
|
if (i>0) path+=fmt::sprintf(".%d",i);
|
||||||
deleteFile(path.c_str());
|
deleteFile(path.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,6 +564,8 @@ struct DivChannelModeHints {
|
||||||
// - 18: inc linear
|
// - 18: inc linear
|
||||||
// - 19: inc bent
|
// - 19: inc bent
|
||||||
// - 20: direct
|
// - 20: direct
|
||||||
|
// - 21: warning
|
||||||
|
// - 22: error
|
||||||
unsigned char type[4];
|
unsigned char type[4];
|
||||||
// up to 4
|
// up to 4
|
||||||
unsigned char count;
|
unsigned char count;
|
||||||
|
|
|
@ -3847,7 +3847,7 @@ bool DivEngine::initAudioBackend() {
|
||||||
if (audioEngine==DIV_AUDIO_SDL) {
|
if (audioEngine==DIV_AUDIO_SDL) {
|
||||||
String audioDriver=getConfString("sdlAudioDriver","");
|
String audioDriver=getConfString("sdlAudioDriver","");
|
||||||
if (!audioDriver.empty()) {
|
if (!audioDriver.empty()) {
|
||||||
SDL_SetHint("SDL_HINT_AUDIODRIVER",audioDriver.c_str());
|
SDL_SetHint(SDL_HINT_AUDIODRIVER,audioDriver.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -4072,7 +4072,7 @@ bool DivEngine::preInit(bool noSafeMode) {
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
String audioDriver=getConfString("sdlAudioDriver","");
|
String audioDriver=getConfString("sdlAudioDriver","");
|
||||||
if (!audioDriver.empty()) {
|
if (!audioDriver.empty()) {
|
||||||
SDL_SetHint("SDL_HINT_AUDIODRIVER",audioDriver.c_str());
|
SDL_SetHint(SDL_HINT_AUDIODRIVER,audioDriver.c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,8 @@ class DivWorkPool;
|
||||||
|
|
||||||
#define DIV_UNSTABLE
|
#define DIV_UNSTABLE
|
||||||
|
|
||||||
#define DIV_VERSION "dev231"
|
#define DIV_VERSION "dev232"
|
||||||
#define DIV_ENGINE_VERSION 231
|
#define DIV_ENGINE_VERSION 232
|
||||||
// for imports
|
// for imports
|
||||||
#define DIV_VERSION_MOD 0xff01
|
#define DIV_VERSION_MOD 0xff01
|
||||||
#define DIV_VERSION_FC 0xff02
|
#define DIV_VERSION_FC 0xff02
|
||||||
|
@ -577,13 +577,13 @@ class DivEngine {
|
||||||
DivWorkPool* renderPool;
|
DivWorkPool* renderPool;
|
||||||
|
|
||||||
// MIDI stuff
|
// MIDI stuff
|
||||||
std::function<int(const TAMidiMessage&)> midiCallback=[](const TAMidiMessage&) -> int {return -2;};
|
std::function<int(const TAMidiMessage&)> midiCallback=[](const TAMidiMessage&) -> int {return -3;};
|
||||||
|
|
||||||
void processRowPre(int i);
|
void processRowPre(int i);
|
||||||
void processRow(int i, bool afterDelay);
|
void processRow(int i, bool afterDelay);
|
||||||
void nextOrder();
|
void nextOrder();
|
||||||
void nextRow();
|
void nextRow();
|
||||||
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream, bool* sampleStoppable, bool dpcm07, DivDispatch** writeNES);
|
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream, bool* sampleStoppable, bool dpcm07, DivDispatch** writeNES, int rateCorrection);
|
||||||
// returns true if end of song.
|
// returns true if end of song.
|
||||||
bool nextTick(bool noAccum=false, bool inhibitLowLat=false);
|
bool nextTick(bool noAccum=false, bool inhibitLowLat=false);
|
||||||
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);
|
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);
|
||||||
|
@ -675,6 +675,8 @@ class DivEngine {
|
||||||
friend class DivExportSAPR;
|
friend class DivExportSAPR;
|
||||||
friend class DivExportTiuna;
|
friend class DivExportTiuna;
|
||||||
friend class DivExportZSM;
|
friend class DivExportZSM;
|
||||||
|
friend class DivExportiPod;
|
||||||
|
friend class DivExportGRUB;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DivSong song;
|
DivSong song;
|
||||||
|
@ -726,7 +728,7 @@ class DivEngine {
|
||||||
// - x to add x+1 ticks of trailing
|
// - x to add x+1 ticks of trailing
|
||||||
// - -1 to auto-determine trailing
|
// - -1 to auto-determine trailing
|
||||||
// - -2 to add a whole loop of trailing
|
// - -2 to add a whole loop of trailing
|
||||||
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false, int trailingTicks=-1, bool dpcm07=false);
|
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false, int trailingTicks=-1, bool dpcm07=false, int correctedRate=44100);
|
||||||
// dump to TIunA.
|
// dump to TIunA.
|
||||||
SafeWriter* saveTiuna(const bool* sysToExport, const char* baseLabel, int firstBankSize, int otherBankSize);
|
SafeWriter* saveTiuna(const bool* sysToExport, const char* baseLabel, int firstBankSize, int otherBankSize);
|
||||||
// dump command stream.
|
// dump command stream.
|
||||||
|
@ -1336,7 +1338,7 @@ class DivEngine {
|
||||||
void setMidiVolExp(float value);
|
void setMidiVolExp(float value);
|
||||||
|
|
||||||
// set MIDI input callback
|
// set MIDI input callback
|
||||||
// if the specified function returns -2, note feedback will be inhibited.
|
// if the specified function returns -3, note feedback will be inhibited.
|
||||||
void setMidiCallback(std::function<int(const TAMidiMessage&)> what);
|
void setMidiCallback(std::function<int(const TAMidiMessage&)> what);
|
||||||
|
|
||||||
// send MIDI message
|
// send MIDI message
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#include "export/sapr.h"
|
#include "export/sapr.h"
|
||||||
#include "export/tiuna.h"
|
#include "export/tiuna.h"
|
||||||
#include "export/zsm.h"
|
#include "export/zsm.h"
|
||||||
|
#include "export/ipod.h"
|
||||||
|
#include "export/grub.h"
|
||||||
|
|
||||||
DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) {
|
DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) {
|
||||||
DivROMExport* exporter=NULL;
|
DivROMExport* exporter=NULL;
|
||||||
|
@ -39,6 +41,12 @@ DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) {
|
||||||
case DIV_ROM_SAP_R:
|
case DIV_ROM_SAP_R:
|
||||||
exporter=new DivExportSAPR;
|
exporter=new DivExportSAPR;
|
||||||
break;
|
break;
|
||||||
|
case DIV_ROM_IPOD:
|
||||||
|
exporter=new DivExportiPod;
|
||||||
|
break;
|
||||||
|
case DIV_ROM_GRUB:
|
||||||
|
exporter=new DivExportGRUB;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
exporter=new DivROMExport;
|
exporter=new DivROMExport;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -32,6 +32,8 @@ enum DivROMExportOptions {
|
||||||
DIV_ROM_ZSM,
|
DIV_ROM_ZSM,
|
||||||
DIV_ROM_TIUNA,
|
DIV_ROM_TIUNA,
|
||||||
DIV_ROM_SAP_R,
|
DIV_ROM_SAP_R,
|
||||||
|
DIV_ROM_IPOD,
|
||||||
|
DIV_ROM_GRUB,
|
||||||
|
|
||||||
DIV_ROM_MAX
|
DIV_ROM_MAX
|
||||||
};
|
};
|
||||||
|
|
204
src/engine/export/grub.cpp
Normal file
204
src/engine/export/grub.cpp
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2025 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 "grub.h"
|
||||||
|
#include "../engine.h"
|
||||||
|
#include "../ta-log.h"
|
||||||
|
#include <fmt/printf.h>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
void DivExportGRUB::run() {
|
||||||
|
bool grubExportBin=conf.getBool("exportBin",false);
|
||||||
|
|
||||||
|
int BEEPER=-1;
|
||||||
|
int IGNORED=0;
|
||||||
|
|
||||||
|
// Locate system index.
|
||||||
|
for (int i=0; i<e->song.systemLen; i++) {
|
||||||
|
if (e->song.system[i] == DIV_SYSTEM_PCSPKR) {
|
||||||
|
if (BEEPER>=0) {
|
||||||
|
IGNORED++;
|
||||||
|
logAppendf("Ignoring duplicate Beeper id %d",i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
BEEPER=i;
|
||||||
|
logAppendf("PC Speaker detected as chip id %d",i);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
IGNORED++;
|
||||||
|
logAppendf("Ignoring chip id %d, system id %d",i,(int)e->song.system[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (BEEPER<0) {
|
||||||
|
logAppendf("ERROR: Could not find PC Speaker/Beeper");
|
||||||
|
failed=true;
|
||||||
|
running=false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (IGNORED>0) {
|
||||||
|
logAppendf("WARNING: GRUB export ignoring unsup sys count: %d",IGNORED);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t tickCount=0;
|
||||||
|
|
||||||
|
e->stop();
|
||||||
|
e->repeatPattern=false;
|
||||||
|
e->setOrder(0);
|
||||||
|
|
||||||
|
logAppend("playing and logging register writes...");
|
||||||
|
|
||||||
|
int oldFreq = 0;
|
||||||
|
int freq = 0;
|
||||||
|
|
||||||
|
e->synchronizedSoft([&]() {
|
||||||
|
double origRate = e->got.rate;
|
||||||
|
double rate = MIN(e->curSubSong->hz,1000.0);
|
||||||
|
logAppendf("export rate is %d hz",(int)rate);
|
||||||
|
int tempo = (int)(60000.0/(1000.0/rate));
|
||||||
|
e->got.rate=rate;
|
||||||
|
|
||||||
|
// Determine loop point.
|
||||||
|
int loopOrder=0;
|
||||||
|
int loopRow=0;
|
||||||
|
int loopEnd=0;
|
||||||
|
e->walkSong(loopOrder,loopRow,loopEnd);
|
||||||
|
logAppendf("loop point: %d %d",loopOrder,loopRow);
|
||||||
|
e->warnings="";
|
||||||
|
|
||||||
|
auto w = new SafeWriter;
|
||||||
|
w->init();
|
||||||
|
|
||||||
|
// Reset the playback state.
|
||||||
|
e->curOrder=0;
|
||||||
|
e->freelance=false;
|
||||||
|
e->playing=false;
|
||||||
|
e->extValuePresent=false;
|
||||||
|
e->remainingLoops=-1;
|
||||||
|
|
||||||
|
e->disCont[BEEPER].dispatch->toggleRegisterDump(true);
|
||||||
|
|
||||||
|
// Prepare to write song data.
|
||||||
|
e->playSub(false);
|
||||||
|
bool done=false;
|
||||||
|
|
||||||
|
logAppend("writing data...");
|
||||||
|
progress[0].amount=0.15f;
|
||||||
|
|
||||||
|
int wait_tempo = 0;
|
||||||
|
if (grubExportBin)
|
||||||
|
w->writeI(tempo); // write tempo
|
||||||
|
else
|
||||||
|
w->writeText(fmt::sprintf("%d",tempo)); // write tempo
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
if (e->nextTick(false,true) || !e->playing) {
|
||||||
|
done=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get register dumps
|
||||||
|
uint8_t* regPool = e->disCont[BEEPER].dispatch->getRegisterPool();
|
||||||
|
int chipClock = e->disCont[BEEPER].dispatch->chipClock;
|
||||||
|
freq = (int)(regPool[0]|(regPool[1]<<8));
|
||||||
|
if (freq > 0) freq = chipClock/freq;
|
||||||
|
|
||||||
|
// write wait
|
||||||
|
tickCount++;
|
||||||
|
int totalWait=e->cycles;
|
||||||
|
if (totalWait>0 && !done) {
|
||||||
|
while (totalWait) {
|
||||||
|
wait_tempo++;
|
||||||
|
if (freq != oldFreq || wait_tempo == 65535) {
|
||||||
|
if (grubExportBin) {
|
||||||
|
w->writeS(oldFreq); // pitch
|
||||||
|
w->writeS(wait_tempo); // duration
|
||||||
|
} else {
|
||||||
|
w->writeText(fmt::sprintf(" %d %d", oldFreq, wait_tempo));
|
||||||
|
}
|
||||||
|
oldFreq = freq;
|
||||||
|
wait_tempo = 0;
|
||||||
|
}
|
||||||
|
totalWait--;
|
||||||
|
tickCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!grubExportBin) w->writeText(fmt::sprintf("\n")); // end song
|
||||||
|
// end of song
|
||||||
|
|
||||||
|
// done - close out.
|
||||||
|
e->got.rate=origRate;
|
||||||
|
e->disCont[BEEPER].dispatch->getRegisterWrites().clear();
|
||||||
|
e->disCont[BEEPER].dispatch->toggleRegisterDump(false);
|
||||||
|
|
||||||
|
e->remainingLoops=-1;
|
||||||
|
e->playing=false;
|
||||||
|
e->freelance=false;
|
||||||
|
e->extValuePresent=false;
|
||||||
|
|
||||||
|
output.push_back(DivROMExportOutput(grubExportBin?"export.bin":"export.txt",w));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
progress[0].amount=1.0f;
|
||||||
|
|
||||||
|
logAppend("finished!");
|
||||||
|
|
||||||
|
running=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivExportGRUB::go(DivEngine* eng) {
|
||||||
|
progress[0].name="Progress";
|
||||||
|
progress[0].amount=0.0f;
|
||||||
|
|
||||||
|
e=eng;
|
||||||
|
running=true;
|
||||||
|
failed=false;
|
||||||
|
mustAbort=false;
|
||||||
|
exportThread=new std::thread(&DivExportGRUB::run,this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivExportGRUB::wait() {
|
||||||
|
if (exportThread!=NULL) {
|
||||||
|
logV("waiting for export thread...");
|
||||||
|
exportThread->join();
|
||||||
|
delete exportThread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivExportGRUB::abort() {
|
||||||
|
mustAbort=true;
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivExportGRUB::isRunning() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivExportGRUB::hasFailed() {
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
DivROMExportProgress DivExportGRUB::getProgress(int index) {
|
||||||
|
if (index<0 || index>1) return progress[1];
|
||||||
|
return progress[index];
|
||||||
|
}
|
38
src/engine/export/grub.h
Normal file
38
src/engine/export/grub.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2025 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 "../export.h"
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class DivExportGRUB: public DivROMExport {
|
||||||
|
DivEngine* e;
|
||||||
|
std::thread* exportThread;
|
||||||
|
DivROMExportProgress progress[2];
|
||||||
|
bool running, failed, mustAbort;
|
||||||
|
void run();
|
||||||
|
public:
|
||||||
|
bool go(DivEngine* e);
|
||||||
|
bool isRunning();
|
||||||
|
bool hasFailed();
|
||||||
|
void abort();
|
||||||
|
void wait();
|
||||||
|
DivROMExportProgress getProgress(int index=0);
|
||||||
|
~DivExportGRUB() {}
|
||||||
|
};
|
194
src/engine/export/ipod.cpp
Normal file
194
src/engine/export/ipod.cpp
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2025 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// thanks asiekierka! (I used your SAP-R export code as a base for this)
|
||||||
|
|
||||||
|
#include "ipod.h"
|
||||||
|
#include "../engine.h"
|
||||||
|
#include "../ta-log.h"
|
||||||
|
#include <fmt/printf.h>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
void DivExportiPod::run() {
|
||||||
|
int BEEPER=-1;
|
||||||
|
int IGNORED=0;
|
||||||
|
|
||||||
|
// Locate system index.
|
||||||
|
for (int i=0; i<e->song.systemLen; i++) {
|
||||||
|
if (e->song.system[i] == DIV_SYSTEM_PCSPKR) {
|
||||||
|
if (BEEPER>=0) {
|
||||||
|
IGNORED++;
|
||||||
|
logAppendf("Ignoring duplicate Beeper id %d",i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
BEEPER=i;
|
||||||
|
logAppendf("PC Speaker detected as chip id %d",i);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
IGNORED++;
|
||||||
|
logAppendf("Ignoring chip id %d, system id %d",i,(int)e->song.system[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (BEEPER<0) {
|
||||||
|
logAppendf("ERROR: Could not find PC Speaker/Beeper");
|
||||||
|
failed=true;
|
||||||
|
running=false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (IGNORED>0) {
|
||||||
|
logAppendf("WARNING: tone export ignoring unsup sys count: %d",IGNORED);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t tickCount=0;
|
||||||
|
|
||||||
|
double rate = 1000.0;
|
||||||
|
|
||||||
|
e->stop();
|
||||||
|
e->repeatPattern=false;
|
||||||
|
e->setOrder(0);
|
||||||
|
|
||||||
|
logAppend("playing and logging register writes...");
|
||||||
|
|
||||||
|
int oldFreq = 0;
|
||||||
|
int freq = 0;
|
||||||
|
|
||||||
|
e->synchronizedSoft([&]() {
|
||||||
|
double origRate = e->got.rate;
|
||||||
|
e->got.rate=rate;
|
||||||
|
|
||||||
|
// Determine loop point.
|
||||||
|
int loopOrder=0;
|
||||||
|
int loopRow=0;
|
||||||
|
int loopEnd=0;
|
||||||
|
e->walkSong(loopOrder,loopRow,loopEnd);
|
||||||
|
logAppendf("loop point: %d %d",loopOrder,loopRow);
|
||||||
|
e->warnings="";
|
||||||
|
|
||||||
|
auto w = new SafeWriter;
|
||||||
|
w->init();
|
||||||
|
|
||||||
|
w->writeText(fmt::sprintf("%s\n", e->song.name));
|
||||||
|
|
||||||
|
// Reset the playback state.
|
||||||
|
e->curOrder=0;
|
||||||
|
e->freelance=false;
|
||||||
|
e->playing=false;
|
||||||
|
e->extValuePresent=false;
|
||||||
|
e->remainingLoops=-1;
|
||||||
|
|
||||||
|
e->disCont[BEEPER].dispatch->toggleRegisterDump(true);
|
||||||
|
|
||||||
|
// Prepare to write song data.
|
||||||
|
e->playSub(false);
|
||||||
|
bool done=false;
|
||||||
|
|
||||||
|
logAppend("writing data...");
|
||||||
|
progress[0].amount=0.15f;
|
||||||
|
|
||||||
|
int wait_ms = 0;
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
if (e->nextTick(false,true) || !e->playing) {
|
||||||
|
done=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get register dumps
|
||||||
|
uint8_t* regPool = e->disCont[BEEPER].dispatch->getRegisterPool();
|
||||||
|
int chipClock = e->disCont[BEEPER].dispatch->chipClock;
|
||||||
|
freq = (int)(regPool[0]|(regPool[1]<<8));
|
||||||
|
if (freq > 0) freq = chipClock/freq;
|
||||||
|
|
||||||
|
// write wait
|
||||||
|
tickCount++;
|
||||||
|
int totalWait=e->cycles;
|
||||||
|
if (totalWait>0 && !done) {
|
||||||
|
while (totalWait) {
|
||||||
|
wait_ms++;
|
||||||
|
if (freq != oldFreq) {
|
||||||
|
w->writeText(fmt::sprintf("%d %d\n", oldFreq, wait_ms));
|
||||||
|
oldFreq = freq;
|
||||||
|
wait_ms = 0;
|
||||||
|
}
|
||||||
|
totalWait--;
|
||||||
|
tickCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end of song
|
||||||
|
|
||||||
|
// done - close out.
|
||||||
|
e->got.rate=origRate;
|
||||||
|
e->disCont[BEEPER].dispatch->getRegisterWrites().clear();
|
||||||
|
e->disCont[BEEPER].dispatch->toggleRegisterDump(false);
|
||||||
|
|
||||||
|
e->remainingLoops=-1;
|
||||||
|
e->playing=false;
|
||||||
|
e->freelance=false;
|
||||||
|
e->extValuePresent=false;
|
||||||
|
|
||||||
|
output.push_back(DivROMExportOutput("export.tone",w));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
progress[0].amount=1.0f;
|
||||||
|
|
||||||
|
logAppend("finished!");
|
||||||
|
|
||||||
|
running=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivExportiPod::go(DivEngine* eng) {
|
||||||
|
progress[0].name="Progress";
|
||||||
|
progress[0].amount=0.0f;
|
||||||
|
|
||||||
|
e=eng;
|
||||||
|
running=true;
|
||||||
|
failed=false;
|
||||||
|
mustAbort=false;
|
||||||
|
exportThread=new std::thread(&DivExportiPod::run,this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivExportiPod::wait() {
|
||||||
|
if (exportThread!=NULL) {
|
||||||
|
logV("waiting for export thread...");
|
||||||
|
exportThread->join();
|
||||||
|
delete exportThread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivExportiPod::abort() {
|
||||||
|
mustAbort=true;
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivExportiPod::isRunning() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivExportiPod::hasFailed() {
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
DivROMExportProgress DivExportiPod::getProgress(int index) {
|
||||||
|
if (index<0 || index>1) return progress[1];
|
||||||
|
return progress[index];
|
||||||
|
}
|
38
src/engine/export/ipod.h
Normal file
38
src/engine/export/ipod.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2025 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 "../export.h"
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class DivExportiPod: public DivROMExport {
|
||||||
|
DivEngine* e;
|
||||||
|
std::thread* exportThread;
|
||||||
|
DivROMExportProgress progress[2];
|
||||||
|
bool running, failed, mustAbort;
|
||||||
|
void run();
|
||||||
|
public:
|
||||||
|
bool go(DivEngine* e);
|
||||||
|
bool isRunning();
|
||||||
|
bool hasFailed();
|
||||||
|
void abort();
|
||||||
|
void wait();
|
||||||
|
DivROMExportProgress getProgress(int index=0);
|
||||||
|
~DivExportiPod() {}
|
||||||
|
};
|
|
@ -119,4 +119,27 @@ void DivEngine::registerROMExports() {
|
||||||
},
|
},
|
||||||
false, DIV_REQPOL_EXACT
|
false, DIV_REQPOL_EXACT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
romExportDefs[DIV_ROM_IPOD]=new DivROMExportDef(
|
||||||
|
"iPod .tone alarm", "AArt1256",
|
||||||
|
"iPod Beeper (.tone) Alarm export\n"
|
||||||
|
"for playback, you can drag the resulting file\n"
|
||||||
|
"into iPod_Control/Tones to your iPod IN DISK MODE",
|
||||||
|
"alarm tone files", ".tone",
|
||||||
|
{
|
||||||
|
DIV_SYSTEM_PCSPKR
|
||||||
|
},
|
||||||
|
false, DIV_REQPOL_ANY
|
||||||
|
);
|
||||||
|
|
||||||
|
romExportDefs[DIV_ROM_GRUB]=new DivROMExportDef(
|
||||||
|
"GRUB_INIT_TUNE", "AArt1256",
|
||||||
|
"GRUB_INIT_TUNE export\n"
|
||||||
|
"for use with the GRUB bootloader using the \"play\" command",
|
||||||
|
"Text/Binary files", NULL,
|
||||||
|
{
|
||||||
|
DIV_SYSTEM_PCSPKR
|
||||||
|
},
|
||||||
|
false, DIV_REQPOL_ANY
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -436,6 +436,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
unsigned int expansions = 0;
|
unsigned int expansions = 0;
|
||||||
unsigned int tchans = 0;
|
unsigned int tchans = 0;
|
||||||
unsigned int n163Chans = 0;
|
unsigned int n163Chans = 0;
|
||||||
|
int n163WaveOff[128];
|
||||||
bool hasSequence[256][8];
|
bool hasSequence[256][8];
|
||||||
unsigned char sequenceIndex[256][8];
|
unsigned char sequenceIndex[256][8];
|
||||||
unsigned char macro_types[256][8];
|
unsigned char macro_types[256][8];
|
||||||
|
@ -459,6 +460,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
|
|
||||||
int total_chans = 0;
|
int total_chans = 0;
|
||||||
|
|
||||||
|
memset(n163WaveOff,0,128*sizeof(int));
|
||||||
memset(hasSequence, 0, 256 * 8 * sizeof(bool));
|
memset(hasSequence, 0, 256 * 8 * sizeof(bool));
|
||||||
memset(sequenceIndex, 0, 256 * 8);
|
memset(sequenceIndex, 0, 256 * 8);
|
||||||
memset(macro_types, 0, 256 * 8);
|
memset(macro_types, 0, 256 * 8);
|
||||||
|
@ -653,6 +655,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
int map_ch = 0;
|
int map_ch = 0;
|
||||||
|
|
||||||
ds.system[systemID++] = DIV_SYSTEM_NES;
|
ds.system[systemID++] = DIV_SYSTEM_NES;
|
||||||
|
ds.systemFlags[0].set("resetSweep",true); // FamiTracker behavior
|
||||||
|
|
||||||
if (pal) {
|
if (pal) {
|
||||||
ds.systemFlags[0].set("clockSel", 1); // PAL clock
|
ds.systemFlags[0].set("clockSel", 1); // PAL clock
|
||||||
|
@ -1187,7 +1190,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int wave_count = reader.readI();
|
unsigned int wave_count = reader.readI();
|
||||||
size_t waveOff = ds.wave.size();
|
n163WaveOff[insIndex] = ds.wave.size();
|
||||||
|
ins->n163.wave = n163WaveOff[insIndex];
|
||||||
|
|
||||||
if (wave_size>256) {
|
if (wave_size>256) {
|
||||||
logE("wave size %d out of range",wave_size);
|
logE("wave size %d out of range",wave_size);
|
||||||
|
@ -1214,17 +1218,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// offset wave macro
|
|
||||||
if (ins->std.waveMacro.len == 0) // empty wave macro
|
|
||||||
{
|
|
||||||
ins->std.waveMacro.len = 1;
|
|
||||||
ins->std.waveMacro.val[0] = waveOff;
|
|
||||||
} else {
|
|
||||||
for (int p=0; p<ins->std.waveMacro.len; p++) {
|
|
||||||
ins->std.waveMacro.val[p] += waveOff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1945,7 +1938,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
for (int v = 0; v < 8; v++) {
|
for (int v = 0; v < 8; v++) {
|
||||||
if (map_channels[ch] == n163_chans[v]) {
|
if (map_channels[ch] == n163_chans[v]) {
|
||||||
if (pat->data[row][4 + (j * 2)] == 0x12) {
|
if (pat->data[row][4 + (j * 2)] == 0x12) {
|
||||||
pat->data[row][4 + (j * 2)] = 0x10; // TODO: map wave
|
pat->data[row][4 + (j * 2)] = 0x110; // N163 wave change (we'll map this later)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2773,6 +2766,46 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
|
||||||
if (i->virtualTempoD<1) i->virtualTempoD=1;
|
if (i->virtualTempoD<1) i->virtualTempoD=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// offset N163 wave macros (local -> global wave conversion)
|
||||||
|
for (size_t i=0; i<ds.ins.size(); i++) {
|
||||||
|
DivInstrument* ins=ds.ins[i];
|
||||||
|
int waveOff=n163WaveOff[i];
|
||||||
|
if (ins->type==DIV_INS_N163) {
|
||||||
|
for (int j=0; j<ins->std.waveMacro.len; j++) {
|
||||||
|
ins->std.waveMacro.val[j]+=waveOff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// offset N163 wave change effects whether possible
|
||||||
|
for (DivSubSong* i: ds.subsong) {
|
||||||
|
for (int j=0; j<total_chans; j++) {
|
||||||
|
int curWaveOff=0;
|
||||||
|
for (int k=0; k<i->ordersLen; k++) {
|
||||||
|
DivPattern* p=i->pat[j].getPattern(i->orders.ord[j][k],true);
|
||||||
|
for (int l=0; l<i->patLen; l++) {
|
||||||
|
// check for instrument change
|
||||||
|
if (p->data[l][2]!=-1) {
|
||||||
|
curWaveOff=n163WaveOff[p->data[l][2]&127];
|
||||||
|
}
|
||||||
|
|
||||||
|
// check effect columns for 0x110 (dummy wave change)
|
||||||
|
for (int m=0; m<i->pat[j].effectCols; m++) {
|
||||||
|
if (p->data[l][4+(m<<1)]==0x110) {
|
||||||
|
// map wave
|
||||||
|
p->data[l][4+(m<<1)]=0x10;
|
||||||
|
if (p->data[l][5+(m<<1)]==-1) {
|
||||||
|
p->data[l][5+(m<<1)]=curWaveOff&0xff;
|
||||||
|
} else {
|
||||||
|
p->data[l][5+(m<<1)]=(p->data[l][5+(m<<1)]+curWaveOff)&0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (active) quitDispatch();
|
if (active) quitDispatch();
|
||||||
BUSY_BEGIN_SOFT;
|
BUSY_BEGIN_SOFT;
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
|
|
|
@ -92,23 +92,14 @@ void DivEngine::loadPPS(SafeReader& reader, std::vector<DivSample*>& ret, String
|
||||||
|
|
||||||
s->rate = PPS_SAMPLE_RATE;
|
s->rate = PPS_SAMPLE_RATE;
|
||||||
s->centerRate = PPS_SAMPLE_RATE;
|
s->centerRate = PPS_SAMPLE_RATE;
|
||||||
s->depth = DIV_SAMPLE_DEPTH_8BIT;
|
s->depth = DIV_SAMPLE_DEPTH_4BIT;
|
||||||
s->init(headers[i].sample_length * 2); //byte per sample
|
s->init(headers[i].sample_length*2); // bytes->samples
|
||||||
|
|
||||||
reader.seek((int)headers[i].start_pointer, SEEK_SET);
|
reader.seek((int)headers[i].start_pointer, SEEK_SET);
|
||||||
|
|
||||||
int sample_pos = 0;
|
|
||||||
|
|
||||||
for(int j = 0; j < headers[i].sample_length; j++)
|
for(int j = 0; j < headers[i].sample_length; j++)
|
||||||
{
|
{
|
||||||
unsigned char curr_byte = (unsigned char)reader.readC();
|
s->data4[j] = reader.readC();
|
||||||
|
|
||||||
s->data8[sample_pos] = (curr_byte >> 4) | (curr_byte & 0xf0);
|
|
||||||
s->data8[sample_pos] += 0x80;
|
|
||||||
sample_pos++;
|
|
||||||
s->data8[sample_pos] = (curr_byte << 4) | (curr_byte & 0xf);
|
|
||||||
s->data8[sample_pos] += 0x80;
|
|
||||||
sample_pos++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.push_back(s);
|
ret.push_back(s);
|
||||||
|
|
|
@ -126,6 +126,13 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) {
|
||||||
w->writeText(fmt::sprintf("- type: %d\n",(int)ins->type));
|
w->writeText(fmt::sprintf("- type: %d\n",(int)ins->type));
|
||||||
|
|
||||||
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPM || ins->type==DIV_INS_ESFM) {
|
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPM || ins->type==DIV_INS_ESFM) {
|
||||||
|
int opCount=4;
|
||||||
|
if (ins->type==DIV_INS_OPLL) {
|
||||||
|
opCount=2;
|
||||||
|
} else if (ins->type==DIV_INS_OPL) {
|
||||||
|
opCount=(ins->fm.ops==4)?4:2;
|
||||||
|
}
|
||||||
|
|
||||||
w->writeText("- FM parameters:\n");
|
w->writeText("- FM parameters:\n");
|
||||||
w->writeText(fmt::sprintf(" - ALG: %d\n",ins->fm.alg));
|
w->writeText(fmt::sprintf(" - ALG: %d\n",ins->fm.alg));
|
||||||
w->writeText(fmt::sprintf(" - FB: %d\n",ins->fm.fb));
|
w->writeText(fmt::sprintf(" - FB: %d\n",ins->fm.fb));
|
||||||
|
@ -133,14 +140,14 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) {
|
||||||
w->writeText(fmt::sprintf(" - AMS: %d\n",ins->fm.ams));
|
w->writeText(fmt::sprintf(" - AMS: %d\n",ins->fm.ams));
|
||||||
w->writeText(fmt::sprintf(" - FMS2: %d\n",ins->fm.fms2));
|
w->writeText(fmt::sprintf(" - FMS2: %d\n",ins->fm.fms2));
|
||||||
w->writeText(fmt::sprintf(" - AMS2: %d\n",ins->fm.ams2));
|
w->writeText(fmt::sprintf(" - AMS2: %d\n",ins->fm.ams2));
|
||||||
w->writeText(fmt::sprintf(" - operators: %d\n",ins->fm.ops));
|
w->writeText(fmt::sprintf(" - operators: %d\n",opCount));
|
||||||
w->writeText(fmt::sprintf(" - OPLL patch: %d\n",ins->fm.opllPreset));
|
w->writeText(fmt::sprintf(" - OPLL patch: %d\n",ins->fm.opllPreset));
|
||||||
w->writeText(fmt::sprintf(" - fixed drum freq: %s\n",trueFalse[ins->fm.fixedDrums?1:0]));
|
w->writeText(fmt::sprintf(" - fixed drum freq: %s\n",trueFalse[ins->fm.fixedDrums?1:0]));
|
||||||
w->writeText(fmt::sprintf(" - kick freq: %.4X\n",ins->fm.kickFreq));
|
w->writeText(fmt::sprintf(" - kick freq: %.4X\n",ins->fm.kickFreq));
|
||||||
w->writeText(fmt::sprintf(" - snare/hat freq: %.4X\n",ins->fm.snareHatFreq));
|
w->writeText(fmt::sprintf(" - snare/hat freq: %.4X\n",ins->fm.snareHatFreq));
|
||||||
w->writeText(fmt::sprintf(" - tom/top freq: %.4X\n",ins->fm.tomTopFreq));
|
w->writeText(fmt::sprintf(" - tom/top freq: %.4X\n",ins->fm.tomTopFreq));
|
||||||
|
|
||||||
for (int j=0; j<ins->fm.ops; j++) {
|
for (int j=0; j<opCount; j++) {
|
||||||
DivInstrumentFM::Operator& op=ins->fm.op[j];
|
DivInstrumentFM::Operator& op=ins->fm.op[j];
|
||||||
|
|
||||||
w->writeText(fmt::sprintf(" - operator %d:\n",j));
|
w->writeText(fmt::sprintf(" - operator %d:\n",j));
|
||||||
|
@ -173,7 +180,7 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) {
|
||||||
w->writeText("- ESFM parameters:\n");
|
w->writeText("- ESFM parameters:\n");
|
||||||
w->writeText(fmt::sprintf(" - noise mode: %d\n",ins->esfm.noise));
|
w->writeText(fmt::sprintf(" - noise mode: %d\n",ins->esfm.noise));
|
||||||
|
|
||||||
for (int j=0; j<ins->fm.ops; j++) {
|
for (int j=0; j<4; j++) {
|
||||||
DivInstrumentESFM::Operator& opE=ins->esfm.op[j];
|
DivInstrumentESFM::Operator& opE=ins->esfm.op[j];
|
||||||
|
|
||||||
w->writeText(fmt::sprintf(" - operator %d:\n",j));
|
w->writeText(fmt::sprintf(" - operator %d:\n",j));
|
||||||
|
|
|
@ -313,12 +313,18 @@ std::vector<DivSample*> DivEngine::sampleFromFile(const char* path) {
|
||||||
logD("sample is 32-bit float");
|
logD("sample is 32-bit float");
|
||||||
buf=new float[si.channels*si.frames];
|
buf=new float[si.channels*si.frames];
|
||||||
sampleLen=sizeof(float);
|
sampleLen=sizeof(float);
|
||||||
|
} else if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_DOUBLE) {
|
||||||
|
logD("sample is 64-bit float");
|
||||||
|
buf=new float[si.channels*si.frames];
|
||||||
|
sampleLen=sizeof(double);
|
||||||
} else {
|
} else {
|
||||||
logD("sample is 16-bit signed");
|
logD("sample is 16-bit signed");
|
||||||
buf=new short[si.channels*si.frames];
|
buf=new short[si.channels*si.frames];
|
||||||
sampleLen=sizeof(short);
|
sampleLen=sizeof(short);
|
||||||
}
|
}
|
||||||
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8 || (si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_FLOAT) {
|
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8 ||
|
||||||
|
(si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_FLOAT ||
|
||||||
|
(si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_DOUBLE) {
|
||||||
if (sf_read_raw(f,buf,si.frames*si.channels*sampleLen)!=(si.frames*si.channels*sampleLen)) {
|
if (sf_read_raw(f,buf,si.frames*si.channels*sampleLen)!=(si.frames*si.channels*sampleLen)) {
|
||||||
logW("sample read size mismatch!");
|
logW("sample read size mismatch!");
|
||||||
}
|
}
|
||||||
|
@ -361,6 +367,19 @@ std::vector<DivSample*> DivEngine::sampleFromFile(const char* path) {
|
||||||
sample->data16[index++]=averaged;
|
sample->data16[index++]=averaged;
|
||||||
}
|
}
|
||||||
delete[] (float*)buf;
|
delete[] (float*)buf;
|
||||||
|
} else if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_DOUBLE) {
|
||||||
|
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||||
|
double averaged=0.0f;
|
||||||
|
for (int j=0; j<si.channels; j++) {
|
||||||
|
averaged+=((double*)buf)[i+j];
|
||||||
|
}
|
||||||
|
averaged/=si.channels;
|
||||||
|
averaged*=32767.0;
|
||||||
|
if (averaged<-32768.0) averaged=-32768.0;
|
||||||
|
if (averaged>32767.0) averaged=32767.0;
|
||||||
|
sample->data16[index++]=averaged;
|
||||||
|
}
|
||||||
|
delete[] (double*)buf;
|
||||||
} else {
|
} else {
|
||||||
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||||
int averaged=0;
|
int averaged=0;
|
||||||
|
@ -505,6 +524,7 @@ DivSample* DivEngine::sampleFromFileRaw(const char* path, DivSampleDepth depth,
|
||||||
case DIV_SAMPLE_DEPTH_ADPCM_B:
|
case DIV_SAMPLE_DEPTH_ADPCM_B:
|
||||||
case DIV_SAMPLE_DEPTH_ADPCM_K:
|
case DIV_SAMPLE_DEPTH_ADPCM_K:
|
||||||
case DIV_SAMPLE_DEPTH_VOX:
|
case DIV_SAMPLE_DEPTH_VOX:
|
||||||
|
case DIV_SAMPLE_DEPTH_4BIT:
|
||||||
samples=lenDivided*2;
|
samples=lenDivided*2;
|
||||||
break;
|
break;
|
||||||
case DIV_SAMPLE_DEPTH_IMA_ADPCM:
|
case DIV_SAMPLE_DEPTH_IMA_ADPCM:
|
||||||
|
@ -617,6 +637,7 @@ DivSample* DivEngine::sampleFromFileRaw(const char* path, DivSampleDepth depth,
|
||||||
case DIV_SAMPLE_DEPTH_ADPCM_B:
|
case DIV_SAMPLE_DEPTH_ADPCM_B:
|
||||||
case DIV_SAMPLE_DEPTH_ADPCM_K:
|
case DIV_SAMPLE_DEPTH_ADPCM_K:
|
||||||
case DIV_SAMPLE_DEPTH_VOX:
|
case DIV_SAMPLE_DEPTH_VOX:
|
||||||
|
case DIV_SAMPLE_DEPTH_4BIT:
|
||||||
// swap nibbles
|
// swap nibbles
|
||||||
for (unsigned int i=0; i<sample->getCurBufLen(); i++) {
|
for (unsigned int i=0; i<sample->getCurBufLen(); i++) {
|
||||||
b[i]=(b[i]<<4)|(b[i]>>4);
|
b[i]=(b[i]<<4)|(b[i]>>4);
|
||||||
|
|
|
@ -748,7 +748,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||||
if (amigaPitch && parent->song.linearPitch!=2) {
|
if (amigaPitch && parent->song.linearPitch!=2) {
|
||||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch*16,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,2,chan[i].pitch2*16,16*COLOR_NTSC,chan[i].pcm.freqOffs);
|
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch*16,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,2,chan[i].pitch2*16,16*COLOR_NTSC,chan[i].pcm.freqOffs);
|
||||||
chan[i].freq=524288*(COLOR_NTSC/chan[i].freq)/(chipClock/32.0);
|
chan[i].freq=PITCH_OFFSET*(COLOR_NTSC/chan[i].freq)/(chipClock/16.0);
|
||||||
chan[i].freq=CLAMP(chan[i].freq,0,0x1ffff);
|
chan[i].freq=CLAMP(chan[i].freq,0,0x1ffff);
|
||||||
} else {
|
} else {
|
||||||
chan[i].freq=CLAMP(parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,chan[i].pcm.freqOffs),0,0x1ffff);
|
chan[i].freq=CLAMP(parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,chan[i].pcm.freqOffs),0,0x1ffff);
|
||||||
|
|
|
@ -79,8 +79,10 @@ class DivPlatformFMBase: public DivDispatch {
|
||||||
unsigned int addr;
|
unsigned int addr;
|
||||||
unsigned short val;
|
unsigned short val;
|
||||||
bool addrOrVal;
|
bool addrOrVal;
|
||||||
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
|
bool urgent;
|
||||||
QueuedWrite(unsigned int a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
QueuedWrite(): addr(0), val(0), addrOrVal(false), urgent(false) {}
|
||||||
|
QueuedWrite(unsigned int a, unsigned char v): addr(a), val(v), addrOrVal(false), urgent(false) {}
|
||||||
|
QueuedWrite(unsigned int a, unsigned char v, bool u): addr(a), val(v), addrOrVal(false), urgent(u) {}
|
||||||
};
|
};
|
||||||
FixedQueue<QueuedWrite,2048> writes;
|
FixedQueue<QueuedWrite,2048> writes;
|
||||||
|
|
||||||
|
@ -108,14 +110,7 @@ class DivPlatformFMBase: public DivDispatch {
|
||||||
// only used by OPN2 for DAC writes
|
// only used by OPN2 for DAC writes
|
||||||
inline void urgentWrite(unsigned short a, unsigned char v) {
|
inline void urgentWrite(unsigned short a, unsigned char v) {
|
||||||
if (!skipRegisterWrites && !flushFirst) {
|
if (!skipRegisterWrites && !flushFirst) {
|
||||||
if (!writes.empty()) {
|
writes.push_front(QueuedWrite(a,v,true));
|
||||||
// check for hard reset
|
|
||||||
if (writes.front().addr==0xf0) {
|
|
||||||
// replace hard reset with DAC write
|
|
||||||
writes.pop_front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writes.push_front(QueuedWrite(a,v));
|
|
||||||
if (dumpWrites) {
|
if (dumpWrites) {
|
||||||
addWrite(a,v);
|
addWrite(a,v);
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,8 +150,9 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
|
||||||
|
|
||||||
os[0]=0; os[1]=0;
|
os[0]=0; os[1]=0;
|
||||||
for (int i=0; i<6; i++) {
|
for (int i=0; i<6; i++) {
|
||||||
if (delay<=0 && !writes.empty()) {
|
if (!writes.empty()) {
|
||||||
QueuedWrite& w=writes.front();
|
QueuedWrite& w=writes.front();
|
||||||
|
if (delay<=0 || w.urgent) {
|
||||||
if (w.addr==0xfffffffe) {
|
if (w.addr==0xfffffffe) {
|
||||||
delay=w.val*3;
|
delay=w.val*3;
|
||||||
writes.pop_front();
|
writes.pop_front();
|
||||||
|
@ -176,6 +177,17 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
|
||||||
w.addrOrVal=true;
|
w.addrOrVal=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (dacWrite>=0) {
|
||||||
|
if (!canWriteDAC) {
|
||||||
|
canWriteDAC=true;
|
||||||
|
} else {
|
||||||
|
urgentWrite(0x2a,dacWrite);
|
||||||
|
dacWrite=-1;
|
||||||
|
canWriteDAC=writes.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
canWriteDAC=true;
|
canWriteDAC=true;
|
||||||
if (dacWrite>=0) {
|
if (dacWrite>=0) {
|
||||||
|
@ -244,8 +256,9 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
|
||||||
if (delay>0) delay--;
|
if (delay>0) delay--;
|
||||||
|
|
||||||
os[0]=0; os[1]=0;
|
os[0]=0; os[1]=0;
|
||||||
if (delay<=0 && !writes.empty()) {
|
if (!writes.empty()) {
|
||||||
QueuedWrite& w=writes.front();
|
QueuedWrite& w=writes.front();
|
||||||
|
if (delay<=0 || w.urgent) {
|
||||||
if (w.addr==0xfffffffe) {
|
if (w.addr==0xfffffffe) {
|
||||||
delay=w.val;
|
delay=w.val;
|
||||||
} else {
|
} else {
|
||||||
|
@ -264,6 +277,17 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
|
||||||
canWriteDAC=writes.empty();
|
canWriteDAC=writes.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (dacWrite>=0) {
|
||||||
|
if (!canWriteDAC) {
|
||||||
|
canWriteDAC=true;
|
||||||
|
} else {
|
||||||
|
urgentWrite(0x2a,dacWrite);
|
||||||
|
dacWrite=-1;
|
||||||
|
canWriteDAC=writes.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
canWriteDAC=true;
|
canWriteDAC=true;
|
||||||
if (dacWrite>=0) {
|
if (dacWrite>=0) {
|
||||||
|
@ -402,8 +426,9 @@ void DivPlatformGenesis::acquire_nuked276(short** buf, size_t len) {
|
||||||
|
|
||||||
if (delay>0) delay--;
|
if (delay>0) delay--;
|
||||||
|
|
||||||
if (delay<=0 && !writes.empty()) {
|
if (!writes.empty()) {
|
||||||
QueuedWrite& w=writes.front();
|
QueuedWrite& w=writes.front();
|
||||||
|
if (delay<=0 || w.urgent) {
|
||||||
if (w.addr==0xfffffffe) {
|
if (w.addr==0xfffffffe) {
|
||||||
delay=w.val;
|
delay=w.val;
|
||||||
writes.pop_front();
|
writes.pop_front();
|
||||||
|
@ -552,6 +577,17 @@ void DivPlatformGenesis::acquire_nuked276(short** buf, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
w.addrOrVal=true;
|
w.addrOrVal=true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (dacWrite>=0) {
|
||||||
|
if (!canWriteDAC) {
|
||||||
|
canWriteDAC=true;
|
||||||
|
} else {
|
||||||
|
urgentWrite(0x2a,dacWrite);
|
||||||
|
dacWrite=-1;
|
||||||
|
canWriteDAC=writes.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
canWriteDAC=true;
|
canWriteDAC=true;
|
||||||
if (dacWrite>=0) {
|
if (dacWrite>=0) {
|
||||||
|
|
|
@ -352,9 +352,6 @@ void DivPlatformNES::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
if (chan[i].sweepChanged) {
|
if (chan[i].sweepChanged) {
|
||||||
chan[i].sweepChanged=false;
|
chan[i].sweepChanged=false;
|
||||||
if (i==0) {
|
|
||||||
// rWrite(16+i*5,chan[i].sweep);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (i<3) if (chan[i].std.phaseReset.had) {
|
if (i<3) if (chan[i].std.phaseReset.had) {
|
||||||
if (chan[i].std.phaseReset.val==1) {
|
if (chan[i].std.phaseReset.val==1) {
|
||||||
|
@ -637,6 +634,13 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
||||||
} else if (!parent->song.brokenOutVol2) {
|
} 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));
|
rWrite(0x4000+c.chan*4,(chan[c.chan].envMode<<4)|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
|
||||||
}
|
}
|
||||||
|
if (resetSweep && c.chan<2) {
|
||||||
|
if (chan[c.chan].sweep!=0x08 && !chan[c.chan].sweepChanged) {
|
||||||
|
chan[c.chan].sweep=0x08;
|
||||||
|
chan[c.chan].prevFreq=-1;
|
||||||
|
rWrite(0x4001+(c.chan*4),chan[c.chan].sweep);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_NOTE_OFF:
|
case DIV_CMD_NOTE_OFF:
|
||||||
if (c.chan==4) {
|
if (c.chan==4) {
|
||||||
|
@ -724,6 +728,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rWrite(0x4001+(c.chan*4),chan[c.chan].sweep);
|
rWrite(0x4001+(c.chan*4),chan[c.chan].sweep);
|
||||||
|
chan[c.chan].sweepChanged=true;
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_NES_ENV_MODE:
|
case DIV_CMD_NES_ENV_MODE:
|
||||||
chan[c.chan].envMode=c.value&3;
|
chan[c.chan].envMode=c.value&3;
|
||||||
|
@ -989,6 +994,7 @@ void DivPlatformNES::setFlags(const DivConfig& flags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dpcmModeDefault=flags.getBool("dpcmMode",true);
|
dpcmModeDefault=flags.getBool("dpcmMode",true);
|
||||||
|
resetSweep=flags.getBool("resetSweep",false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformNES::notifyInsDeletion(void* ins) {
|
void DivPlatformNES::notifyInsDeletion(void* ins) {
|
||||||
|
|
|
@ -68,6 +68,7 @@ class DivPlatformNES: public DivDispatch {
|
||||||
signed char lastDPCMFreq;
|
signed char lastDPCMFreq;
|
||||||
bool dpcmMode;
|
bool dpcmMode;
|
||||||
bool dpcmModeDefault;
|
bool dpcmModeDefault;
|
||||||
|
bool resetSweep;
|
||||||
bool dacAntiClickOn;
|
bool dacAntiClickOn;
|
||||||
bool useNP;
|
bool useNP;
|
||||||
bool goingToLoop;
|
bool goingToLoop;
|
||||||
|
|
|
@ -1338,7 +1338,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
||||||
unsigned char slot=slots[j][i];
|
unsigned char slot=slots[j][i];
|
||||||
if (slot==255) continue;
|
if (slot==255) continue;
|
||||||
unsigned short baseAddr=slotMap[slot];
|
unsigned short baseAddr=slotMap[slot];
|
||||||
if (baseAddr>0x100) {
|
if (baseAddr>=0x100) {
|
||||||
weWillWriteRRLater[(baseAddr&0xff)|32]=true;
|
weWillWriteRRLater[(baseAddr&0xff)|32]=true;
|
||||||
} else {
|
} else {
|
||||||
weWillWriteRRLater[(baseAddr&0xff)]=true;
|
weWillWriteRRLater[(baseAddr&0xff)]=true;
|
||||||
|
@ -1464,7 +1464,8 @@ void DivPlatformOPL::tick(bool sysTick) {
|
||||||
chan[i].freqL=(chan[i].freq>>chan[i].freqH)&0x3ff;
|
chan[i].freqL=(chan[i].freq>>chan[i].freqH)&0x3ff;
|
||||||
chan[i].freqH=8^chan[i].freqH;
|
chan[i].freqH=8^chan[i].freqH;
|
||||||
ctrl|=(chan[i].active?0x80:0)|(chan[i].damp?0x40:0)|(chan[i].lfoReset?0x20:0)|(chan[i].ch?0x10:0)|(isMuted[i]?8:(chan[i].pan&0xf));
|
ctrl|=(chan[i].active?0x80:0)|(chan[i].damp?0x40:0)|(chan[i].lfoReset?0x20:0)|(chan[i].ch?0x10:0)|(isMuted[i]?8:(chan[i].pan&0xf));
|
||||||
unsigned int waveNum=chan[i].sample;
|
int waveNum=chan[i].sample;
|
||||||
|
if (waveNum>=0) {
|
||||||
if (ramSize<=0x200000) {
|
if (ramSize<=0x200000) {
|
||||||
waveNum=CLAMP(waveNum,0,0x7f)|0x180;
|
waveNum=CLAMP(waveNum,0,0x7f)|0x180;
|
||||||
}
|
}
|
||||||
|
@ -1497,6 +1498,10 @@ void DivPlatformOPL::tick(bool sysTick) {
|
||||||
immWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+PCM_REG(i),ctrl);
|
immWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+PCM_REG(i),ctrl);
|
||||||
chan[i].writeCtrl=false;
|
chan[i].writeCtrl=false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// cut if we don't have a sample
|
||||||
|
immWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+PCM_REG(i),ctrl&~0x80);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (chan[i].freqChanged) {
|
if (chan[i].freqChanged) {
|
||||||
|
|
|
@ -202,23 +202,30 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chan[i].state.opllPreset==0) {
|
|
||||||
if (chan[i].std.alg.had) { // SUS
|
if (chan[i].std.alg.had) { // SUS
|
||||||
chan[i].state.alg=chan[i].std.alg.val;
|
chan[i].state.alg=chan[i].std.alg.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
chan[i].freqChanged=true;
|
chan[i].freqChanged=true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (chan[i].std.fb.had) {
|
if (chan[i].std.fb.had) {
|
||||||
chan[i].state.fb=chan[i].std.fb.val;
|
chan[i].state.fb=chan[i].std.fb.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x03,(chan[i].state.op[1].ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb);
|
rWrite(0x03,(chan[i].state.op[1].ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (chan[i].std.fms.had) {
|
if (chan[i].std.fms.had) {
|
||||||
chan[i].state.fms=chan[i].std.fms.val;
|
chan[i].state.fms=chan[i].std.fms.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x03,(chan[i].state.op[1].ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb);
|
rWrite(0x03,(chan[i].state.op[1].ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (chan[i].std.ams.had) {
|
if (chan[i].std.ams.had) {
|
||||||
chan[i].state.ams=chan[i].std.ams.val;
|
chan[i].state.ams=chan[i].std.ams.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x03,(chan[i].state.op[1].ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb);
|
rWrite(0x03,(chan[i].state.op[1].ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int j=0; j<2; j++) {
|
for (int j=0; j<2; j++) {
|
||||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||||
|
@ -226,28 +233,40 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
||||||
|
|
||||||
if (m.am.had) {
|
if (m.am.had) {
|
||||||
op.am=m.am.val;
|
op.am=m.am.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult));
|
rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (m.ar.had) {
|
if (m.ar.had) {
|
||||||
op.ar=m.ar.val;
|
op.ar=m.ar.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x04+j,(op.ar<<4)|(op.dr));
|
rWrite(0x04+j,(op.ar<<4)|(op.dr));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (m.dr.had) {
|
if (m.dr.had) {
|
||||||
op.dr=m.dr.val;
|
op.dr=m.dr.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x04+j,(op.ar<<4)|(op.dr));
|
rWrite(0x04+j,(op.ar<<4)|(op.dr));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (m.mult.had) {
|
if (m.mult.had) {
|
||||||
op.mult=m.mult.val;
|
op.mult=m.mult.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult));
|
rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (m.rr.had) {
|
if (m.rr.had) {
|
||||||
op.rr=m.rr.val;
|
op.rr=m.rr.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x06+j,(op.sl<<4)|(op.rr));
|
rWrite(0x06+j,(op.sl<<4)|(op.rr));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (m.sl.had) {
|
if (m.sl.had) {
|
||||||
op.sl=m.sl.val;
|
op.sl=m.sl.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x06+j,(op.sl<<4)|(op.rr));
|
rWrite(0x06+j,(op.sl<<4)|(op.rr));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (m.tl.had) {
|
if (m.tl.had) {
|
||||||
op.tl=m.tl.val&((j==1)?15:63);
|
op.tl=m.tl.val&((j==1)?15:63);
|
||||||
if (j==1) {
|
if (j==1) {
|
||||||
|
@ -255,28 +274,37 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
||||||
rWrite(0x30+i,((15-VOL_SCALE_LOG_BROKEN(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
|
rWrite(0x30+i,((15-VOL_SCALE_LOG_BROKEN(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x02,(chan[i].state.op[0].ksl<<6)|(op.tl&63));
|
rWrite(0x02,(chan[i].state.op[0].ksl<<6)|(op.tl&63));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m.egt.had) {
|
if (m.egt.had) {
|
||||||
op.ssgEnv=(m.egt.val&1)?8:0;
|
op.ssgEnv=(m.egt.val&1)?8:0;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult));
|
rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (m.ksl.had) {
|
if (m.ksl.had) {
|
||||||
op.ksl=m.ksl.val;
|
op.ksl=m.ksl.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
if (j==1) {
|
if (j==1) {
|
||||||
rWrite(0x02,(chan[i].state.op[0].ksl<<6)|(chan[i].state.op[0].tl&63));
|
rWrite(0x02,(chan[i].state.op[0].ksl<<6)|(chan[i].state.op[0].tl&63));
|
||||||
} else {
|
} else {
|
||||||
rWrite(0x03,(chan[i].state.op[1].ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb);
|
rWrite(0x03,(chan[i].state.op[1].ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (m.ksr.had) {
|
if (m.ksr.had) {
|
||||||
op.ksr=m.ksr.val;
|
op.ksr=m.ksr.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult));
|
rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (m.vib.had) {
|
if (m.vib.had) {
|
||||||
op.vib=m.vib.val;
|
op.vib=m.vib.val;
|
||||||
|
if (chan[i].state.opllPreset==0) {
|
||||||
rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult));
|
rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -390,6 +418,7 @@ int DivPlatformOPLL::toFreq(int freq, int fixedBlock) {
|
||||||
block=freq/OPLL_C_NUM;
|
block=freq/OPLL_C_NUM;
|
||||||
if (block>0) block=bsr(block);
|
if (block>0) block=bsr(block);
|
||||||
}
|
}
|
||||||
|
if (block>7) block=7;
|
||||||
freq>>=block;
|
freq>>=block;
|
||||||
if (freq>0x1ff) freq=0x1ff;
|
if (freq>0x1ff) freq=0x1ff;
|
||||||
return (block<<9)|freq;
|
return (block<<9)|freq;
|
||||||
|
|
|
@ -72,11 +72,19 @@ void sm8521_noise_tick(struct sm8521_noise_t *noise, const int cycle)
|
||||||
noise->base.counter += cycle;
|
noise->base.counter += cycle;
|
||||||
while (noise->base.counter >= (noise->base.t + 1))
|
while (noise->base.counter >= (noise->base.t + 1))
|
||||||
{
|
{
|
||||||
|
// https://github.com/tildearrow/furnace/issues/2567
|
||||||
// unknown algorithm, but don't use rand()
|
// unknown algorithm, but don't use rand()
|
||||||
|
//
|
||||||
|
// some research suggests VIC-like noise, although
|
||||||
|
// that remains to be confirmed
|
||||||
|
noise->oldLFSR = noise->lfsr & 1;
|
||||||
noise->lfsr = ( noise->lfsr>>1|(((noise->lfsr) ^ (noise->lfsr >> 5) ^ (noise->lfsr >> 8) ^ (noise->lfsr >> 13) ) & 1)<<31);
|
noise->lfsr = ( noise->lfsr>>1|(((noise->lfsr) ^ (noise->lfsr >> 5) ^ (noise->lfsr >> 8) ^ (noise->lfsr >> 13) ) & 1)<<31);
|
||||||
noise->base.counter -= (noise->base.t + 1);
|
noise->base.counter -= (noise->base.t + 1);
|
||||||
|
if (noise->oldLFSR^(noise->lfsr&1)) {
|
||||||
|
noise->out ^= 1;
|
||||||
}
|
}
|
||||||
noise->base.out = (((noise->lfsr & 0x1) ? 7 : -8) * noise->base.level) >> 1; // scale out to 8bit
|
}
|
||||||
|
noise->base.out = ((noise->out ? 7 : -8) * noise->base.level) >> 1; // scale out to 8bit
|
||||||
}
|
}
|
||||||
|
|
||||||
void sm8521_sound_tick(struct sm8521_t *sm8521, const int cycle)
|
void sm8521_sound_tick(struct sm8521_t *sm8521, const int cycle)
|
||||||
|
@ -130,6 +138,8 @@ void sm8521_reset(struct sm8521_t *sm8521)
|
||||||
sm8521->noise.base.out = 0;
|
sm8521->noise.base.out = 0;
|
||||||
sm8521->noise.base.counter = 0;
|
sm8521->noise.base.counter = 0;
|
||||||
sm8521->noise.lfsr = 0x89abcdef;
|
sm8521->noise.lfsr = 0x89abcdef;
|
||||||
|
sm8521->noise.oldLFSR = 1;
|
||||||
|
sm8521->noise.out = 0;
|
||||||
sm8521->out = 0;
|
sm8521->out = 0;
|
||||||
sm8521->sgda = 0;
|
sm8521->sgda = 0;
|
||||||
sm8521->sgc = 0;
|
sm8521->sgc = 0;
|
||||||
|
|
|
@ -65,7 +65,8 @@ struct sm8521_wave_t
|
||||||
struct sm8521_noise_t
|
struct sm8521_noise_t
|
||||||
{
|
{
|
||||||
struct sm8521_sg_t base;
|
struct sm8521_sg_t base;
|
||||||
unsigned int lfsr; // LFSR
|
unsigned int lfsr, oldLFSR; // LFSR
|
||||||
|
unsigned char out;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sm8521_t
|
struct sm8521_t
|
||||||
|
|
|
@ -51,6 +51,8 @@ void VSU::Power(void)
|
||||||
SweepControl = 0;
|
SweepControl = 0;
|
||||||
SweepModCounter = 0;
|
SweepModCounter = 0;
|
||||||
SweepModClockDivider = 1;
|
SweepModClockDivider = 1;
|
||||||
|
ModState = 0;
|
||||||
|
ModLock = 0;
|
||||||
|
|
||||||
for(int ch = 0; ch < 6; ch++)
|
for(int ch = 0; ch < 6; ch++)
|
||||||
{
|
{
|
||||||
|
@ -62,7 +64,9 @@ void VSU::Power(void)
|
||||||
RAMAddress[ch] = 0;
|
RAMAddress[ch] = 0;
|
||||||
|
|
||||||
EffFreq[ch] = 0;
|
EffFreq[ch] = 0;
|
||||||
Envelope[ch] = 0;
|
EnvelopeReload[ch] = 0;
|
||||||
|
EnvelopeValue[ch] = 0;
|
||||||
|
EnvelopeModMask[ch] = 0;
|
||||||
WavePos[ch] = 0;
|
WavePos[ch] = 0;
|
||||||
FreqCounter[ch] = 1;
|
FreqCounter[ch] = 1;
|
||||||
IntervalCounter[ch] = 0;
|
IntervalCounter[ch] = 0;
|
||||||
|
@ -100,6 +104,8 @@ void VSU::Write(int timestamp, unsigned int A, unsigned char V)
|
||||||
|
|
||||||
Update(timestamp);
|
Update(timestamp);
|
||||||
|
|
||||||
|
ModLock = 0;
|
||||||
|
|
||||||
//printf("VSU Write: %d, %08x %02x\n", timestamp, A, V);
|
//printf("VSU Write: %d, %08x %02x\n", timestamp, A, V);
|
||||||
|
|
||||||
if(A < 0x280)
|
if(A < 0x280)
|
||||||
|
@ -133,7 +139,6 @@ void VSU::Write(int timestamp, unsigned int A, unsigned char V)
|
||||||
|
|
||||||
if(V & 0x80)
|
if(V & 0x80)
|
||||||
{
|
{
|
||||||
EffFreq[ch] = Frequency[ch];
|
|
||||||
if(ch == 5)
|
if(ch == 5)
|
||||||
FreqCounter[ch] = 10 * (2048 - EffFreq[ch]);
|
FreqCounter[ch] = 10 * (2048 - EffFreq[ch]);
|
||||||
else
|
else
|
||||||
|
@ -146,15 +151,20 @@ void VSU::Write(int timestamp, unsigned int A, unsigned char V)
|
||||||
SweepModCounter = (SweepControl >> 4) & 7;
|
SweepModCounter = (SweepControl >> 4) & 7;
|
||||||
SweepModClockDivider = (SweepControl & 0x80) ? 8 : 1;
|
SweepModClockDivider = (SweepControl & 0x80) ? 8 : 1;
|
||||||
ModWavePos = 0;
|
ModWavePos = 0;
|
||||||
|
ModState = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
WavePos[ch] = 0;
|
WavePos[ch] = 0;
|
||||||
|
|
||||||
if(ch == 5) // Not sure if this is correct.
|
if(ch == 5) { // Not sure if this is correct.
|
||||||
lfsr = 1;
|
lfsr = 1;
|
||||||
|
}
|
||||||
|
|
||||||
//if(!(IntlControl[ch] & 0x80))
|
EnvelopeModMask[ch] = 0;
|
||||||
// Envelope[ch] = (EnvControl[ch] >> 4) & 0xF;
|
if(!(EnvControl[ch] & 0x200) && (
|
||||||
|
(EnvelopeValue[ch] == 0 && !(EnvControl[ch] & 0x0008)) ||
|
||||||
|
(EnvelopeValue[ch] == 0xF && (EnvControl[ch] & 0x0008))))
|
||||||
|
EnvelopeModMask[ch] = 1;
|
||||||
|
|
||||||
EffectsClockDivider[ch] = 4800;
|
EffectsClockDivider[ch] = 4800;
|
||||||
IntervalClockDivider[ch] = 4;
|
IntervalClockDivider[ch] = 4;
|
||||||
|
@ -170,18 +180,24 @@ void VSU::Write(int timestamp, unsigned int A, unsigned char V)
|
||||||
Frequency[ch] |= V << 0;
|
Frequency[ch] |= V << 0;
|
||||||
EffFreq[ch] &= 0xFF00;
|
EffFreq[ch] &= 0xFF00;
|
||||||
EffFreq[ch] |= V << 0;
|
EffFreq[ch] |= V << 0;
|
||||||
|
ModLock = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x3: Frequency[ch] &= 0x00FF;
|
case 0x3: Frequency[ch] &= 0x00FF;
|
||||||
Frequency[ch] |= (V & 0x7) << 8;
|
Frequency[ch] |= (V & 0x7) << 8;
|
||||||
EffFreq[ch] &= 0x00FF;
|
EffFreq[ch] &= 0x00FF;
|
||||||
EffFreq[ch] |= (V & 0x7) << 8;
|
EffFreq[ch] |= (V & 0x7) << 8;
|
||||||
|
ModLock = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x4: EnvControl[ch] &= 0xFF00;
|
case 0x4: EnvControl[ch] &= 0xFF00;
|
||||||
EnvControl[ch] |= V << 0;
|
EnvControl[ch] |= V << 0;
|
||||||
|
|
||||||
Envelope[ch] = (V >> 4) & 0xF;
|
EnvelopeReload[ch] = (V >> 4) & 0xF;
|
||||||
|
EnvelopeValue[ch] = (V >> 4) & 0xF;
|
||||||
|
|
||||||
|
if(EnvelopeModMask[ch] == 1)
|
||||||
|
EnvelopeModMask[ch] = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x5: EnvControl[ch] &= 0x00FF;
|
case 0x5: EnvControl[ch] &= 0x00FF;
|
||||||
|
@ -194,6 +210,12 @@ void VSU::Write(int timestamp, unsigned int A, unsigned char V)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
EnvControl[ch] |= (V & 0x03) << 8;
|
EnvControl[ch] |= (V & 0x03) << 8;
|
||||||
|
|
||||||
|
if(EnvelopeModMask[ch] == 0 && !(EnvControl[ch] & 0x200) && (
|
||||||
|
(EnvelopeValue[ch] == 0 && !(EnvControl[ch] & 0x0008)) ||
|
||||||
|
(EnvelopeValue[ch] == 0xF && (EnvControl[ch] & 0x0008))))
|
||||||
|
EnvelopeModMask[ch] = 1;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x6: RAMAddress[ch] = V & 0xF;
|
case 0x6: RAMAddress[ch] = V & 0xF;
|
||||||
|
@ -228,14 +250,14 @@ inline void VSU::CalcCurrentOutput(int ch, int &left, int &right)
|
||||||
else
|
else
|
||||||
WD = WaveData[RAMAddress[ch]][WavePos[ch]]; // - 0x20;
|
WD = WaveData[RAMAddress[ch]][WavePos[ch]]; // - 0x20;
|
||||||
}
|
}
|
||||||
l_ol = Envelope[ch] * LeftLevel[ch];
|
l_ol = EnvelopeValue[ch] * LeftLevel[ch];
|
||||||
if(l_ol)
|
if(l_ol)
|
||||||
{
|
{
|
||||||
l_ol >>= 3;
|
l_ol >>= 3;
|
||||||
l_ol += 1;
|
l_ol += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
r_ol = Envelope[ch] * RightLevel[ch];
|
r_ol = EnvelopeValue[ch] * RightLevel[ch];
|
||||||
if(r_ol)
|
if(r_ol)
|
||||||
{
|
{
|
||||||
r_ol >>= 3;
|
r_ol >>= 3;
|
||||||
|
@ -355,23 +377,27 @@ void VSU::Update(int timestamp)
|
||||||
{
|
{
|
||||||
EnvelopeClockDivider[ch] += 4;
|
EnvelopeClockDivider[ch] += 4;
|
||||||
|
|
||||||
|
int new_envelope = EnvelopeValue[ch];
|
||||||
|
if(EnvelopeValue[ch] < 0xF && (EnvControl[ch] & 0x0008))
|
||||||
|
new_envelope++;
|
||||||
|
else if(EnvelopeValue[ch] > 0 && !(EnvControl[ch] & 0x0008))
|
||||||
|
new_envelope--;
|
||||||
|
else if((EnvControl[ch] & 0x200) && EnvelopeModMask[ch] != 2)
|
||||||
|
{
|
||||||
|
new_envelope = EnvelopeReload[ch];
|
||||||
|
EnvelopeModMask[ch] = 0;
|
||||||
|
}
|
||||||
|
else if(EnvelopeModMask[ch] == 0)
|
||||||
|
EnvelopeModMask[ch] = 1;
|
||||||
|
|
||||||
if(EnvControl[ch] & 0x0100) // Enveloping enabled?
|
if(EnvControl[ch] & 0x0100) // Enveloping enabled?
|
||||||
{
|
{
|
||||||
EnvelopeCounter[ch]--;
|
EnvelopeCounter[ch]--;
|
||||||
if(!EnvelopeCounter[ch])
|
if(!EnvelopeCounter[ch])
|
||||||
{
|
{
|
||||||
EnvelopeCounter[ch] = (EnvControl[ch] & 0x7) + 1;
|
EnvelopeCounter[ch] = (EnvControl[ch] & 0x7) + 1;
|
||||||
|
if(EnvelopeModMask[ch] == 0)
|
||||||
if(EnvControl[ch] & 0x0008) // Grow
|
EnvelopeValue[ch] = new_envelope;
|
||||||
{
|
|
||||||
if(Envelope[ch] < 0xF || (EnvControl[ch] & 0x200))
|
|
||||||
Envelope[ch] = (Envelope[ch] + 1) & 0xF;
|
|
||||||
}
|
|
||||||
else // Decay
|
|
||||||
{
|
|
||||||
if(Envelope[ch] > 0 || (EnvControl[ch] & 0x200))
|
|
||||||
Envelope[ch] = (Envelope[ch] - 1) & 0xF;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,6 +406,19 @@ void VSU::Update(int timestamp)
|
||||||
|
|
||||||
if(ch == 4)
|
if(ch == 4)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// Calculate sweep early
|
||||||
|
int delta = EffFreq[ch] >> (SweepControl & 0x7);
|
||||||
|
int NewSweepFreq = EffFreq[ch] + ((SweepControl & 0x8) ? delta : -delta);
|
||||||
|
|
||||||
|
if(!(EnvControl[ch] & 0x1000))
|
||||||
|
{
|
||||||
|
if(NewSweepFreq < 0)
|
||||||
|
NewSweepFreq = 0;
|
||||||
|
else if(NewSweepFreq > 0x7FF)
|
||||||
|
IntlControl[ch] &= ~0x80;
|
||||||
|
}
|
||||||
|
|
||||||
SweepModClockDivider--;
|
SweepModClockDivider--;
|
||||||
while(SweepModClockDivider <= 0)
|
while(SweepModClockDivider <= 0)
|
||||||
{
|
{
|
||||||
|
@ -396,30 +435,27 @@ void VSU::Update(int timestamp)
|
||||||
|
|
||||||
if(EnvControl[ch] & 0x1000) // Modulation
|
if(EnvControl[ch] & 0x1000) // Modulation
|
||||||
{
|
{
|
||||||
if(ModWavePos < 32 || (EnvControl[ch] & 0x2000))
|
if(ModState == 0 || (EnvControl[ch] & 0x2000))
|
||||||
{
|
|
||||||
ModWavePos &= 0x1F;
|
|
||||||
|
|
||||||
EffFreq[ch] = (Frequency[ch] + (signed char)ModData[ModWavePos]) & 0x7FF;
|
EffFreq[ch] = (Frequency[ch] + (signed char)ModData[ModWavePos]) & 0x7FF;
|
||||||
ModWavePos++;
|
if(ModState == 1)
|
||||||
}
|
ModState = 2;
|
||||||
}
|
|
||||||
else // Sweep
|
|
||||||
{
|
|
||||||
int delta = EffFreq[ch] >> (SweepControl & 0x7);
|
|
||||||
int NewFreq = EffFreq[ch] + ((SweepControl & 0x8) ? delta : -delta);
|
|
||||||
|
|
||||||
//printf("Sweep(%d): Old: %d, New: %d\n", ch, EffFreq[ch], NewFreq);
|
// Hardware bug: writing to S5FQ* locks the relevant byte when modulating
|
||||||
|
if(ModLock == 1)
|
||||||
if(NewFreq < 0)
|
EffFreq[ch] = (EffFreq[ch] & 0x700) | (Frequency[ch] & 0xFF);
|
||||||
EffFreq[ch] = 0;
|
else if(ModLock == 2)
|
||||||
else if(NewFreq > 0x7FF)
|
EffFreq[ch] = (EffFreq[ch] & 0xFF) | (Frequency[ch] & 0x700);
|
||||||
{
|
|
||||||
//EffFreq[ch] = 0x7FF;
|
|
||||||
IntlControl[ch] &= ~0x80;
|
|
||||||
}
|
}
|
||||||
else
|
else if(ModState < 2) // Sweep
|
||||||
EffFreq[ch] = NewFreq;
|
{
|
||||||
|
EffFreq[ch] = NewSweepFreq;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(++ModWavePos >= 32)
|
||||||
|
{
|
||||||
|
if(ModState == 0)
|
||||||
|
ModState = 1;
|
||||||
|
ModWavePos = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,8 @@ class VSU
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
int EffFreq[6];
|
int EffFreq[6];
|
||||||
int Envelope[6];
|
int EnvelopeValue[6];
|
||||||
|
int EnvelopeReload[6];
|
||||||
|
|
||||||
int WavePos[6];
|
int WavePos[6];
|
||||||
int ModWavePos;
|
int ModWavePos;
|
||||||
|
@ -91,6 +92,12 @@ class VSU
|
||||||
int EnvelopeClockDivider[6];
|
int EnvelopeClockDivider[6];
|
||||||
int SweepModClockDivider;
|
int SweepModClockDivider;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int EnvelopeModMask[6];
|
||||||
|
int ModState;
|
||||||
|
int ModLock;
|
||||||
|
|
||||||
|
private:
|
||||||
int NoiseLatcherClockDivider;
|
int NoiseLatcherClockDivider;
|
||||||
unsigned int NoiseLatcher;
|
unsigned int NoiseLatcher;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef YMF278_HH
|
#ifndef YMF278_HH
|
||||||
#define YMF278_HH
|
#define YMF278_HH
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
|
@ -338,11 +338,13 @@ void pcm_channel::output(output_data &output) const
|
||||||
|
|
||||||
// fetch current sample and add
|
// fetch current sample and add
|
||||||
int16_t sample = fetch_sample();
|
int16_t sample = fetch_sample();
|
||||||
|
int32_t outl = (lvol * sample) >> 15;
|
||||||
|
int32_t outr = (rvol * sample) >> 15;
|
||||||
uint32_t outnum = m_regs.ch_output_channel(m_choffs) * 2;
|
uint32_t outnum = m_regs.ch_output_channel(m_choffs) * 2;
|
||||||
output.data[outnum + 0] += (lvol * sample) >> 15;
|
output.data[outnum + 0] += outl;
|
||||||
output.data[outnum + 1] += (rvol * sample) >> 15;
|
output.data[outnum + 1] += outr;
|
||||||
m_output[outnum + 0] = output.data[outnum + 0];
|
m_output[outnum + 0] = outl;
|
||||||
m_output[outnum + 1] = output.data[outnum + 1];
|
m_output[outnum + 1] = outr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
#include "vb.h"
|
#include "vb.h"
|
||||||
#include "../engine.h"
|
#include "../engine.h"
|
||||||
|
#include "IconsFontAwesome4.h"
|
||||||
|
#include "furIcons.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
//#define rWrite(a,v) pendingWrites[a]=v;
|
//#define rWrite(a,v) pendingWrites[a]=v;
|
||||||
|
@ -252,6 +254,27 @@ void DivPlatformVB::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<6; i++) {
|
||||||
|
if ((chan[i].envHigh&3)==0) {
|
||||||
|
chan[i].hasEnvWarning=0;
|
||||||
|
} else {
|
||||||
|
switch (vb->EnvelopeModMask[i]) {
|
||||||
|
case 0: // envelope OK
|
||||||
|
chan[i].hasEnvWarning=0;
|
||||||
|
break;
|
||||||
|
case 1: // envelope has finished
|
||||||
|
chan[i].hasEnvWarning=21;
|
||||||
|
break;
|
||||||
|
case 2: // can't envelope
|
||||||
|
chan[i].hasEnvWarning=22;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*if (vb->ModLock) {
|
||||||
|
chan[4].hasEnvWarning=4;
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformVB::dispatch(DivCommand c) {
|
int DivPlatformVB::dispatch(DivCommand c) {
|
||||||
|
@ -477,6 +500,16 @@ unsigned short DivPlatformVB::getPan(int ch) {
|
||||||
return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15);
|
return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DivChannelModeHints DivPlatformVB::getModeHints(int ch) {
|
||||||
|
DivChannelModeHints ret;
|
||||||
|
//if (ch>4) return ret;
|
||||||
|
ret.count=1;
|
||||||
|
ret.hint[0]=ICON_FA_EXCLAMATION_TRIANGLE;
|
||||||
|
ret.type[0]=chan[ch].hasEnvWarning;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
DivDispatchOscBuffer* DivPlatformVB::getOscBuffer(int ch) {
|
DivDispatchOscBuffer* DivPlatformVB::getOscBuffer(int ch) {
|
||||||
return oscBuf[ch];
|
return oscBuf[ch];
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ class DivPlatformVB: public DivDispatch {
|
||||||
int antiClickPeriodCount, antiClickWavePos;
|
int antiClickPeriodCount, antiClickWavePos;
|
||||||
unsigned char pan, envLow, envHigh;
|
unsigned char pan, envLow, envHigh;
|
||||||
bool noise, deferredWaveUpdate, intWritten;
|
bool noise, deferredWaveUpdate, intWritten;
|
||||||
|
unsigned char hasEnvWarning;
|
||||||
signed short wave;
|
signed short wave;
|
||||||
DivWaveSynth ws;
|
DivWaveSynth ws;
|
||||||
Channel():
|
Channel():
|
||||||
|
@ -42,6 +43,7 @@ class DivPlatformVB: public DivDispatch {
|
||||||
noise(false),
|
noise(false),
|
||||||
deferredWaveUpdate(false),
|
deferredWaveUpdate(false),
|
||||||
intWritten(false),
|
intWritten(false),
|
||||||
|
hasEnvWarning(0),
|
||||||
wave(-1) {}
|
wave(-1) {}
|
||||||
};
|
};
|
||||||
Channel chan[6];
|
Channel chan[6];
|
||||||
|
@ -78,6 +80,7 @@ class DivPlatformVB: public DivDispatch {
|
||||||
void* getChanState(int chan);
|
void* getChanState(int chan);
|
||||||
DivMacroInt* getChanMacroInt(int ch);
|
DivMacroInt* getChanMacroInt(int ch);
|
||||||
unsigned short getPan(int chan);
|
unsigned short getPan(int chan);
|
||||||
|
DivChannelModeHints getModeHints(int chan);
|
||||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||||
unsigned char* getRegisterPool();
|
unsigned char* getRegisterPool();
|
||||||
int getRegisterPoolSize();
|
int getRegisterPoolSize();
|
||||||
|
|
|
@ -284,13 +284,13 @@ void DivPlatformVIC20::muteChannel(int ch, bool mute) {
|
||||||
void DivPlatformVIC20::forceIns() {
|
void DivPlatformVIC20::forceIns() {
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
chan[i].insChanged=true;
|
chan[i].insChanged=true;
|
||||||
// I give up!
|
if (chan[i].onOff && chan[i].active) {
|
||||||
if (chan[i].onOff) {
|
|
||||||
chan[i].freqChanged=true;
|
chan[i].freqChanged=true;
|
||||||
} else {
|
} else {
|
||||||
chan[i].freqChanged=false;
|
chan[i].freqChanged=false;
|
||||||
chan[i].keyOff=true;
|
chan[i].keyOff=true;
|
||||||
chan[i].keyOn=false;
|
chan[i].keyOn=false;
|
||||||
|
chan[i].waveWriteCycle=-1;
|
||||||
}
|
}
|
||||||
writeOutVol(i);
|
writeOutVol(i);
|
||||||
}
|
}
|
||||||
|
|
|
@ -418,6 +418,9 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
|
||||||
case DIV_CMD_STD_NOISE_MODE:
|
case DIV_CMD_STD_NOISE_MODE:
|
||||||
if ((c.chan!=2) && (!chan[c.chan].pcm)) { // pulse
|
if ((c.chan!=2) && (!chan[c.chan].pcm)) { // pulse
|
||||||
chan[c.chan].duty=c.value;
|
chan[c.chan].duty=c.value;
|
||||||
|
if (!isMuted[c.chan]) { // pulse
|
||||||
|
chWrite(c.chan,0,(chan[c.chan].outVol&0xf)|((chan[c.chan].duty&7)<<4));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_SAMPLE_MODE:
|
case DIV_CMD_SAMPLE_MODE:
|
||||||
|
|
|
@ -1577,6 +1577,9 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
||||||
cycles++;
|
cycles++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't let user play anything during export
|
||||||
|
if (exporting) pendingNotes.clear();
|
||||||
|
|
||||||
if (!pendingNotes.empty()) {
|
if (!pendingNotes.empty()) {
|
||||||
bool isOn[DIV_MAX_CHANS];
|
bool isOn[DIV_MAX_CHANS];
|
||||||
memset(isOn,0,DIV_MAX_CHANS*sizeof(bool));
|
memset(isOn,0,DIV_MAX_CHANS*sizeof(bool));
|
||||||
|
@ -1616,6 +1619,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
||||||
}
|
}
|
||||||
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,note.channel,note.note));
|
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,note.channel,note.note));
|
||||||
keyHit[note.channel]=true;
|
keyHit[note.channel]=true;
|
||||||
|
chan[note.channel].note = note.note;
|
||||||
chan[note.channel].releasing=false;
|
chan[note.channel].releasing=false;
|
||||||
chan[note.channel].noteOnInhibit=true;
|
chan[note.channel].noteOnInhibit=true;
|
||||||
chan[note.channel].lastIns=note.ins;
|
chan[note.channel].lastIns=note.ins;
|
||||||
|
@ -2248,7 +2252,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int ins=-1;
|
int ins=-1;
|
||||||
if ((ins=midiCallback(msg))!=-2) {
|
if ((ins=midiCallback(msg))!=-3) {
|
||||||
int chan=msg.type&15;
|
int chan=msg.type&15;
|
||||||
switch (msg.type&0xf0) {
|
switch (msg.type&0xf0) {
|
||||||
case TA_MIDI_NOTE_OFF: {
|
case TA_MIDI_NOTE_OFF: {
|
||||||
|
@ -2298,7 +2302,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
||||||
}
|
}
|
||||||
|
|
||||||
// process sample/wave preview
|
// process sample/wave preview
|
||||||
if ((sPreview.sample>=0 && sPreview.sample<(int)song.sample.size()) || (sPreview.wave>=0 && sPreview.wave<(int)song.wave.size())) {
|
if (((sPreview.sample>=0 && sPreview.sample<(int)song.sample.size()) || (sPreview.wave>=0 && sPreview.wave<(int)song.wave.size())) && !exporting) {
|
||||||
unsigned int samp_bbOff=0;
|
unsigned int samp_bbOff=0;
|
||||||
unsigned int prevAvail=blip_samples_avail(samp_bb);
|
unsigned int prevAvail=blip_samples_avail(samp_bb);
|
||||||
if (prevAvail>size) prevAvail=size;
|
if (prevAvail>size) prevAvail=size;
|
||||||
|
|
|
@ -291,6 +291,9 @@ int DivSample::getSampleOffset(int offset, int length, DivSampleDepth depth) {
|
||||||
case DIV_SAMPLE_DEPTH_16BIT:
|
case DIV_SAMPLE_DEPTH_16BIT:
|
||||||
off=offset*2;
|
off=offset*2;
|
||||||
break;
|
break;
|
||||||
|
case DIV_SAMPLE_DEPTH_4BIT:
|
||||||
|
off=(offset+1)/2;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -355,6 +358,10 @@ int DivSample::getSampleOffset(int offset, int length, DivSampleDepth depth) {
|
||||||
off=((offset*3)+1)/2;
|
off=((offset*3)+1)/2;
|
||||||
len=((length*3)+1)/2;
|
len=((length*3)+1)/2;
|
||||||
break;
|
break;
|
||||||
|
case DIV_SAMPLE_DEPTH_4BIT:
|
||||||
|
off=(offset+1)/2;
|
||||||
|
len=(length+1)/2;
|
||||||
|
break;
|
||||||
case DIV_SAMPLE_DEPTH_16BIT:
|
case DIV_SAMPLE_DEPTH_16BIT:
|
||||||
off=offset*2;
|
off=offset*2;
|
||||||
len=length*2;
|
len=length*2;
|
||||||
|
@ -419,6 +426,9 @@ int DivSample::getEndPosition(DivSampleDepth depth) {
|
||||||
case DIV_SAMPLE_DEPTH_12BIT:
|
case DIV_SAMPLE_DEPTH_12BIT:
|
||||||
off=length12;
|
off=length12;
|
||||||
break;
|
break;
|
||||||
|
case DIV_SAMPLE_DEPTH_4BIT:
|
||||||
|
off=length4;
|
||||||
|
break;
|
||||||
case DIV_SAMPLE_DEPTH_16BIT:
|
case DIV_SAMPLE_DEPTH_16BIT:
|
||||||
off=length16;
|
off=length16;
|
||||||
break;
|
break;
|
||||||
|
@ -622,6 +632,12 @@ bool DivSample::initInternal(DivSampleDepth d, int count) {
|
||||||
data12=new unsigned char[length12+8];
|
data12=new unsigned char[length12+8];
|
||||||
memset(data12,0,length12+8);
|
memset(data12,0,length12+8);
|
||||||
break;
|
break;
|
||||||
|
case DIV_SAMPLE_DEPTH_4BIT:
|
||||||
|
if (data4!=NULL) delete[] data4;
|
||||||
|
length4=(count+1)/2;
|
||||||
|
data4=new unsigned char[length4];
|
||||||
|
memset(data4,0,length4);
|
||||||
|
break;
|
||||||
case DIV_SAMPLE_DEPTH_16BIT: // 16-bit
|
case DIV_SAMPLE_DEPTH_16BIT: // 16-bit
|
||||||
if (data16!=NULL) delete[] data16;
|
if (data16!=NULL) delete[] data16;
|
||||||
length16=count*2;
|
length16=count*2;
|
||||||
|
@ -860,6 +876,9 @@ void DivSample::convert(DivSampleDepth newDepth, unsigned int formatMask) {
|
||||||
case DIV_SAMPLE_DEPTH_VOX: // VOX
|
case DIV_SAMPLE_DEPTH_VOX: // VOX
|
||||||
setSampleCount((samples+1)&(~1));
|
setSampleCount((samples+1)&(~1));
|
||||||
break;
|
break;
|
||||||
|
case DIV_SAMPLE_DEPTH_4BIT:
|
||||||
|
setSampleCount((samples+1)&(~1));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1317,6 +1336,18 @@ void DivSample::render(unsigned int formatMask) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case DIV_SAMPLE_DEPTH_4BIT: {
|
||||||
|
unsigned short nibble=0;
|
||||||
|
for (unsigned int i=0; i<samples; i++) {
|
||||||
|
if (i&1) {
|
||||||
|
nibble=data4[i>>1]&0xf;
|
||||||
|
} else {
|
||||||
|
nibble=data4[i>>1]>>4;
|
||||||
|
}
|
||||||
|
data16[i]=((nibble<<12)|(nibble<<8)|(nibble<<4)|nibble)^0x8000;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1518,6 +1549,20 @@ void DivSample::render(unsigned int formatMask) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_4BIT)) {
|
||||||
|
if (!initInternal(DIV_SAMPLE_DEPTH_4BIT,samples)) return;
|
||||||
|
unsigned char _sample=0, sample4=0;
|
||||||
|
unsigned short* samplePtr = (unsigned short*)data16;
|
||||||
|
for (unsigned int i=0; i<samples; i+=2) {
|
||||||
|
_sample=(*samplePtr++^0x8000)>>12;
|
||||||
|
sample4=_sample<<4;
|
||||||
|
if (i+1<samples) {
|
||||||
|
_sample=(*samplePtr++^0x8000)>>12;
|
||||||
|
sample4|=_sample;
|
||||||
|
}
|
||||||
|
data4[i>>1]=sample4;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* DivSample::getCurBuf() {
|
void* DivSample::getCurBuf() {
|
||||||
|
@ -1550,6 +1595,8 @@ void* DivSample::getCurBuf() {
|
||||||
return dataIMA;
|
return dataIMA;
|
||||||
case DIV_SAMPLE_DEPTH_12BIT:
|
case DIV_SAMPLE_DEPTH_12BIT:
|
||||||
return data12;
|
return data12;
|
||||||
|
case DIV_SAMPLE_DEPTH_4BIT:
|
||||||
|
return data4;
|
||||||
case DIV_SAMPLE_DEPTH_16BIT:
|
case DIV_SAMPLE_DEPTH_16BIT:
|
||||||
return data16;
|
return data16;
|
||||||
default:
|
default:
|
||||||
|
@ -1588,6 +1635,8 @@ unsigned int DivSample::getCurBufLen() {
|
||||||
return lengthIMA;
|
return lengthIMA;
|
||||||
case DIV_SAMPLE_DEPTH_12BIT:
|
case DIV_SAMPLE_DEPTH_12BIT:
|
||||||
return length12;
|
return length12;
|
||||||
|
case DIV_SAMPLE_DEPTH_4BIT:
|
||||||
|
return length4;
|
||||||
case DIV_SAMPLE_DEPTH_16BIT:
|
case DIV_SAMPLE_DEPTH_16BIT:
|
||||||
return length16;
|
return length16;
|
||||||
default:
|
default:
|
||||||
|
@ -1703,4 +1752,5 @@ DivSample::~DivSample() {
|
||||||
if (dataC219) delete[] dataC219;
|
if (dataC219) delete[] dataC219;
|
||||||
if (dataIMA) delete[] dataIMA;
|
if (dataIMA) delete[] dataIMA;
|
||||||
if (data12) delete[] data12;
|
if (data12) delete[] data12;
|
||||||
|
if (data4) delete[] data4;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ enum DivSampleDepth: unsigned char {
|
||||||
DIV_SAMPLE_DEPTH_C219=12,
|
DIV_SAMPLE_DEPTH_C219=12,
|
||||||
DIV_SAMPLE_DEPTH_IMA_ADPCM=13,
|
DIV_SAMPLE_DEPTH_IMA_ADPCM=13,
|
||||||
DIV_SAMPLE_DEPTH_12BIT=14,
|
DIV_SAMPLE_DEPTH_12BIT=14,
|
||||||
|
DIV_SAMPLE_DEPTH_4BIT=15,
|
||||||
DIV_SAMPLE_DEPTH_16BIT=16,
|
DIV_SAMPLE_DEPTH_16BIT=16,
|
||||||
DIV_SAMPLE_DEPTH_MAX // boundary for sample depth
|
DIV_SAMPLE_DEPTH_MAX // boundary for sample depth
|
||||||
};
|
};
|
||||||
|
@ -147,8 +148,9 @@ struct DivSample {
|
||||||
unsigned char* dataC219; // 12
|
unsigned char* dataC219; // 12
|
||||||
unsigned char* dataIMA; // 13
|
unsigned char* dataIMA; // 13
|
||||||
unsigned char* data12; // 14
|
unsigned char* data12; // 14
|
||||||
|
unsigned char* data4; // 15
|
||||||
|
|
||||||
unsigned int length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthK, lengthBRR, lengthVOX, lengthMuLaw, lengthC219, lengthIMA, length12;
|
unsigned int length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthK, lengthBRR, lengthVOX, lengthMuLaw, lengthC219, lengthIMA, length12, length4;
|
||||||
|
|
||||||
unsigned int samples;
|
unsigned int samples;
|
||||||
|
|
||||||
|
@ -360,6 +362,7 @@ struct DivSample {
|
||||||
dataC219(NULL),
|
dataC219(NULL),
|
||||||
dataIMA(NULL),
|
dataIMA(NULL),
|
||||||
data12(NULL),
|
data12(NULL),
|
||||||
|
data4(NULL),
|
||||||
length8(0),
|
length8(0),
|
||||||
length16(0),
|
length16(0),
|
||||||
length1(0),
|
length1(0),
|
||||||
|
@ -375,6 +378,7 @@ struct DivSample {
|
||||||
lengthC219(0),
|
lengthC219(0),
|
||||||
lengthIMA(0),
|
lengthIMA(0),
|
||||||
length12(0),
|
length12(0),
|
||||||
|
length4(0),
|
||||||
samples(0) {
|
samples(0) {
|
||||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
for (int j=0; j<DIV_MAX_SAMPLE_TYPE; j++) {
|
for (int j=0; j<DIV_MAX_SAMPLE_TYPE; j++) {
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
// this function is so long
|
// this function is so long
|
||||||
// may as well make it something else
|
// may as well make it something else
|
||||||
void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream, bool* sampleStoppable, bool dpcm07, DivDispatch** writeNES) {
|
void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream, bool* sampleStoppable, bool dpcm07, DivDispatch** writeNES, int rateCorrection) {
|
||||||
unsigned char baseAddr1=isSecond?0xa0:0x50;
|
unsigned char baseAddr1=isSecond?0xa0:0x50;
|
||||||
unsigned char baseAddr2=isSecond?0x80:0;
|
unsigned char baseAddr2=isSecond?0x80:0;
|
||||||
unsigned short baseAddr2S=isSecond?0x8000:0;
|
unsigned short baseAddr2S=isSecond?0x8000:0;
|
||||||
|
@ -188,6 +188,8 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
case DIV_SYSTEM_YM2610_EXT:
|
case DIV_SYSTEM_YM2610_EXT:
|
||||||
case DIV_SYSTEM_YM2610_FULL_EXT:
|
case DIV_SYSTEM_YM2610_FULL_EXT:
|
||||||
case DIV_SYSTEM_YM2610B_EXT:
|
case DIV_SYSTEM_YM2610B_EXT:
|
||||||
|
case DIV_SYSTEM_YM2610_CSM:
|
||||||
|
case DIV_SYSTEM_YM2610B_CSM:
|
||||||
// TODO: YM2610B channels 1 and 4 and ADPCM-B
|
// TODO: YM2610B channels 1 and 4 and ADPCM-B
|
||||||
for (int i=0; i<2; i++) { // set SL and RR to highest
|
for (int i=0; i<2; i++) { // set SL and RR to highest
|
||||||
w->writeC(8|baseAddr1);
|
w->writeC(8|baseAddr1);
|
||||||
|
@ -264,6 +266,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_YM2203:
|
case DIV_SYSTEM_YM2203:
|
||||||
case DIV_SYSTEM_YM2203_EXT:
|
case DIV_SYSTEM_YM2203_EXT:
|
||||||
|
case DIV_SYSTEM_YM2203_CSM:
|
||||||
for (int i=0; i<3; i++) { // set SL and RR to highest
|
for (int i=0; i<3; i++) { // set SL and RR to highest
|
||||||
w->writeC(5|baseAddr1);
|
w->writeC(5|baseAddr1);
|
||||||
w->writeC(0x80+i);
|
w->writeC(0x80+i);
|
||||||
|
@ -795,7 +798,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
break;
|
break;
|
||||||
case 1: { // set sample freq
|
case 1: { // set sample freq
|
||||||
sampleStoppable[streamID]=true;
|
sampleStoppable[streamID]=true;
|
||||||
int realFreq=write.val;
|
int realFreq=(write.val*44100)/rateCorrection;
|
||||||
if (realFreq<0) realFreq=0;
|
if (realFreq<0) realFreq=0;
|
||||||
if (realFreq>44100) realFreq=44100;
|
if (realFreq>44100) realFreq=44100;
|
||||||
w->writeC(0x92);
|
w->writeC(0x92);
|
||||||
|
@ -977,6 +980,8 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
case DIV_SYSTEM_YM2610_EXT:
|
case DIV_SYSTEM_YM2610_EXT:
|
||||||
case DIV_SYSTEM_YM2610_FULL_EXT:
|
case DIV_SYSTEM_YM2610_FULL_EXT:
|
||||||
case DIV_SYSTEM_YM2610B_EXT:
|
case DIV_SYSTEM_YM2610B_EXT:
|
||||||
|
case DIV_SYSTEM_YM2610_CSM:
|
||||||
|
case DIV_SYSTEM_YM2610B_CSM:
|
||||||
switch (write.addr>>8) {
|
switch (write.addr>>8) {
|
||||||
case 0: // port 0
|
case 0: // port 0
|
||||||
w->writeC(8|baseAddr1);
|
w->writeC(8|baseAddr1);
|
||||||
|
@ -992,12 +997,14 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_YM2203:
|
case DIV_SYSTEM_YM2203:
|
||||||
case DIV_SYSTEM_YM2203_EXT:
|
case DIV_SYSTEM_YM2203_EXT:
|
||||||
|
case DIV_SYSTEM_YM2203_CSM:
|
||||||
w->writeC(5|baseAddr1);
|
w->writeC(5|baseAddr1);
|
||||||
w->writeC(write.addr&0xff);
|
w->writeC(write.addr&0xff);
|
||||||
w->writeC(write.val);
|
w->writeC(write.val);
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_YM2608:
|
case DIV_SYSTEM_YM2608:
|
||||||
case DIV_SYSTEM_YM2608_EXT:
|
case DIV_SYSTEM_YM2608_EXT:
|
||||||
|
case DIV_SYSTEM_YM2608_CSM:
|
||||||
switch (write.addr>>8) {
|
switch (write.addr>>8) {
|
||||||
case 0: // port 0
|
case 0: // port 0
|
||||||
w->writeC(6|baseAddr1);
|
w->writeC(6|baseAddr1);
|
||||||
|
@ -1233,7 +1240,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
chipVol.push_back((_id)|(0x80000100)|(((unsigned int)_vol)<<16)); \
|
chipVol.push_back((_id)|(0x80000100)|(((unsigned int)_vol)<<16)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool patternHints, bool directStream, int trailingTicks, bool dpcm07) {
|
SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool patternHints, bool directStream, int trailingTicks, bool dpcm07, int correctedRate) {
|
||||||
if (version<0x150) {
|
if (version<0x150) {
|
||||||
lastError="VGM version is too low";
|
lastError="VGM version is too low";
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1243,7 +1250,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
setOrder(0);
|
setOrder(0);
|
||||||
BUSY_BEGIN_SOFT;
|
BUSY_BEGIN_SOFT;
|
||||||
double origRate=got.rate;
|
double origRate=got.rate;
|
||||||
got.rate=44100;
|
got.rate=correctedRate;
|
||||||
// determine loop point
|
// determine loop point
|
||||||
int loopOrder=0;
|
int loopOrder=0;
|
||||||
int loopRow=0;
|
int loopRow=0;
|
||||||
|
@ -1516,6 +1523,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
case DIV_SYSTEM_YM2610_EXT:
|
case DIV_SYSTEM_YM2610_EXT:
|
||||||
case DIV_SYSTEM_YM2610_FULL_EXT:
|
case DIV_SYSTEM_YM2610_FULL_EXT:
|
||||||
case DIV_SYSTEM_YM2610B_EXT:
|
case DIV_SYSTEM_YM2610B_EXT:
|
||||||
|
case DIV_SYSTEM_YM2610_CSM:
|
||||||
|
case DIV_SYSTEM_YM2610B_CSM:
|
||||||
if (!hasOPNB) {
|
if (!hasOPNB) {
|
||||||
hasOPNB=disCont[i].dispatch->chipClock;
|
hasOPNB=disCont[i].dispatch->chipClock;
|
||||||
CHIP_VOL(8,1.0);
|
CHIP_VOL(8,1.0);
|
||||||
|
@ -1531,7 +1540,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
hasOPNB|=0x40000000;
|
hasOPNB|=0x40000000;
|
||||||
howManyChips++;
|
howManyChips++;
|
||||||
}
|
}
|
||||||
if (((song.system[i]==DIV_SYSTEM_YM2610B) || (song.system[i]==DIV_SYSTEM_YM2610B_EXT)) && (!(hasOPNB&0x80000000))) { // YM2610B flag
|
if (((song.system[i]==DIV_SYSTEM_YM2610B) || (song.system[i]==DIV_SYSTEM_YM2610B_EXT) || (song.system[i]==DIV_SYSTEM_YM2610B_CSM)) && (!(hasOPNB&0x80000000))) { // YM2610B flag
|
||||||
hasOPNB|=0x80000000;
|
hasOPNB|=0x80000000;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1627,6 +1636,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_YM2203:
|
case DIV_SYSTEM_YM2203:
|
||||||
case DIV_SYSTEM_YM2203_EXT:
|
case DIV_SYSTEM_YM2203_EXT:
|
||||||
|
case DIV_SYSTEM_YM2203_CSM:
|
||||||
if (!hasOPN) {
|
if (!hasOPN) {
|
||||||
hasOPN=disCont[i].dispatch->chipClock;
|
hasOPN=disCont[i].dispatch->chipClock;
|
||||||
willExport[i]=true;
|
willExport[i]=true;
|
||||||
|
@ -1643,6 +1653,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_YM2608:
|
case DIV_SYSTEM_YM2608:
|
||||||
case DIV_SYSTEM_YM2608_EXT:
|
case DIV_SYSTEM_YM2608_EXT:
|
||||||
|
case DIV_SYSTEM_YM2608_CSM:
|
||||||
if (!hasOPNA) {
|
if (!hasOPNA) {
|
||||||
hasOPNA=disCont[i].dispatch->chipClock;
|
hasOPNA=disCont[i].dispatch->chipClock;
|
||||||
CHIP_VOL(7,1.0);
|
CHIP_VOL(7,1.0);
|
||||||
|
@ -2849,7 +2860,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
lastOne=i.second.time;
|
lastOne=i.second.time;
|
||||||
}
|
}
|
||||||
// write write
|
// write write
|
||||||
performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],pendingFreq,playingSample,setPos,sampleOff8,sampleLen8,bankOffset[i.first],directStream,sampleStoppable,dpcm07,writeNES);
|
performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],pendingFreq,playingSample,setPos,sampleOff8,sampleLen8,bankOffset[i.first],directStream,sampleStoppable,dpcm07,writeNES,correctedRate);
|
||||||
writeCount++;
|
writeCount++;
|
||||||
}
|
}
|
||||||
sortedWrites.clear();
|
sortedWrites.clear();
|
||||||
|
|
|
@ -145,7 +145,9 @@ void DivEngine::runExportThread() {
|
||||||
|
|
||||||
// take control of audio output
|
// take control of audio output
|
||||||
deinitAudioBackend();
|
deinitAudioBackend();
|
||||||
|
freelance=false;
|
||||||
playSub(false);
|
playSub(false);
|
||||||
|
freelance=false;
|
||||||
|
|
||||||
logI("rendering to file...");
|
logI("rendering to file...");
|
||||||
|
|
||||||
|
@ -244,7 +246,9 @@ void DivEngine::runExportThread() {
|
||||||
|
|
||||||
// take control of audio output
|
// take control of audio output
|
||||||
deinitAudioBackend();
|
deinitAudioBackend();
|
||||||
|
freelance=false;
|
||||||
playSub(false);
|
playSub(false);
|
||||||
|
freelance=false;
|
||||||
|
|
||||||
logI("rendering to files...");
|
logI("rendering to files...");
|
||||||
|
|
||||||
|
@ -380,7 +384,9 @@ void DivEngine::runExportThread() {
|
||||||
totalLoops=0;
|
totalLoops=0;
|
||||||
isFadingOut=false;
|
isFadingOut=false;
|
||||||
remainingLoops=-1;
|
remainingLoops=-1;
|
||||||
|
freelance=false;
|
||||||
playSub(false);
|
playSub(false);
|
||||||
|
freelance=false;
|
||||||
|
|
||||||
while (playing) {
|
while (playing) {
|
||||||
size_t total=0;
|
size_t total=0;
|
||||||
|
|
|
@ -35,12 +35,14 @@ const char* aboutLine[]={
|
||||||
_N("-- program --"),
|
_N("-- program --"),
|
||||||
"tildearrow",
|
"tildearrow",
|
||||||
_N("A M 4 N (intro tune)"),
|
_N("A M 4 N (intro tune)"),
|
||||||
|
"AArt1256",
|
||||||
"Adam Lederer",
|
"Adam Lederer",
|
||||||
"akumanatt",
|
"akumanatt",
|
||||||
"asiekierka",
|
"asiekierka",
|
||||||
"cam900",
|
"cam900",
|
||||||
"djtuBIG-MaliceX",
|
"djtuBIG-MaliceX",
|
||||||
"Eknous",
|
"Eknous",
|
||||||
|
"host12prog",
|
||||||
"Kagamiin~",
|
"Kagamiin~",
|
||||||
"laoo",
|
"laoo",
|
||||||
"LTVA",
|
"LTVA",
|
||||||
|
@ -71,6 +73,7 @@ const char* aboutLine[]={
|
||||||
_N("-- localization/translation team --"),
|
_N("-- localization/translation team --"),
|
||||||
"Bahasa Indonesia: ZoomTen (Zumi)",
|
"Bahasa Indonesia: ZoomTen (Zumi)",
|
||||||
"Español: CrimsonZN, ThaCuber, tildearrow",
|
"Español: CrimsonZN, ThaCuber, tildearrow",
|
||||||
|
"Français: fouinne44",
|
||||||
"Հայերեն: Eknous",
|
"Հայերեն: Eknous",
|
||||||
"한국어: Heemin, leejh20, Nicknamé",
|
"한국어: Heemin, leejh20, Nicknamé",
|
||||||
"Nederlands: Lunathir",
|
"Nederlands: Lunathir",
|
||||||
|
@ -90,6 +93,7 @@ const char* aboutLine[]={
|
||||||
"Lumigado",
|
"Lumigado",
|
||||||
"Lunathir",
|
"Lunathir",
|
||||||
"plane",
|
"plane",
|
||||||
|
"skyfloogle",
|
||||||
"TheEssem",
|
"TheEssem",
|
||||||
"",
|
"",
|
||||||
_N("-- Metal backend test team --"),
|
_N("-- Metal backend test team --"),
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue