diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 115571cf1..e1328c322 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,7 @@ name: Build furnace on: push: - branches: [master, esfm-contest] + branches: master pull_request: branches: master @@ -22,8 +22,8 @@ jobs: - { name: 'Windows MSVC x86_64', os: windows-latest, compiler: msvc, arch: x86_64 } #- { name: 'Windows MinGW x86', os: ubuntu-20.04, compiler: mingw, arch: x86 } #- { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 } - #- { name: 'macOS x86_64', os: macos-latest, arch: x86_64 } - #- { name: 'macOS ARM', os: macos-latest, arch: arm64 } + - { name: 'macOS x86_64', os: macos-latest, arch: x86_64 } + - { name: 'macOS ARM', os: macos-latest, arch: arm64 } - { name: 'Linux x86_64', os: ubuntu-20.04, arch: x86_64 } #- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf } fail-fast: false @@ -33,7 +33,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4.1.1 with: submodules: recursive @@ -106,7 +106,7 @@ jobs: - name: Setup Toolchain [Windows MSVC] if: ${{ matrix.config.compiler == 'msvc' }} - uses: vadz/gha-setup-vsdevenv@avoid-deprecation-warnings + uses: lunathir/gha-setup-vsdevenv@avoid-deprecation-warnings with: arch: ${{ steps.windows-identify.outputs.vswhere-target }} @@ -150,57 +150,6 @@ jobs: libjack-jackd2-dev:armhf ls /usr/arm-linux-gnueabihf/lib - - name: Configure (System Libraries) - if: ${{ runner.os == 'Linux' && matrix.config.compiler != 'mingw' && matrix.config.arch == 'x86_64' }} - run: | - export USE_WAE=ON - export CMAKE_EXTRA_ARGS=() - if [ '${{ matrix.config.compiler }}' == 'msvc' ]; then - CMAKE_EXTRA_ARGS+=('-DCMAKE_GENERATOR_PLATFORM=${{ steps.windows-identify.outputs.msvc-target }}') - elif [ '${{ matrix.config.compiler }}' == 'mingw' ]; then - CMAKE_EXTRA_ARGS+=('-DCMAKE_TOOLCHAIN_FILE=scripts/Cross-MinGW-${{ steps.windows-identify.outputs.mingw-target }}.cmake') - else - # Test with system libs - CMAKE_EXTRA_ARGS+=( - '-DSYSTEM_FMT=OFF' - '-DSYSTEM_LIBSNDFILE=ON' - '-DSYSTEM_RTMIDI=ON' - '-DSYSTEM_ZLIB=ON' - '-DWITH_JACK=ON' - ) - # Too old on Ubuntu - if [ '${{ runner.os }}' == 'macOS' ]; then - CMAKE_EXTRA_ARGS+=('-DSYSTEM_SDL2=ON') - fi - fi - - cmake \ - -B ${PWD}/build \ - -DCMAKE_INSTALL_PREFIX=${PWD}/target \ - -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ - -DWARNINGS_ARE_ERRORS=${USE_WAE} \ - "${CMAKE_EXTRA_ARGS[@]}" - - - name: Build (System Libraries) - if: ${{ runner.os == 'Linux' && matrix.config.compiler != 'mingw' && matrix.config.arch == 'x86_64' }} - run: | - cmake \ - --build ${PWD}/build \ - --config ${{ env.BUILD_TYPE }} \ - --parallel ${{ steps.build-cores.outputs.amount }} - - - name: Install (System Libraries) - if: ${{ runner.os == 'Linux' && matrix.config.compiler != 'mingw' && matrix.config.arch == 'x86_64' }} - run: | - cmake \ - --install ${PWD}/build \ - --config ${{ env.BUILD_TYPE }} - - - name: Cleanup (System Libraries) - if: ${{ runner.os == 'Linux' && matrix.config.compiler != 'mingw' && matrix.config.arch == 'x86_64' }} - run: | - rm -rf build/ target/ - - name: Configure run: | export USE_WAE=ON @@ -284,15 +233,31 @@ jobs: if: ${{ runner.os == 'macOS' }} run: | pushd build - cpack + retries=0 + while ! cpack; do + echo "TRYING AGAIN..." + retries=$((retries+1)) + if [ $retries -gt 5 ]; then + echo "OH NO, WE'VE FAILED..." + exit 1 + break + fi + done + echo "making dirs" mkdir orig mkdir new + echo "attaching" echo "y" | hdiutil attach Furnace-*-Darwin.dmg -readonly -mount required -mountpoint orig + echo "copying" cp -v -r orig/Furnace.app new/Furnace.app + echo "synchronizing" + sync + echo "detaching" hdiutil detach orig + echo "removing orig" rmdir orig rm Furnace-*-Darwin.dmg @@ -301,6 +266,7 @@ jobs: rmdir new/Furnace.app/Contents/Resources/bin fi + echo "copying extra stuff" cp -v ../LICENSE new/LICENSE.txt cp -v ../res/releaseReadme/stable-mac.txt new/README cp -v -r ../demos new/demos @@ -310,7 +276,18 @@ jobs: wget https://tildearrow.org/furnace/doc/latest/manual.pdf cd .. - hdiutil create -srcfolder new -volname Furnace -format UDZO furnace.dmg + echo "creating new image" + retries=0 + while ! hdiutil create -srcfolder new -volname Furnace -format UDZO furnace.dmg; do + echo "TRYING AGAIN..." + retries=$((retries+1)) + if [ $retries -gt 5 ]; then + echo "OH NO, WE'VE FAILED..." + exit 1 + break + fi + sleep 5 + done mv furnace.dmg ../${{ steps.package-identify.outputs.filename }} popd @@ -356,7 +333,7 @@ jobs: - name: Upload artifact if: ${{ github.repository == 'tildearrow/furnace' && github.ref_name == 'master' }} - uses: actions/upload-artifact@v3.1.1 + uses: actions/upload-artifact@v4.3.0 with: name: ${{ steps.package-identify.outputs.id }} path: ${{ steps.package-identify.outputs.filename }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a3f8e641..2490246a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_PROJECT_VERSION_MAJOR 0) set(CMAKE_PROJECT_VERSION_MINOR 6) -set(CMAKE_PROJECT_VERSION_PATCH 0) +set(CMAKE_PROJECT_VERSION_PATCH 1) set(BUILD_GUI_DEFAULT ON) set(USE_SDL2_DEFAULT ON) @@ -198,14 +198,14 @@ if (BUILD_GUI AND USE_FREETYPE) # TODO: FIX FIX FIX FIX FIX if (SYSTEM_FREETYPE) if (PKG_CONFIG_FOUND) - pkg_check_modules(FREETYPE freetype>=2.0.0) + pkg_check_modules(FREETYPE freetype2>=2.0.0) if (FREETYPE_FOUND) - list(APPEND DEPENDENCIES_INCLUDE_DIRS ${FMT_INCLUDE_DIRS}) - list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${FMT_CFLAGS_OTHER}) - list(APPEND DEPENDENCIES_LIBRARIES ${FMT_LIBRARIES}) - list(APPEND DEPENDENCIES_LIBRARY_DIRS ${FMT_LIBRARY_DIRS}) - list(APPEND DEPENDENCIES_LINK_OPTIONS ${FMT_LDFLAGS_OTHER}) - list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${FMT_LDFLAGS}) + list(APPEND DEPENDENCIES_INCLUDE_DIRS ${FREETYPE_INCLUDE_DIRS}) + list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${FREETYPE_CFLAGS_OTHER}) + list(APPEND DEPENDENCIES_LIBRARIES ${FREETYPE_LIBRARIES}) + list(APPEND DEPENDENCIES_LIBRARY_DIRS ${FREETYPE_LIBRARY_DIRS}) + list(APPEND DEPENDENCIES_LINK_OPTIONS ${FREETYPE_LDFLAGS_OTHER}) + list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${FREETYPE_LDFLAGS}) endif() endif() if (NOT FREETYPE_FOUND) @@ -495,6 +495,8 @@ extern/YMF276-LLE/fmopn2.c extern/ESFMu/esfm.c extern/ESFMu/esfm_registers.c +extern/pwrnoise/pwrnoise.c + src/pch.cpp src/engine/platform/sound/sn76496.cpp @@ -703,6 +705,7 @@ src/engine/platform/k053260.cpp src/engine/platform/ted.cpp src/engine/platform/c140.cpp src/engine/platform/esfm.cpp +src/engine/platform/powernoise.cpp src/engine/platform/pcmdac.cpp src/engine/platform/dummy.cpp diff --git a/README.md b/README.md index 5abdc8127..3bf73dc14 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ for other operating systems, you may [build the source](#developer-info). - YM3812 (OPL2) - YMF262 (OPL3) with full 4-op support! - Y8950 (OPL with ADPCM) + - ESS ESFM (like OPL3 but with more features) - square wave chips: - AY-3-8910/YM2149(F) used in several computers and game consoles - Commodore VIC used in the VIC-20 @@ -86,6 +87,7 @@ for other operating systems, you may [build the source](#developer-info). - modern/fantasy: - Commander X16 VERA - tildearrow Sound Unit + - PowerNoise - Generic PCM DAC - mix and match sound chips! - over 200 ready to use presets from computers, game consoles and arcade boards... diff --git a/TODO.md b/TODO.md index f1918edef..03e64ae2e 100644 --- a/TODO.md +++ b/TODO.md @@ -1,13 +1,5 @@ # to-do -- finish color import improvements (settings refactor) -- new undo stuff -- fix some bugs - finish auto-clone - -once you have done all of this (maybe not the first one), release 0.6.1 - -# and then - - new oscilloscope renderer - custom code that uses texture and fixes two issues: too many vertices, and broken anti-aliasing - new pattern renderer - performance improvements diff --git a/android/app/build.gradle b/android/app/build.gradle index 1b09780c5..6fed5ed8e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -15,8 +15,8 @@ android { } minSdkVersion 21 targetSdkVersion 26 - versionCode 181 - versionName "0.6" + versionCode 192 + versionName "0.6.1" externalNativeBuild { cmake { arguments "-DANDROID_APP_PLATFORM=android-21", "-DANDROID_STL=c++_static", "-DWARNINGS_ARE_ERRORS=ON" diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a8b2007b1..0c430ea6b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ diff --git a/demos/amiga/trance_dream.fur b/demos/amiga/trance_dream.fur new file mode 100644 index 000000000..553b3bbc8 Binary files /dev/null and b/demos/amiga/trance_dream.fur differ diff --git a/demos/ay8930/One_Minute_Waltz.fur b/demos/ay8930/One_Minute_Waltz.fur new file mode 100644 index 000000000..8295974d9 Binary files /dev/null and b/demos/ay8930/One_Minute_Waltz.fur differ diff --git a/demos/ay8930/PlayingOnTheStairs.fur b/demos/ay8930/PlayingOnTheStairs.fur deleted file mode 100644 index 80c5fd82a..000000000 Binary files a/demos/ay8930/PlayingOnTheStairs.fur and /dev/null differ diff --git a/demos/ay8930/Playing_On_The_Stairs.fur b/demos/ay8930/Playing_On_The_Stairs.fur new file mode 100644 index 000000000..da4e7ddd5 Binary files /dev/null and b/demos/ay8930/Playing_On_The_Stairs.fur differ diff --git a/demos/ay8930/powerful-sample-generator.fur b/demos/ay8930/powerful-sample-generator.fur new file mode 100644 index 000000000..783e5f699 Binary files /dev/null and b/demos/ay8930/powerful-sample-generator.fur differ diff --git a/demos/esfm/AAAA.fur b/demos/esfm/AAAA.fur new file mode 100644 index 000000000..36f29a613 Binary files /dev/null and b/demos/esfm/AAAA.fur differ diff --git a/demos/esfm/ALCATRAZ_ALWAYS.fur b/demos/esfm/ALCATRAZ_ALWAYS.fur new file mode 100644 index 000000000..08d61df24 Binary files /dev/null and b/demos/esfm/ALCATRAZ_ALWAYS.fur differ diff --git a/demos/esfm/CampingJourney.fur b/demos/esfm/CampingJourney.fur new file mode 100644 index 000000000..0eb13e69a Binary files /dev/null and b/demos/esfm/CampingJourney.fur differ diff --git a/demos/esfm/Deadline.fur b/demos/esfm/Deadline.fur new file mode 100644 index 000000000..8231af8f9 Binary files /dev/null and b/demos/esfm/Deadline.fur differ diff --git a/demos/esfm/Devil_Detective.fur b/demos/esfm/Devil_Detective.fur new file mode 100644 index 000000000..8a92094df Binary files /dev/null and b/demos/esfm/Devil_Detective.fur differ diff --git a/demos/esfm/Drinkin_Coffee_Way_Too_Much.fur b/demos/esfm/Drinkin_Coffee_Way_Too_Much.fur new file mode 100644 index 000000000..9b946f186 Binary files /dev/null and b/demos/esfm/Drinkin_Coffee_Way_Too_Much.fur differ diff --git a/demos/esfm/EdgeRunning_2078.fur b/demos/esfm/EdgeRunning_2078.fur new file mode 100644 index 000000000..946d97353 Binary files /dev/null and b/demos/esfm/EdgeRunning_2078.fur differ diff --git a/demos/esfm/Fighting_the_Dimensions_Big_Bad_Because_its_Fun.fur b/demos/esfm/Fighting_the_Dimensions_Big_Bad_Because_its_Fun.fur new file mode 100644 index 000000000..e4144c343 Binary files /dev/null and b/demos/esfm/Fighting_the_Dimensions_Big_Bad_Because_its_Fun.fur differ diff --git a/demos/esfm/I_Cant_Escape_Your_Memory.fur b/demos/esfm/I_Cant_Escape_Your_Memory.fur new file mode 100644 index 000000000..66c5f3eae Binary files /dev/null and b/demos/esfm/I_Cant_Escape_Your_Memory.fur differ diff --git a/demos/esfm/Just_me_and_my_Brain.fur b/demos/esfm/Just_me_and_my_Brain.fur new file mode 100644 index 000000000..5f6e0933e Binary files /dev/null and b/demos/esfm/Just_me_and_my_Brain.fur differ diff --git a/demos/esfm/Ken_Stage.fur b/demos/esfm/Ken_Stage.fur new file mode 100644 index 000000000..b2ecc1d8d Binary files /dev/null and b/demos/esfm/Ken_Stage.fur differ diff --git a/demos/esfm/Magician_Lake_Cover.fur b/demos/esfm/Magician_Lake_Cover.fur new file mode 100644 index 000000000..7e8c00d3b Binary files /dev/null and b/demos/esfm/Magician_Lake_Cover.fur differ diff --git a/demos/esfm/Napalm-Loader.fur b/demos/esfm/Napalm-Loader.fur new file mode 100644 index 000000000..56501f144 Binary files /dev/null and b/demos/esfm/Napalm-Loader.fur differ diff --git a/demos/esfm/Perilous059.fur b/demos/esfm/Perilous059.fur new file mode 100644 index 000000000..abe825a8c Binary files /dev/null and b/demos/esfm/Perilous059.fur differ diff --git a/demos/esfm/Poets_I.fur b/demos/esfm/Poets_I.fur new file mode 100644 index 000000000..7b24ee6ca Binary files /dev/null and b/demos/esfm/Poets_I.fur differ diff --git a/demos/esfm/Redial.fur b/demos/esfm/Redial.fur new file mode 100644 index 000000000..477af17f5 Binary files /dev/null and b/demos/esfm/Redial.fur differ diff --git a/demos/esfm/SearchPrincess.fur b/demos/esfm/SearchPrincess.fur new file mode 100644 index 000000000..9c769b69b Binary files /dev/null and b/demos/esfm/SearchPrincess.fur differ diff --git a/demos/esfm/Second_Start.fur b/demos/esfm/Second_Start.fur new file mode 100644 index 000000000..3473e63dd Binary files /dev/null and b/demos/esfm/Second_Start.fur differ diff --git a/demos/esfm/Strange_Sunset.fur b/demos/esfm/Strange_Sunset.fur new file mode 100644 index 000000000..19328b612 Binary files /dev/null and b/demos/esfm/Strange_Sunset.fur differ diff --git a/demos/esfm/Tangent_to_a_Segue.fur b/demos/esfm/Tangent_to_a_Segue.fur new file mode 100644 index 000000000..6ca6e7ac3 Binary files /dev/null and b/demos/esfm/Tangent_to_a_Segue.fur differ diff --git a/demos/esfm/TheOneWayFuture.fur b/demos/esfm/TheOneWayFuture.fur new file mode 100644 index 000000000..dc4150c3b Binary files /dev/null and b/demos/esfm/TheOneWayFuture.fur differ diff --git a/demos/esfm/Unconscious_Unravelling.fur b/demos/esfm/Unconscious_Unravelling.fur new file mode 100644 index 000000000..940d8bd61 Binary files /dev/null and b/demos/esfm/Unconscious_Unravelling.fur differ diff --git a/demos/esfm/Unknown_Location.fur b/demos/esfm/Unknown_Location.fur new file mode 100644 index 000000000..58d3b9a34 Binary files /dev/null and b/demos/esfm/Unknown_Location.fur differ diff --git a/demos/esfm/WalkInThePark.fur b/demos/esfm/WalkInThePark.fur new file mode 100644 index 000000000..a593e1988 Binary files /dev/null and b/demos/esfm/WalkInThePark.fur differ diff --git a/demos/esfm/act_of_fighter.fur b/demos/esfm/act_of_fighter.fur new file mode 100644 index 000000000..2d04e49f7 Binary files /dev/null and b/demos/esfm/act_of_fighter.fur differ diff --git a/demos/esfm/cielos_esfumados.fur b/demos/esfm/cielos_esfumados.fur new file mode 100644 index 000000000..7aed36c51 Binary files /dev/null and b/demos/esfm/cielos_esfumados.fur differ diff --git a/demos/esfm/esfm_ins.fur b/demos/esfm/esfm_ins.fur new file mode 100644 index 000000000..48af457e8 Binary files /dev/null and b/demos/esfm/esfm_ins.fur differ diff --git a/demos/esfm/experiment.fur b/demos/esfm/experiment.fur new file mode 100644 index 000000000..d0a7a6992 Binary files /dev/null and b/demos/esfm/experiment.fur differ diff --git a/demos/esfm/flashback.fur b/demos/esfm/flashback.fur new file mode 100644 index 000000000..597abd45e Binary files /dev/null and b/demos/esfm/flashback.fur differ diff --git a/demos/esfm/frosty_dusk.fur b/demos/esfm/frosty_dusk.fur new file mode 100644 index 000000000..2f2d0c39c Binary files /dev/null and b/demos/esfm/frosty_dusk.fur differ diff --git a/demos/esfm/haunted_castle.fur b/demos/esfm/haunted_castle.fur new file mode 100644 index 000000000..90ec11b93 Binary files /dev/null and b/demos/esfm/haunted_castle.fur differ diff --git a/demos/esfm/her_wishes.fur b/demos/esfm/her_wishes.fur new file mode 100644 index 000000000..872952f90 Binary files /dev/null and b/demos/esfm/her_wishes.fur differ diff --git a/demos/esfm/ledstorm.fur b/demos/esfm/ledstorm.fur new file mode 100644 index 000000000..9515d0ece Binary files /dev/null and b/demos/esfm/ledstorm.fur differ diff --git a/demos/esfm/loneguitarist.fur b/demos/esfm/loneguitarist.fur new file mode 100644 index 000000000..03293d86d Binary files /dev/null and b/demos/esfm/loneguitarist.fur differ diff --git a/demos/esfm/moon_closedspace.fur b/demos/esfm/moon_closedspace.fur new file mode 100644 index 000000000..107bb3b59 Binary files /dev/null and b/demos/esfm/moon_closedspace.fur differ diff --git a/demos/esfm/sticker1.fur b/demos/esfm/sticker1.fur new file mode 100644 index 000000000..f09f7c523 Binary files /dev/null and b/demos/esfm/sticker1.fur differ diff --git a/demos/esfm/synthy.fur b/demos/esfm/synthy.fur new file mode 100644 index 000000000..f9c2fb772 Binary files /dev/null and b/demos/esfm/synthy.fur differ diff --git a/demos/esfm/wack.fur b/demos/esfm/wack.fur new file mode 100644 index 000000000..e475919cd Binary files /dev/null and b/demos/esfm/wack.fur differ diff --git a/demos/esfm/x_evil_soul.fur b/demos/esfm/x_evil_soul.fur new file mode 100644 index 000000000..780ff36d1 Binary files /dev/null and b/demos/esfm/x_evil_soul.fur differ diff --git a/demos/genesis/Kobito_of_the_Shining_Needle.fur b/demos/genesis/Kobito_of_the_Shining_Needle.fur new file mode 100644 index 000000000..ef37f2c19 Binary files /dev/null and b/demos/genesis/Kobito_of_the_Shining_Needle.fur differ diff --git a/demos/misc/Playground_2xT6W28.fur b/demos/misc/Playground_2xT6W28.fur new file mode 100644 index 000000000..7709900e3 Binary files /dev/null and b/demos/misc/Playground_2xT6W28.fur differ diff --git a/demos/misc/RunningOnThePlayground_2xT6W28.fur b/demos/misc/RunningOnThePlayground_2xT6W28.fur deleted file mode 100644 index 769f12373..000000000 Binary files a/demos/misc/RunningOnThePlayground_2xT6W28.fur and /dev/null differ diff --git a/demos/misc/Someting_I_can_feel_MSM5232.fur b/demos/misc/Someting_I_can_feel_MSM5232.fur new file mode 100644 index 000000000..5e9c724ee Binary files /dev/null and b/demos/misc/Someting_I_can_feel_MSM5232.fur differ diff --git a/demos/misc/T_Six_T6W28.fur b/demos/misc/T_Six_T6W28.fur new file mode 100644 index 000000000..0d8530946 Binary files /dev/null and b/demos/misc/T_Six_T6W28.fur differ diff --git a/demos/msx/Morning_Alarm.fur b/demos/msx/Morning_Alarm.fur new file mode 100644 index 000000000..3f063df4b Binary files /dev/null and b/demos/msx/Morning_Alarm.fur differ diff --git a/demos/msx/WakingUpWhenMorningAlarmRings_OPLL.fur b/demos/msx/WakingUpWhenMorningAlarmRings_OPLL.fur deleted file mode 100644 index 36968ed0c..000000000 Binary files a/demos/msx/WakingUpWhenMorningAlarmRings_OPLL.fur and /dev/null differ diff --git a/demos/multichip/Chip_Duel_AY-3-8910+SN76489.fur b/demos/multichip/Chip_Duel_AY-3-8910+SN76489.fur new file mode 100644 index 000000000..e5aa24679 Binary files /dev/null and b/demos/multichip/Chip_Duel_AY-3-8910+SN76489.fur differ diff --git a/demos/nes/Fur_Nes.fur b/demos/nes/Fur_Nes.fur new file mode 100644 index 000000000..2535d591c Binary files /dev/null and b/demos/nes/Fur_Nes.fur differ diff --git a/demos/nes/Future_Re-ference.fur b/demos/nes/Future_Re-ference.fur new file mode 100644 index 000000000..7f40ac1ed Binary files /dev/null and b/demos/nes/Future_Re-ference.fur differ diff --git a/demos/specs2/back_into_adventure.fur b/demos/specs2/back_into_adventure.fur new file mode 100644 index 000000000..865a97e6b Binary files /dev/null and b/demos/specs2/back_into_adventure.fur differ diff --git a/demos/specs2/spa.fur b/demos/specs2/spa.fur new file mode 100644 index 000000000..34ecd2558 Binary files /dev/null and b/demos/specs2/spa.fur differ diff --git a/doc/2-interface/asset-list.md b/doc/2-interface/asset-list.md index 2b5ffaaa9..3e2b4bed9 100644 --- a/doc/2-interface/asset-list.md +++ b/doc/2-interface/asset-list.md @@ -60,7 +60,22 @@ right-clicking the Save button brings up a menu with the following options: right-clicking a sample in the list brings up a menu: - **make instrument**: creates a new instrument which is set to use the selected sample. +- **make me a drum kit**: allows you to instantly create a drum kit using all the samples in the list. see the next section for more information. - **duplicate**: makes a copy of the selected sample. - **replace...**: opens a file dialog to choose a replacement sample. - **save**: opens a file dialog to choose where to save the sample. - **delete**: removes the sample. + +### make me a drum kit + +I have added this option to make it easier for you to create a drum kit. +it puts all the samples into a new instrument with sample map. + +after selecting this option, a list of parameters appears: + +- **Drum kit mode**: select how to arrange the samples in the sample map. + - **Normal**: put all samples from the starting octave onwards. + - **12 samples per octave**: map the first 12 samples to all octaves, DefleMask-style. +- **Starting octave**: change the octave where the first sample will be at. + +following that is a list of viable instrument types. click on one of them to proceed with drum kit creation! diff --git a/doc/2-interface/song-info.md b/doc/2-interface/song-info.md index 1ead4f879..24a42f95e 100644 --- a/doc/2-interface/song-info.md +++ b/doc/2-interface/song-info.md @@ -30,7 +30,7 @@ items in _italic_ don't appear in basic mode and are only available in advanced - clicking the Tick Rate button switches to a more traditional **Base Tempo** BPM setting. **Speed**: the number of ticks per row. -- clicking the "Speed" button changes to more complex modes covered in the [grooves] page. +- clicking the "Speed" button changes to more complex modes covered in the [grooves](../8-advanced/grooves.md) page. _**Virtual Tempo**:_ Simulates any arbitrary tempo without altering the tick rate. it does this by adding or skipping ticks to approximate the tempo. the two numbers represent a ratio applied to the actual tick rate. example: - set tick rate to 150 BPM (60 Hz) and speed to 6. diff --git a/doc/3-pattern/effects.md b/doc/3-pattern/effects.md index 8182d4c93..e8f02c8cf 100644 --- a/doc/3-pattern/effects.md +++ b/doc/3-pattern/effects.md @@ -34,7 +34,7 @@ however, effects are continuous, which means you only need to type it once and t - `E2xy`: **Note slide down.** `x` is the speed, while `y` is how many semitones to slide down. - --- - `EAxx`: **Toggle legato.** while on, new notes instantly change the pitch of the currently playing sound instead of starting it over. -- `00xy`: **Arpeggio.** after using this effect the channel will rapidly switch between semitone values of `note`, `note + x` and `note + y`. +- `00xy`: **Arpeggio.** this effect produces a rapid cycle between the current note, the note plus `x` semitones and the note plus `y` semitones. - `E0xx`: **Set arpeggio speed.** this sets the number of ticks between arpeggio values. default is 1. - --- - `04xy`: **Vibrato.** changes pitch to be "wavy" with a sine LFO. `x` is the speed, while `y` is the depth. diff --git a/doc/4-instrument/README.md b/doc/4-instrument/README.md index 1ba003c13..058fc7f7e 100644 --- a/doc/4-instrument/README.md +++ b/doc/4-instrument/README.md @@ -9,10 +9,8 @@ alternatively, window > instrument editor displays it. ![top of instrument editor](instrument-editor-top.png) -**TODO: add descriptions to buttons in the image. it really needs them.** - -- **Instrument Selector**: displays a list of instruments in the song. -- **Open**: open an instrument file. +- **Select**: displays a list of instruments in the song. +- **Load**: open an instrument file. - **Save**: save current instrument to a file. - right-click to see additional options, such as saving in DefleMask preset format (.dmp). - **Name**: changes the instrument name. @@ -76,6 +74,7 @@ the following instrument types are available: - [TED](ted.md) - for use with Commodore Plus/4 and Commodore 16's TED chip. - [C140](c140.md) - for use with C140 sample chip. - [C219](c219.md) - for use with C219 sample chip. +- [PowerNoise](powernoise.md) - for use with PowerNoise chip. ## macros @@ -93,9 +92,6 @@ each macro has the following parameters: - **Step Length (ticks)**: determines the number of ticks between macro steps. default is 1. - **Delay**: delays the macro until this many ticks have elapsed. default is 0. - the button is highlighted if either of these parameters is set to non-default values. -- release mode: determines how macro release (`===` or `REL` in the pattern) is handled: - - **Active**: jumps to release position on release. - - **Passive**: does not jump to release position. this will result in delay if release position has not been reached yet. ## macro types @@ -115,8 +111,6 @@ this is the most basic macro type. when the instrument is played, every value in ![bitmask sequence macro editor](macro-seq-bitmask.png) -**TODO: once again, text in the image. this sucks.** - the Length field allows you to set the number of steps in the sequence. the sequence view allows you to edit the macro. @@ -171,6 +165,10 @@ the sequence can be edited in the text input field at the very bottom. the follo in bitmask-style macros, the values are added up in binary and converted to decimal. +the release mode parameter determines how macro release (`===` or `REL` in the pattern) is handled: +- **Active**: jumps to release position on release. +- **Passive**: does not jump to release position. this will result in delay if release position has not been reached yet. + ### ADSR ![ADSR macro editor](macro-ADSR.png) diff --git a/doc/4-instrument/fm-esfm.md b/doc/4-instrument/fm-esfm.md index 50fdec3d8..2de2ca9b4 100644 --- a/doc/4-instrument/fm-esfm.md +++ b/doc/4-instrument/fm-esfm.md @@ -7,13 +7,13 @@ the ESFM editor is divided into 6 tabs: - **Macros (OP2)**: for macros controlling FM parameters of operator 2. - **Macros (OP3)**: for macros controlling FM parameters of operator 3. - **Macros (OP4)**: for macros controlling FM parameters of operator 4. -- **Macros**: for other macros (volume/arp/pitch/pan/noise mode). +- **Macros**: for other macros (volume/arp/pitch/pan/operator 4 noise mode). ## FM -ESFM is four-operator, but it is different from the rest of FM chips. +ESFM is four-operator, meaning it takes four oscillators to produce a single sound. -the concept of an algorithm does not exist in ESFM. instead, modulation routing is arbitrary, with each operator having output level and modulation input parameters. +unlike most four-operator FM synthesizers, however, ESFM does not have an algorithm selection. instead, it uses a fixed operator arrangement, but allows you to independently control the output and modulation input levels of each operator. this allows it to reproduce a few common four-operator algorithms, as well as unique combinations where operators act as modulators and carriers at the same time. these apply to the instrument as a whole: - **OP4 Noise Mode**: determines the mode used to produce noise in operator 4. @@ -21,57 +21,114 @@ these apply to the instrument as a whole: - Snare: takes the snare noise generation mode from OPL. square + noise. - HiHat: ring modulates with operator 3 and adds noise. - Top: ring modulates with operator 3 and double pitch modulation input. - - this mode is not emulated correctly. subject to change! + - these are normally used for the drum channels in ESFM's OPL3 compatibility mode. + - however, in ESFM, operator 4 can be modulated by operator 3, whereas in OPL3 drum mode the rhythm channels cannot be modulated. + - __note__: usage of noise mode "Top" is discouraged for now as it is not properly emulated yet, and results may change when the emulation gets fixed in the future. +- **operator routing preview**: shows how operators are connected with each other and with the audio output (at the bottom). + - left-click pops up a small "operators changes with volume?" dialog where each operator can be toggled to scale with volume level. + - right-click switches to a preview display of the waveform generated on a new note: + - left-click restarts the preview. + - middle-click pauses and unpauses the preview. + - right-click returns to algorithm view. these apply to each operator: - the crossed-arrows button can be dragged to rearrange operators. - **Amplitude Modulation (AM)**: makes the operator affected by LFO tremolo. -- **Sustain flag (SUS)**: when enabled, value of Sustain Level is in effect. -- **AM Depth (AMD)**: when enabled, LFO tremolo is deeper. +- **AM Depth (DAM/AMD)**: when enabled, LFO tremolo is deeper (1dB off; 4.8dB on). +- **Sustain flag (SUS)**: when enabled, the envelope pauses ("sustains") once it reaches the Sustain Level and does not proceed to the release phase until note off. +- **Envelope Delay (DL)**: determines the delay time before the envelope is triggered. the bigger the value, the longer the delay (0 to 7). + - a change of one unit doubles or halves the delay time. + - a value of 0 results in no delay. - **Attack Rate (AR)**: determines the rising time for the sound. the bigger the value, the faster the attack (0 to 15). - **Decay Rate (DR)**: determines the diminishing time for the sound. the higher the value, the shorter the decay. it's the initial amplitude decay rate (0 to 15). - **Sustain Level (SL)**: determines the point at which the sound ceases to decay and changes to a sound having a constant level. the sustain level is expressed as a fraction of the maximum level (0 to 15). - **Release Rate (RR)**: determines the rate at which the sound disappears after note off. the higher the value, the shorter the release (0 to 15). - - **Total Level (TL)**: represents the envelope’s highest amplitude, with 0 being the largest and 63 (decimal) the smallest. a change of one unit is about 0.75 dB. -- **Output Level (OL)**: determines the volume at which the operator will be output. -- **Modulation Input (MI)**: determines how much to take from the previous operator for modulation. - - this controls feedback level in the case of operator 1. - **Key Scale Level (KSL)**: also known as "Level Scale". determines the degree to which the amplitude decreases according to the pitch. ![FM ADSR chart](FM-ADSRchart.png) - **Key Scale Rate (KSR)**: also known as "Rate Scale". determines the degree to which the envelope execution speed increases according to the pitch. - **Frequency Multiplier (MULT)**: sets the coarse pitch offset in relation to the note (0 to 15). 0 is -1 octave, 1 is 0 octaves, 2 is 1 octave, 3 is 1 octave 7 semitones, and so on. - - note that values 11, 13 and 14 behave as 10, 12 and 12 respectively. + - note that values 11, 13 and 14 behave as 10, 12 and 15 respectively. +- **Tune (CT)**: sets the semitone offset in relation to the note (-24 to 24). + - this is a software effect. +- **Fine Detune (DT)**: shifts the pitch in fine steps (-128 to 127). 0 is the base pitch, -128 is -1 semitone, 127 is nearly +1 semitone. + - this is a software effect. +- **Left (L)**: toggles output to the left channel from the operator to the audio output. +- **Right (R)**: toggles output to the right channel from the operator to the audio output. - **Waveform Select (WS)**: changes the waveform of the operator (0 to 7). - **Vibrato (VIB)**: makes the operator affected by LFO vibrato. -- **FM Depth (FMD)**: when enabled, vibrato is deeper. +- **Vibrato Depth (DVB/FMD)**: when enabled, vibrato is deeper. -- **Tune**: sets the coarse tune of the operator. -- **Detune**: sets the fine tune of the operator. +### routing controls -- **Left (L)**: output on the left channel. -- **Right (R)**: output on the right channel. +- **Output Level (OL)**: sets the output level from this operator to the audio output (0 to 7). + - 7 is the loudest level and 1 is the softest, while 0 disables audio output. + - a change of one unit is about 6 dB. + - this output scaling factor is applied after TL and envelope scaling have been performed. +- **Modulation Input Level (MI)**: sets the modulation level from the previous operator to this operator (0 to 7). + - 7 is the strongest level and 1 is the weakest, while 0 disables modulation. + - a change of one unit is about 6 dB. + - for operator 1 this controls the **feedback level**. + - this modulation scaling factor is applied after the previous operator's TL and envelope scaling have been performed, but is unaffected by OL above. + +### common algorithms + +this table contains a list of modulation input/output level values which resemble common algorithms in Yamaha FM chips. + +__note__: MI1 is not included as it is the feedback level. + +| algorithm | OL1 | MI2 | OL2 | MI3 | OL3 | MI4 | OL4 | +|--------------------------------|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| OPN algorithm **0** | 0 | 7 | 0 | 7 | 0 | 7 | 7 | +| OPN algorithm **4** | 0 | 7 | 7 | 0 | 0 | 7 | 7 | +| OPN algorithm **6** | 0 | 7 | 7 | 0 | 7 | 0 | 7 | +| OPN algorithm **7** | 7 | 0 | 7 | 0 | 7 | 0 | 7 | +| OPL3 algorithm **1** | 7 | 0 | 0 | 7 | 0 | 7 | 7 | +| OPL3 algorithm **3** | 7 | 0 | 0 | 7 | 7 | 0 | 7 | +| OPL3 algorithm **1** (variant) | 0 | 7 | 0 | 7 | 7 | 0 | 7 | ### fixed frequency mode each operator has a Fixed Frequency mode. once enabled, the operator runs at the specified frequency regardless of the note. +when fixed frequency mode is enabled, the Tune and Fine Detune sliders will be replaced by **Block (Blk)** and **FreqNum (F)**, which allow you to input a raw frequency value into the operator. +the actual frequency is determined by the formula: `FreqNum*(2^Block)`. + +in other words, FreqNum defines the base frequency, while Block determines the scaling factor in octaves. + ## macros these macros allow you to control several parameters of FM per tick. ## OP1-OP4 Macros -all parameters are listed above. +most parameters are listed above. + +### envelope delay macro tricks + +due to a quirk in how the envelope delay feature works, the **Envelope Delay** macro can control the operator's key-on status for a limited amount of time after a note is played. a value of 0 represents key-on, while a value of 7 represents key-off. + +note that the macro cannot exceed 659.1 ms in length - anything beyond that will be treated as a value of 0. + +### operator arpeggio and pitch macros + +among the available macros are **Op. Arpeggio** and **Op. Pitch**. these work like the **Arpeggio** and **Pitch** macros featured below, but are applied to the individual operator, overriding the **Arpeggio**/**Pitch** macros respectively. + +the **Tune** and **Fine Detune** FM parameters are still respected when using these macros. + +### fixed frequency macros + +when fixed frequency is enabled for an operator, the **Op. Arpeggio** and **Op. Pitch** macros will be replaced by the **Block** and **FreqNum** macros. these can be used to change the operator's fixed frequency over time. ## Macros - **Volume**: volume sequence. - **Arpeggio**: pitch sequence. -- **OP4 Noise Mode**: noise mode sequence. +- **OP4 Noise Mode**: operator 4 noise mode sequence. - **Panning**: enables output on left/right channels. + - note that each operator also has its own pan controls, which get masked by this global pan control. - **Pitch**: fine pitch. - **Relative**: when enabled, pitch changes are relative to the current pitch. - **Phase Reset**: restarts all operators and resets the waveform to its start. diff --git a/doc/4-instrument/fm-opl.md b/doc/4-instrument/fm-opl.md index bfd979651..5879abcf8 100644 --- a/doc/4-instrument/fm-opl.md +++ b/doc/4-instrument/fm-opl.md @@ -29,7 +29,7 @@ these apply to the instrument as a whole: these apply to each operator: - the crossed-arrows button can be dragged to rearrange operators. - **Amplitude Modulation (AM)**: makes the operator affected by LFO tremolo. -- **Sustain flag (SUS)**: when enabled, value of Sustain Level is in effect. +- **Sustain flag (SUS)**: when enabled, the envelope pauses ("sustains") once it reaches the Sustain Level and does not proceed to the release phase until note off. - **Attack Rate (AR)**: determines the rising time for the sound. the bigger the value, the faster the attack (0 to 15). - **Decay Rate (DR)**: determines the diminishing time for the sound. the higher the value, the shorter the decay. it's the initial amplitude decay rate (0 to 15). - **Sustain Level (SL)**: determines the point at which the sound ceases to decay and changes to a sound having a constant level. the sustain level is expressed as a fraction of the maximum level (0 to 15). diff --git a/doc/4-instrument/instrument-editor-top.png b/doc/4-instrument/instrument-editor-top.png index 5aa3f7289..3cfab1ec1 100644 Binary files a/doc/4-instrument/instrument-editor-top.png and b/doc/4-instrument/instrument-editor-top.png differ diff --git a/doc/4-instrument/lynx.md b/doc/4-instrument/lynx.md index e4bb3bc78..73cf93734 100644 --- a/doc/4-instrument/lynx.md +++ b/doc/4-instrument/lynx.md @@ -24,6 +24,20 @@ note that using samples on Lynx is CPU expensive! Atari Lynx generates sound using a 12-bit linear feedback shift register with configurable tap. nine separate bits can be enabled to be the source of feedback: 0, 1, 2, 3, 4, 5, 7, 10 and 11. to generate _any_ sound at least one bit _must_ be enabled. +### LFSR-based synthesis + +a linear-feedback shift register is one method used for random number generation. +it works by shifting a sequence of binary numbers (bits), taking the last bit into the output. then some of the bits are combined with others, doing a XOR (exclusive or) operation and then being pushed back. + +think of it as a conveyor carrying glass bottles. each bottle may be empty or carrying water. +the bottle at the end is taken. if there's water, then the output is 1. if it's empty, the output is 0. +depending on the LFSR configuration, many bottles at specific positions ("taps") are looked at. these are combined from left to right, two by two: +- if two bottles are identical, an empty bottle is pushed. +- if one bottle has water but the other is empty, a water bottle is pushed. +the process is repeated indefinitely. + +unlike PowerNoise, Lynx's taps are in fixed positions, but it has many of them. + ### square wave the LFSR is shifted at the rate define by sound pitch and generates square wave by setting channel output value to +volume or -volume, depending on the bit shifted in. diff --git a/doc/4-instrument/macro-seq-bitmask.png b/doc/4-instrument/macro-seq-bitmask.png index ce4c323f4..e9d846278 100644 Binary files a/doc/4-instrument/macro-seq-bitmask.png and b/doc/4-instrument/macro-seq-bitmask.png differ diff --git a/doc/4-instrument/macro-seq-clip.png b/doc/4-instrument/macro-seq-clip.png index 063e92faa..e3dcea591 100644 Binary files a/doc/4-instrument/macro-seq-clip.png and b/doc/4-instrument/macro-seq-clip.png differ diff --git a/doc/4-instrument/macro-seq.png b/doc/4-instrument/macro-seq.png index fa7e0d946..e9b86443c 100644 Binary files a/doc/4-instrument/macro-seq.png and b/doc/4-instrument/macro-seq.png differ diff --git a/doc/4-instrument/macroview.png b/doc/4-instrument/macroview.png index faadedb2e..af8dd58aa 100644 Binary files a/doc/4-instrument/macroview.png and b/doc/4-instrument/macroview.png differ diff --git a/doc/4-instrument/powernoise.md b/doc/4-instrument/powernoise.md new file mode 100644 index 000000000..6b4909e5b --- /dev/null +++ b/doc/4-instrument/powernoise.md @@ -0,0 +1,78 @@ +# PowerNoise instrument editor + +the PowerNoise instrument editor consists of two tabs. + +## LFSR-based synthesis + +PowerNoise employs LFSR-based synthesis for the noise channels, using linear-feedback shift registers for sound generation. + +a linear-feedback shift register is one method used for random number generation. +it works by shifting a sequence of binary numbers (bits), taking the last bit into the output. then one of the bits is either pushed back into the register, or combined with another, doing a XOR (exclusive or) operation and then being pushed back. + +think of it as a conveyor carrying glass bottles. each bottle may be empty or carrying water. +the bottle at the end is taken. if there's water, then the output is 1. if it's empty, the output is 0. +depending on the LFSR configuration: +- a bottle is pushed into the conveyor. it is either empty or filled with water depending on the bottle at a specific position in the conveyor (this is called a "tap"), or +- two bottles at specific positions ("taps") are looked at and combined as follows: + - if the bottles are identical, an empty bottle is pushed. + - if one bottle has water but the other is empty, a water bottle is pushed. +the process is repeated indefinitely. + +PowerNoise uses either one or two taps for the LFSR, configurable via the Control macro. + +the LFSR must be initialized before it can produce sound. the Load LFSR macro allows you to do so. + +by default the LFSR is configured to produce square waves, by having a single tap in position 1 and an alternating LFSR pattern. + +## Macros (noise) + +- **Volume**: volume sequence. +- **Arpeggio**: pitch sequence. +- **Panning (left)**: output level for left channel. +- **Panning (right)**: output level for right channel. +- **Pitch**: fine pitch. +- **Phase Reset**: trigger reloading the LFSR. +- **Control**: channel settings: + - **slope AM**: when enabled, this channel's output and the slope channel go through amplitude modulation. + - **tap B**: enables use of two taps for the LFSR. +- **Tap A Location**: sets the position of the first tap. +- **Tap B Location**: sets the position of the second tap. +- **Load LFSR**: allows you to load the LFSR with a specific pattern. + +## PowerNoise tab + +this tab allows you to change the base octave - important when you have set a longer LFSR pattern. + +## PowerNoise (slope) instrument editor + +this channel has its own instrument type, as it does not use LFSR-based synthesis but instead generates saw waves. + +it uses a custom algorithm which will be (roughly) described below. + +the slope channel uses two "portions" - each with length, offset, invert and clip parameters. +the channel alternates between these portions as it is cycled. + +on every cycle, the offset of the current portion is either added or subtracted into the accumulator (depending on the invert parameter), effectively behaving like a multiplier. +if the clip parameter is enabled, this will make sure the accumulator doesn't go past 0 or 127 (depending on the invert parameter, again). otherwise, the accumulator will be ANDed with 127. +once an amount of cycles set by the portion length parameter have elapsed, the channel switches into the other portion. + +the current value of the accumulator is output. + +## Macros (slope) + +- **Volume**: volume sequence. +- **Arpeggio**: pitch sequence. +- **Panning (left)**: output level for left channel. +- **Panning (right)**: output level for right channel. +- **Pitch**: fine pitch. +- **Control**: channel settings: + - **clip A**: sets clip parameter of first portion. + - **clip B**: sets clip parameter of second portion. + - **reset A**: resets the first portion. + - **reset B**: resets the second portion. + - **invert A**: sets invert parameter of first portion. + - **invert B**: sets invert parameter of second portion. +- **Portion A Length**: sets the duration of the first portion. +- **Portion B Length**: sets the duration of the second portion. +- **Portion A Offset**: sets the accumulator speed of the first portion. +- **Portion B Offset**: sets the accumulator speed of the second portion. diff --git a/doc/4-instrument/qsound.md b/doc/4-instrument/qsound.md index 3e2f54ed5..37711fd0a 100644 --- a/doc/4-instrument/qsound.md +++ b/doc/4-instrument/qsound.md @@ -1,6 +1,6 @@ # Capcom QSound instrument editor -the QSound instrument editor contains three tabs: Sample and Macros. +the QSound instrument editor contains two tabs: Sample and Macros. ## Sample diff --git a/doc/4-instrument/vrc6.md b/doc/4-instrument/vrc6.md index 64691282d..330e5efb7 100644 --- a/doc/4-instrument/vrc6.md +++ b/doc/4-instrument/vrc6.md @@ -19,7 +19,7 @@ note that using samples on VRC6 is CPU expensive! ## VRC6 (saw) instrument editor -this channel has its own instrument type, a one-of-a-kind thing in Furnace that was decided as a compromise during a debate. +this channel has its own instrument type, a thing in Furnace that was decided as a compromise during a debate. the only differences from this instrument type compared to the regular one are: - the lack of a Sample tab. diff --git a/doc/7-systems/README.md b/doc/7-systems/README.md index 4c1d09f98..8f5616faf 100644 --- a/doc/7-systems/README.md +++ b/doc/7-systems/README.md @@ -90,6 +90,7 @@ this is the full list of chips that Furnace supports. - [PET](pet.md) - [Pokémon Mini](pokemini.md) - [POKEY](pokey.md) +- [PowerNoise](powernoise.md) - [PV-1000](pv1000.md) - [QSound](qsound.md) - [RF5C68/RF5C164](ricoh.md) diff --git a/doc/7-systems/esfm.md b/doc/7-systems/esfm.md index bd53589a4..cdff9d860 100644 --- a/doc/7-systems/esfm.md +++ b/doc/7-systems/esfm.md @@ -1,8 +1,12 @@ # ESS ESFM -an enhanced version of Yamaha's OPL3, adding many features which weren't present on the original chip, such as 4-op on all channels, coarse/fine detune, per-op panning, envelope delay, noise generator and advanced modulation routing (no more algorithms!). +an FM synthesizer core included in a series of sound card chipsets made by ESS, which were mildly popular in the DOS days during the mid-late 90s. -the technology was present in many of ESS' sound cards (the ES1xxx series in particular). +at a cursory glance, it looks like just an [OPL3 clone](opl.md). but hidden under a veil of mystery is its exclusive "native mode", revealing an impressive superset of features, including 4-operator support on all 18 channels, semi-modular operator routing, per-operator pitch control, and even a few unique features. + +for a long time, not much was known about the inner workings of ESFM's native mode, since ESS did not release any documentation to developers on how to use it. this has thankfully changed in recent years thanks to reverse-engineering efforts from the community. + +thanks to ESS's decision to not release any documentation to developers and lock down usage of native mode behind a couple of General MIDI drivers shipping with rather lackluster patch sets, ESFM's native mode was unfortunately not very well used over its original lifespan. ## effects diff --git a/doc/7-systems/powernoise.md b/doc/7-systems/powernoise.md new file mode 100644 index 000000000..9b9b40fc0 --- /dev/null +++ b/doc/7-systems/powernoise.md @@ -0,0 +1,23 @@ +# PowerNoise + +a fantasy sound chip created by The Beesh-Spweesh! and jvsTSX for the Hexheld fantasy video game console. + +its design employs linear-feedback shift registers (LFSR) for sound generation. this technology is used in many random number generators to produce noise, but it is also capable of producing other tones. + +it has three noise channels and one "slope" channel capable of generating a variety of saw waves. + +it outputs stereo sound with 4-bit volume control per channel and 3-bit master volume control. + +refer to its instrument type's documentation for details on sound synthesis. + +## effect commands + +- `20xx`: **load LFSR value (low byte).** + - on the slope channel, this sets its accumulator (from `00` to `7F`). +- `21xx`: **load LFSR value (high byte).** +- `22xx`: **write to I/O port A.** +- `23xx`: **write to I/O port B.** + +## info + +this chip uses the [PowerNoise](../4-instrument/powernoise.md) instrument editor. diff --git a/extern/ESFMu/esfm.c b/extern/ESFMu/esfm.c index 277927096..6c743245b 100644 --- a/extern/ESFMu/esfm.c +++ b/extern/ESFMu/esfm.c @@ -63,40 +63,1042 @@ * TODO: Extract sine table from ESFM die scans... does ESFM even use a sine * table? Patent documents give a hint to a possible method of generating sine * waves using some sort of boolean logic wizardry (lol) + * Optimization: All 8 waveforms are calculated and unfolded from the actual + * data in OPL3's ROM. Negative entries are marked by 0x8000. */ -static const uint16_t logsinrom[256] = { - 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, - 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, - 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, - 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, - 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, - 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, - 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, - 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, - 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, - 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, - 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, - 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, - 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, - 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, - 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, - 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, - 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, - 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, - 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, - 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, - 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, - 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, - 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, - 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, - 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, - 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, - 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, - 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, - 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, - 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, - 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +static const uint16_t logsinrom[1024*8] = { + // wave 0 + 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471, + 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365, + 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd, + 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261, + 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f, + 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd, + 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195, + 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166, + 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c, + 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118, + 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8, + 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db, + 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1, + 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9, + 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094, + 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081, + 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070, + 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060, + 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052, + 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045, + 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039, + 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f, + 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026, + 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e, + 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017, + 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011, + 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c, + 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007, + 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004, + 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004, + 0x0004, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x0007, 0x0007, + 0x0007, 0x0008, 0x0008, 0x0009, 0x0009, 0x000a, 0x000a, 0x000b, + 0x000c, 0x000c, 0x000d, 0x000d, 0x000e, 0x000f, 0x000f, 0x0010, + 0x0011, 0x0011, 0x0012, 0x0013, 0x0014, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, + 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, + 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002d, 0x002e, + 0x002f, 0x0030, 0x0031, 0x0033, 0x0034, 0x0035, 0x0037, 0x0038, + 0x0039, 0x003b, 0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043, + 0x0045, 0x0046, 0x0048, 0x004a, 0x004b, 0x004d, 0x004e, 0x0050, + 0x0052, 0x0053, 0x0055, 0x0057, 0x0059, 0x005b, 0x005c, 0x005e, + 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, + 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x007a, 0x007d, 0x007f, + 0x0081, 0x0083, 0x0086, 0x0088, 0x008a, 0x008d, 0x008f, 0x0092, + 0x0094, 0x0097, 0x0099, 0x009c, 0x009f, 0x00a1, 0x00a4, 0x00a7, + 0x00a9, 0x00ac, 0x00af, 0x00b2, 0x00b5, 0x00b8, 0x00bb, 0x00be, + 0x00c1, 0x00c4, 0x00c7, 0x00ca, 0x00cd, 0x00d1, 0x00d4, 0x00d7, + 0x00db, 0x00de, 0x00e2, 0x00e5, 0x00e9, 0x00ec, 0x00f0, 0x00f4, + 0x00f8, 0x00fb, 0x00ff, 0x0103, 0x0107, 0x010b, 0x010f, 0x0114, + 0x0118, 0x011c, 0x0121, 0x0125, 0x0129, 0x012e, 0x0133, 0x0137, + 0x013c, 0x0141, 0x0146, 0x014b, 0x0150, 0x0155, 0x015b, 0x0160, + 0x0166, 0x016b, 0x0171, 0x0177, 0x017c, 0x0182, 0x0188, 0x018f, + 0x0195, 0x019b, 0x01a2, 0x01a9, 0x01b0, 0x01b7, 0x01be, 0x01c5, + 0x01cd, 0x01d4, 0x01dc, 0x01e4, 0x01ec, 0x01f5, 0x01fd, 0x0206, + 0x020f, 0x0218, 0x0222, 0x022c, 0x0236, 0x0240, 0x024b, 0x0256, + 0x0261, 0x026d, 0x0279, 0x0286, 0x0293, 0x02a0, 0x02af, 0x02bd, + 0x02cd, 0x02dc, 0x02ed, 0x02ff, 0x0311, 0x0324, 0x0339, 0x034e, + 0x0365, 0x037e, 0x0398, 0x03b5, 0x03d3, 0x03f5, 0x041a, 0x0443, + 0x0471, 0x04a6, 0x04e4, 0x052e, 0x058b, 0x0607, 0x06c3, 0x0859, + 0x8859, 0x86c3, 0x8607, 0x858b, 0x852e, 0x84e4, 0x84a6, 0x8471, + 0x8443, 0x841a, 0x83f5, 0x83d3, 0x83b5, 0x8398, 0x837e, 0x8365, + 0x834e, 0x8339, 0x8324, 0x8311, 0x82ff, 0x82ed, 0x82dc, 0x82cd, + 0x82bd, 0x82af, 0x82a0, 0x8293, 0x8286, 0x8279, 0x826d, 0x8261, + 0x8256, 0x824b, 0x8240, 0x8236, 0x822c, 0x8222, 0x8218, 0x820f, + 0x8206, 0x81fd, 0x81f5, 0x81ec, 0x81e4, 0x81dc, 0x81d4, 0x81cd, + 0x81c5, 0x81be, 0x81b7, 0x81b0, 0x81a9, 0x81a2, 0x819b, 0x8195, + 0x818f, 0x8188, 0x8182, 0x817c, 0x8177, 0x8171, 0x816b, 0x8166, + 0x8160, 0x815b, 0x8155, 0x8150, 0x814b, 0x8146, 0x8141, 0x813c, + 0x8137, 0x8133, 0x812e, 0x8129, 0x8125, 0x8121, 0x811c, 0x8118, + 0x8114, 0x810f, 0x810b, 0x8107, 0x8103, 0x80ff, 0x80fb, 0x80f8, + 0x80f4, 0x80f0, 0x80ec, 0x80e9, 0x80e5, 0x80e2, 0x80de, 0x80db, + 0x80d7, 0x80d4, 0x80d1, 0x80cd, 0x80ca, 0x80c7, 0x80c4, 0x80c1, + 0x80be, 0x80bb, 0x80b8, 0x80b5, 0x80b2, 0x80af, 0x80ac, 0x80a9, + 0x80a7, 0x80a4, 0x80a1, 0x809f, 0x809c, 0x8099, 0x8097, 0x8094, + 0x8092, 0x808f, 0x808d, 0x808a, 0x8088, 0x8086, 0x8083, 0x8081, + 0x807f, 0x807d, 0x807a, 0x8078, 0x8076, 0x8074, 0x8072, 0x8070, + 0x806e, 0x806c, 0x806a, 0x8068, 0x8066, 0x8064, 0x8062, 0x8060, + 0x805e, 0x805c, 0x805b, 0x8059, 0x8057, 0x8055, 0x8053, 0x8052, + 0x8050, 0x804e, 0x804d, 0x804b, 0x804a, 0x8048, 0x8046, 0x8045, + 0x8043, 0x8042, 0x8040, 0x803f, 0x803e, 0x803c, 0x803b, 0x8039, + 0x8038, 0x8037, 0x8035, 0x8034, 0x8033, 0x8031, 0x8030, 0x802f, + 0x802e, 0x802d, 0x802b, 0x802a, 0x8029, 0x8028, 0x8027, 0x8026, + 0x8025, 0x8024, 0x8023, 0x8022, 0x8021, 0x8020, 0x801f, 0x801e, + 0x801d, 0x801c, 0x801b, 0x801a, 0x8019, 0x8018, 0x8017, 0x8017, + 0x8016, 0x8015, 0x8014, 0x8014, 0x8013, 0x8012, 0x8011, 0x8011, + 0x8010, 0x800f, 0x800f, 0x800e, 0x800d, 0x800d, 0x800c, 0x800c, + 0x800b, 0x800a, 0x800a, 0x8009, 0x8009, 0x8008, 0x8008, 0x8007, + 0x8007, 0x8007, 0x8006, 0x8006, 0x8005, 0x8005, 0x8005, 0x8004, + 0x8004, 0x8004, 0x8003, 0x8003, 0x8003, 0x8002, 0x8002, 0x8002, + 0x8002, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8002, + 0x8002, 0x8002, 0x8002, 0x8003, 0x8003, 0x8003, 0x8004, 0x8004, + 0x8004, 0x8005, 0x8005, 0x8005, 0x8006, 0x8006, 0x8007, 0x8007, + 0x8007, 0x8008, 0x8008, 0x8009, 0x8009, 0x800a, 0x800a, 0x800b, + 0x800c, 0x800c, 0x800d, 0x800d, 0x800e, 0x800f, 0x800f, 0x8010, + 0x8011, 0x8011, 0x8012, 0x8013, 0x8014, 0x8014, 0x8015, 0x8016, + 0x8017, 0x8017, 0x8018, 0x8019, 0x801a, 0x801b, 0x801c, 0x801d, + 0x801e, 0x801f, 0x8020, 0x8021, 0x8022, 0x8023, 0x8024, 0x8025, + 0x8026, 0x8027, 0x8028, 0x8029, 0x802a, 0x802b, 0x802d, 0x802e, + 0x802f, 0x8030, 0x8031, 0x8033, 0x8034, 0x8035, 0x8037, 0x8038, + 0x8039, 0x803b, 0x803c, 0x803e, 0x803f, 0x8040, 0x8042, 0x8043, + 0x8045, 0x8046, 0x8048, 0x804a, 0x804b, 0x804d, 0x804e, 0x8050, + 0x8052, 0x8053, 0x8055, 0x8057, 0x8059, 0x805b, 0x805c, 0x805e, + 0x8060, 0x8062, 0x8064, 0x8066, 0x8068, 0x806a, 0x806c, 0x806e, + 0x8070, 0x8072, 0x8074, 0x8076, 0x8078, 0x807a, 0x807d, 0x807f, + 0x8081, 0x8083, 0x8086, 0x8088, 0x808a, 0x808d, 0x808f, 0x8092, + 0x8094, 0x8097, 0x8099, 0x809c, 0x809f, 0x80a1, 0x80a4, 0x80a7, + 0x80a9, 0x80ac, 0x80af, 0x80b2, 0x80b5, 0x80b8, 0x80bb, 0x80be, + 0x80c1, 0x80c4, 0x80c7, 0x80ca, 0x80cd, 0x80d1, 0x80d4, 0x80d7, + 0x80db, 0x80de, 0x80e2, 0x80e5, 0x80e9, 0x80ec, 0x80f0, 0x80f4, + 0x80f8, 0x80fb, 0x80ff, 0x8103, 0x8107, 0x810b, 0x810f, 0x8114, + 0x8118, 0x811c, 0x8121, 0x8125, 0x8129, 0x812e, 0x8133, 0x8137, + 0x813c, 0x8141, 0x8146, 0x814b, 0x8150, 0x8155, 0x815b, 0x8160, + 0x8166, 0x816b, 0x8171, 0x8177, 0x817c, 0x8182, 0x8188, 0x818f, + 0x8195, 0x819b, 0x81a2, 0x81a9, 0x81b0, 0x81b7, 0x81be, 0x81c5, + 0x81cd, 0x81d4, 0x81dc, 0x81e4, 0x81ec, 0x81f5, 0x81fd, 0x8206, + 0x820f, 0x8218, 0x8222, 0x822c, 0x8236, 0x8240, 0x824b, 0x8256, + 0x8261, 0x826d, 0x8279, 0x8286, 0x8293, 0x82a0, 0x82af, 0x82bd, + 0x82cd, 0x82dc, 0x82ed, 0x82ff, 0x8311, 0x8324, 0x8339, 0x834e, + 0x8365, 0x837e, 0x8398, 0x83b5, 0x83d3, 0x83f5, 0x841a, 0x8443, + 0x8471, 0x84a6, 0x84e4, 0x852e, 0x858b, 0x8607, 0x86c3, 0x8859, + // wave 1 + 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471, + 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365, + 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd, + 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261, + 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f, + 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd, + 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195, + 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166, + 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c, + 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118, + 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8, + 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db, + 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1, + 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9, + 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094, + 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081, + 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070, + 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060, + 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052, + 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045, + 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039, + 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f, + 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026, + 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e, + 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017, + 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011, + 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c, + 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007, + 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004, + 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004, + 0x0004, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x0007, 0x0007, + 0x0007, 0x0008, 0x0008, 0x0009, 0x0009, 0x000a, 0x000a, 0x000b, + 0x000c, 0x000c, 0x000d, 0x000d, 0x000e, 0x000f, 0x000f, 0x0010, + 0x0011, 0x0011, 0x0012, 0x0013, 0x0014, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, + 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, + 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002d, 0x002e, + 0x002f, 0x0030, 0x0031, 0x0033, 0x0034, 0x0035, 0x0037, 0x0038, + 0x0039, 0x003b, 0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043, + 0x0045, 0x0046, 0x0048, 0x004a, 0x004b, 0x004d, 0x004e, 0x0050, + 0x0052, 0x0053, 0x0055, 0x0057, 0x0059, 0x005b, 0x005c, 0x005e, + 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, + 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x007a, 0x007d, 0x007f, + 0x0081, 0x0083, 0x0086, 0x0088, 0x008a, 0x008d, 0x008f, 0x0092, + 0x0094, 0x0097, 0x0099, 0x009c, 0x009f, 0x00a1, 0x00a4, 0x00a7, + 0x00a9, 0x00ac, 0x00af, 0x00b2, 0x00b5, 0x00b8, 0x00bb, 0x00be, + 0x00c1, 0x00c4, 0x00c7, 0x00ca, 0x00cd, 0x00d1, 0x00d4, 0x00d7, + 0x00db, 0x00de, 0x00e2, 0x00e5, 0x00e9, 0x00ec, 0x00f0, 0x00f4, + 0x00f8, 0x00fb, 0x00ff, 0x0103, 0x0107, 0x010b, 0x010f, 0x0114, + 0x0118, 0x011c, 0x0121, 0x0125, 0x0129, 0x012e, 0x0133, 0x0137, + 0x013c, 0x0141, 0x0146, 0x014b, 0x0150, 0x0155, 0x015b, 0x0160, + 0x0166, 0x016b, 0x0171, 0x0177, 0x017c, 0x0182, 0x0188, 0x018f, + 0x0195, 0x019b, 0x01a2, 0x01a9, 0x01b0, 0x01b7, 0x01be, 0x01c5, + 0x01cd, 0x01d4, 0x01dc, 0x01e4, 0x01ec, 0x01f5, 0x01fd, 0x0206, + 0x020f, 0x0218, 0x0222, 0x022c, 0x0236, 0x0240, 0x024b, 0x0256, + 0x0261, 0x026d, 0x0279, 0x0286, 0x0293, 0x02a0, 0x02af, 0x02bd, + 0x02cd, 0x02dc, 0x02ed, 0x02ff, 0x0311, 0x0324, 0x0339, 0x034e, + 0x0365, 0x037e, 0x0398, 0x03b5, 0x03d3, 0x03f5, 0x041a, 0x0443, + 0x0471, 0x04a6, 0x04e4, 0x052e, 0x058b, 0x0607, 0x06c3, 0x0859, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + // wave 2 + 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471, + 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365, + 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd, + 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261, + 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f, + 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd, + 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195, + 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166, + 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c, + 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118, + 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8, + 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db, + 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1, + 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9, + 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094, + 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081, + 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070, + 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060, + 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052, + 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045, + 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039, + 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f, + 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026, + 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e, + 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017, + 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011, + 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c, + 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007, + 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004, + 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004, + 0x0004, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x0007, 0x0007, + 0x0007, 0x0008, 0x0008, 0x0009, 0x0009, 0x000a, 0x000a, 0x000b, + 0x000c, 0x000c, 0x000d, 0x000d, 0x000e, 0x000f, 0x000f, 0x0010, + 0x0011, 0x0011, 0x0012, 0x0013, 0x0014, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, + 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, + 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002d, 0x002e, + 0x002f, 0x0030, 0x0031, 0x0033, 0x0034, 0x0035, 0x0037, 0x0038, + 0x0039, 0x003b, 0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043, + 0x0045, 0x0046, 0x0048, 0x004a, 0x004b, 0x004d, 0x004e, 0x0050, + 0x0052, 0x0053, 0x0055, 0x0057, 0x0059, 0x005b, 0x005c, 0x005e, + 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, + 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x007a, 0x007d, 0x007f, + 0x0081, 0x0083, 0x0086, 0x0088, 0x008a, 0x008d, 0x008f, 0x0092, + 0x0094, 0x0097, 0x0099, 0x009c, 0x009f, 0x00a1, 0x00a4, 0x00a7, + 0x00a9, 0x00ac, 0x00af, 0x00b2, 0x00b5, 0x00b8, 0x00bb, 0x00be, + 0x00c1, 0x00c4, 0x00c7, 0x00ca, 0x00cd, 0x00d1, 0x00d4, 0x00d7, + 0x00db, 0x00de, 0x00e2, 0x00e5, 0x00e9, 0x00ec, 0x00f0, 0x00f4, + 0x00f8, 0x00fb, 0x00ff, 0x0103, 0x0107, 0x010b, 0x010f, 0x0114, + 0x0118, 0x011c, 0x0121, 0x0125, 0x0129, 0x012e, 0x0133, 0x0137, + 0x013c, 0x0141, 0x0146, 0x014b, 0x0150, 0x0155, 0x015b, 0x0160, + 0x0166, 0x016b, 0x0171, 0x0177, 0x017c, 0x0182, 0x0188, 0x018f, + 0x0195, 0x019b, 0x01a2, 0x01a9, 0x01b0, 0x01b7, 0x01be, 0x01c5, + 0x01cd, 0x01d4, 0x01dc, 0x01e4, 0x01ec, 0x01f5, 0x01fd, 0x0206, + 0x020f, 0x0218, 0x0222, 0x022c, 0x0236, 0x0240, 0x024b, 0x0256, + 0x0261, 0x026d, 0x0279, 0x0286, 0x0293, 0x02a0, 0x02af, 0x02bd, + 0x02cd, 0x02dc, 0x02ed, 0x02ff, 0x0311, 0x0324, 0x0339, 0x034e, + 0x0365, 0x037e, 0x0398, 0x03b5, 0x03d3, 0x03f5, 0x041a, 0x0443, + 0x0471, 0x04a6, 0x04e4, 0x052e, 0x058b, 0x0607, 0x06c3, 0x0859, + 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471, + 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365, + 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd, + 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261, + 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f, + 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd, + 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195, + 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166, + 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c, + 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118, + 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8, + 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db, + 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1, + 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9, + 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094, + 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081, + 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070, + 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060, + 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052, + 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045, + 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039, + 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f, + 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026, + 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e, + 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017, + 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011, + 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c, + 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007, + 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004, + 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004, + 0x0004, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x0007, 0x0007, + 0x0007, 0x0008, 0x0008, 0x0009, 0x0009, 0x000a, 0x000a, 0x000b, + 0x000c, 0x000c, 0x000d, 0x000d, 0x000e, 0x000f, 0x000f, 0x0010, + 0x0011, 0x0011, 0x0012, 0x0013, 0x0014, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, + 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, + 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002d, 0x002e, + 0x002f, 0x0030, 0x0031, 0x0033, 0x0034, 0x0035, 0x0037, 0x0038, + 0x0039, 0x003b, 0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043, + 0x0045, 0x0046, 0x0048, 0x004a, 0x004b, 0x004d, 0x004e, 0x0050, + 0x0052, 0x0053, 0x0055, 0x0057, 0x0059, 0x005b, 0x005c, 0x005e, + 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, + 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x007a, 0x007d, 0x007f, + 0x0081, 0x0083, 0x0086, 0x0088, 0x008a, 0x008d, 0x008f, 0x0092, + 0x0094, 0x0097, 0x0099, 0x009c, 0x009f, 0x00a1, 0x00a4, 0x00a7, + 0x00a9, 0x00ac, 0x00af, 0x00b2, 0x00b5, 0x00b8, 0x00bb, 0x00be, + 0x00c1, 0x00c4, 0x00c7, 0x00ca, 0x00cd, 0x00d1, 0x00d4, 0x00d7, + 0x00db, 0x00de, 0x00e2, 0x00e5, 0x00e9, 0x00ec, 0x00f0, 0x00f4, + 0x00f8, 0x00fb, 0x00ff, 0x0103, 0x0107, 0x010b, 0x010f, 0x0114, + 0x0118, 0x011c, 0x0121, 0x0125, 0x0129, 0x012e, 0x0133, 0x0137, + 0x013c, 0x0141, 0x0146, 0x014b, 0x0150, 0x0155, 0x015b, 0x0160, + 0x0166, 0x016b, 0x0171, 0x0177, 0x017c, 0x0182, 0x0188, 0x018f, + 0x0195, 0x019b, 0x01a2, 0x01a9, 0x01b0, 0x01b7, 0x01be, 0x01c5, + 0x01cd, 0x01d4, 0x01dc, 0x01e4, 0x01ec, 0x01f5, 0x01fd, 0x0206, + 0x020f, 0x0218, 0x0222, 0x022c, 0x0236, 0x0240, 0x024b, 0x0256, + 0x0261, 0x026d, 0x0279, 0x0286, 0x0293, 0x02a0, 0x02af, 0x02bd, + 0x02cd, 0x02dc, 0x02ed, 0x02ff, 0x0311, 0x0324, 0x0339, 0x034e, + 0x0365, 0x037e, 0x0398, 0x03b5, 0x03d3, 0x03f5, 0x041a, 0x0443, + 0x0471, 0x04a6, 0x04e4, 0x052e, 0x058b, 0x0607, 0x06c3, 0x0859, + // wave 3 + 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471, + 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365, + 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd, + 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261, + 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f, + 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd, + 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195, + 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166, + 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c, + 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118, + 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8, + 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db, + 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1, + 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9, + 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094, + 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081, + 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070, + 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060, + 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052, + 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045, + 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039, + 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f, + 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026, + 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e, + 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017, + 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011, + 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c, + 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007, + 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004, + 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471, + 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365, + 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd, + 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261, + 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f, + 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd, + 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195, + 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166, + 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c, + 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118, + 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8, + 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db, + 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1, + 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9, + 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094, + 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081, + 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070, + 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060, + 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052, + 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045, + 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039, + 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f, + 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026, + 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e, + 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017, + 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011, + 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c, + 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007, + 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004, + 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + // wave 4 + 0x0859, 0x0607, 0x052e, 0x04a6, 0x0443, 0x03f5, 0x03b5, 0x037e, + 0x034e, 0x0324, 0x02ff, 0x02dc, 0x02bd, 0x02a0, 0x0286, 0x026d, + 0x0256, 0x0240, 0x022c, 0x0218, 0x0206, 0x01f5, 0x01e4, 0x01d4, + 0x01c5, 0x01b7, 0x01a9, 0x019b, 0x018f, 0x0182, 0x0177, 0x016b, + 0x0160, 0x0155, 0x014b, 0x0141, 0x0137, 0x012e, 0x0125, 0x011c, + 0x0114, 0x010b, 0x0103, 0x00fb, 0x00f4, 0x00ec, 0x00e5, 0x00de, + 0x00d7, 0x00d1, 0x00ca, 0x00c4, 0x00be, 0x00b8, 0x00b2, 0x00ac, + 0x00a7, 0x00a1, 0x009c, 0x0097, 0x0092, 0x008d, 0x0088, 0x0083, + 0x007f, 0x007a, 0x0076, 0x0072, 0x006e, 0x006a, 0x0066, 0x0062, + 0x005e, 0x005b, 0x0057, 0x0053, 0x0050, 0x004d, 0x004a, 0x0046, + 0x0043, 0x0040, 0x003e, 0x003b, 0x0038, 0x0035, 0x0033, 0x0030, + 0x002e, 0x002b, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, + 0x001d, 0x001b, 0x0019, 0x0017, 0x0016, 0x0014, 0x0013, 0x0011, + 0x0010, 0x000f, 0x000d, 0x000c, 0x000b, 0x000a, 0x0009, 0x0008, + 0x0007, 0x0006, 0x0005, 0x0005, 0x0004, 0x0003, 0x0003, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0003, 0x0003, 0x0004, 0x0005, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000f, 0x0010, + 0x0011, 0x0013, 0x0014, 0x0016, 0x0017, 0x0019, 0x001b, 0x001d, + 0x001f, 0x0021, 0x0023, 0x0025, 0x0027, 0x0029, 0x002b, 0x002e, + 0x0030, 0x0033, 0x0035, 0x0038, 0x003b, 0x003e, 0x0040, 0x0043, + 0x0046, 0x004a, 0x004d, 0x0050, 0x0053, 0x0057, 0x005b, 0x005e, + 0x0062, 0x0066, 0x006a, 0x006e, 0x0072, 0x0076, 0x007a, 0x007f, + 0x0083, 0x0088, 0x008d, 0x0092, 0x0097, 0x009c, 0x00a1, 0x00a7, + 0x00ac, 0x00b2, 0x00b8, 0x00be, 0x00c4, 0x00ca, 0x00d1, 0x00d7, + 0x00de, 0x00e5, 0x00ec, 0x00f4, 0x00fb, 0x0103, 0x010b, 0x0114, + 0x011c, 0x0125, 0x012e, 0x0137, 0x0141, 0x014b, 0x0155, 0x0160, + 0x016b, 0x0177, 0x0182, 0x018f, 0x019b, 0x01a9, 0x01b7, 0x01c5, + 0x01d4, 0x01e4, 0x01f5, 0x0206, 0x0218, 0x022c, 0x0240, 0x0256, + 0x026d, 0x0286, 0x02a0, 0x02bd, 0x02dc, 0x02ff, 0x0324, 0x034e, + 0x037e, 0x03b5, 0x03f5, 0x0443, 0x04a6, 0x052e, 0x0607, 0x0859, + 0x8859, 0x8607, 0x852e, 0x84a6, 0x8443, 0x83f5, 0x83b5, 0x837e, + 0x834e, 0x8324, 0x82ff, 0x82dc, 0x82bd, 0x82a0, 0x8286, 0x826d, + 0x8256, 0x8240, 0x822c, 0x8218, 0x8206, 0x81f5, 0x81e4, 0x81d4, + 0x81c5, 0x81b7, 0x81a9, 0x819b, 0x818f, 0x8182, 0x8177, 0x816b, + 0x8160, 0x8155, 0x814b, 0x8141, 0x8137, 0x812e, 0x8125, 0x811c, + 0x8114, 0x810b, 0x8103, 0x80fb, 0x80f4, 0x80ec, 0x80e5, 0x80de, + 0x80d7, 0x80d1, 0x80ca, 0x80c4, 0x80be, 0x80b8, 0x80b2, 0x80ac, + 0x80a7, 0x80a1, 0x809c, 0x8097, 0x8092, 0x808d, 0x8088, 0x8083, + 0x807f, 0x807a, 0x8076, 0x8072, 0x806e, 0x806a, 0x8066, 0x8062, + 0x805e, 0x805b, 0x8057, 0x8053, 0x8050, 0x804d, 0x804a, 0x8046, + 0x8043, 0x8040, 0x803e, 0x803b, 0x8038, 0x8035, 0x8033, 0x8030, + 0x802e, 0x802b, 0x8029, 0x8027, 0x8025, 0x8023, 0x8021, 0x801f, + 0x801d, 0x801b, 0x8019, 0x8017, 0x8016, 0x8014, 0x8013, 0x8011, + 0x8010, 0x800f, 0x800d, 0x800c, 0x800b, 0x800a, 0x8009, 0x8008, + 0x8007, 0x8006, 0x8005, 0x8005, 0x8004, 0x8003, 0x8003, 0x8002, + 0x8002, 0x8001, 0x8001, 0x8001, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8001, 0x8001, 0x8002, + 0x8002, 0x8003, 0x8003, 0x8004, 0x8005, 0x8005, 0x8006, 0x8007, + 0x8008, 0x8009, 0x800a, 0x800b, 0x800c, 0x800d, 0x800f, 0x8010, + 0x8011, 0x8013, 0x8014, 0x8016, 0x8017, 0x8019, 0x801b, 0x801d, + 0x801f, 0x8021, 0x8023, 0x8025, 0x8027, 0x8029, 0x802b, 0x802e, + 0x8030, 0x8033, 0x8035, 0x8038, 0x803b, 0x803e, 0x8040, 0x8043, + 0x8046, 0x804a, 0x804d, 0x8050, 0x8053, 0x8057, 0x805b, 0x805e, + 0x8062, 0x8066, 0x806a, 0x806e, 0x8072, 0x8076, 0x807a, 0x807f, + 0x8083, 0x8088, 0x808d, 0x8092, 0x8097, 0x809c, 0x80a1, 0x80a7, + 0x80ac, 0x80b2, 0x80b8, 0x80be, 0x80c4, 0x80ca, 0x80d1, 0x80d7, + 0x80de, 0x80e5, 0x80ec, 0x80f4, 0x80fb, 0x8103, 0x810b, 0x8114, + 0x811c, 0x8125, 0x812e, 0x8137, 0x8141, 0x814b, 0x8155, 0x8160, + 0x816b, 0x8177, 0x8182, 0x818f, 0x819b, 0x81a9, 0x81b7, 0x81c5, + 0x81d4, 0x81e4, 0x81f5, 0x8206, 0x8218, 0x822c, 0x8240, 0x8256, + 0x826d, 0x8286, 0x82a0, 0x82bd, 0x82dc, 0x82ff, 0x8324, 0x834e, + 0x837e, 0x83b5, 0x83f5, 0x8443, 0x84a6, 0x852e, 0x8607, 0x8859, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + // wave 5 + 0x0859, 0x0607, 0x052e, 0x04a6, 0x0443, 0x03f5, 0x03b5, 0x037e, + 0x034e, 0x0324, 0x02ff, 0x02dc, 0x02bd, 0x02a0, 0x0286, 0x026d, + 0x0256, 0x0240, 0x022c, 0x0218, 0x0206, 0x01f5, 0x01e4, 0x01d4, + 0x01c5, 0x01b7, 0x01a9, 0x019b, 0x018f, 0x0182, 0x0177, 0x016b, + 0x0160, 0x0155, 0x014b, 0x0141, 0x0137, 0x012e, 0x0125, 0x011c, + 0x0114, 0x010b, 0x0103, 0x00fb, 0x00f4, 0x00ec, 0x00e5, 0x00de, + 0x00d7, 0x00d1, 0x00ca, 0x00c4, 0x00be, 0x00b8, 0x00b2, 0x00ac, + 0x00a7, 0x00a1, 0x009c, 0x0097, 0x0092, 0x008d, 0x0088, 0x0083, + 0x007f, 0x007a, 0x0076, 0x0072, 0x006e, 0x006a, 0x0066, 0x0062, + 0x005e, 0x005b, 0x0057, 0x0053, 0x0050, 0x004d, 0x004a, 0x0046, + 0x0043, 0x0040, 0x003e, 0x003b, 0x0038, 0x0035, 0x0033, 0x0030, + 0x002e, 0x002b, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, + 0x001d, 0x001b, 0x0019, 0x0017, 0x0016, 0x0014, 0x0013, 0x0011, + 0x0010, 0x000f, 0x000d, 0x000c, 0x000b, 0x000a, 0x0009, 0x0008, + 0x0007, 0x0006, 0x0005, 0x0005, 0x0004, 0x0003, 0x0003, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0003, 0x0003, 0x0004, 0x0005, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000f, 0x0010, + 0x0011, 0x0013, 0x0014, 0x0016, 0x0017, 0x0019, 0x001b, 0x001d, + 0x001f, 0x0021, 0x0023, 0x0025, 0x0027, 0x0029, 0x002b, 0x002e, + 0x0030, 0x0033, 0x0035, 0x0038, 0x003b, 0x003e, 0x0040, 0x0043, + 0x0046, 0x004a, 0x004d, 0x0050, 0x0053, 0x0057, 0x005b, 0x005e, + 0x0062, 0x0066, 0x006a, 0x006e, 0x0072, 0x0076, 0x007a, 0x007f, + 0x0083, 0x0088, 0x008d, 0x0092, 0x0097, 0x009c, 0x00a1, 0x00a7, + 0x00ac, 0x00b2, 0x00b8, 0x00be, 0x00c4, 0x00ca, 0x00d1, 0x00d7, + 0x00de, 0x00e5, 0x00ec, 0x00f4, 0x00fb, 0x0103, 0x010b, 0x0114, + 0x011c, 0x0125, 0x012e, 0x0137, 0x0141, 0x014b, 0x0155, 0x0160, + 0x016b, 0x0177, 0x0182, 0x018f, 0x019b, 0x01a9, 0x01b7, 0x01c5, + 0x01d4, 0x01e4, 0x01f5, 0x0206, 0x0218, 0x022c, 0x0240, 0x0256, + 0x026d, 0x0286, 0x02a0, 0x02bd, 0x02dc, 0x02ff, 0x0324, 0x034e, + 0x037e, 0x03b5, 0x03f5, 0x0443, 0x04a6, 0x052e, 0x0607, 0x0859, + 0x0859, 0x0607, 0x052e, 0x04a6, 0x0443, 0x03f5, 0x03b5, 0x037e, + 0x034e, 0x0324, 0x02ff, 0x02dc, 0x02bd, 0x02a0, 0x0286, 0x026d, + 0x0256, 0x0240, 0x022c, 0x0218, 0x0206, 0x01f5, 0x01e4, 0x01d4, + 0x01c5, 0x01b7, 0x01a9, 0x019b, 0x018f, 0x0182, 0x0177, 0x016b, + 0x0160, 0x0155, 0x014b, 0x0141, 0x0137, 0x012e, 0x0125, 0x011c, + 0x0114, 0x010b, 0x0103, 0x00fb, 0x00f4, 0x00ec, 0x00e5, 0x00de, + 0x00d7, 0x00d1, 0x00ca, 0x00c4, 0x00be, 0x00b8, 0x00b2, 0x00ac, + 0x00a7, 0x00a1, 0x009c, 0x0097, 0x0092, 0x008d, 0x0088, 0x0083, + 0x007f, 0x007a, 0x0076, 0x0072, 0x006e, 0x006a, 0x0066, 0x0062, + 0x005e, 0x005b, 0x0057, 0x0053, 0x0050, 0x004d, 0x004a, 0x0046, + 0x0043, 0x0040, 0x003e, 0x003b, 0x0038, 0x0035, 0x0033, 0x0030, + 0x002e, 0x002b, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, + 0x001d, 0x001b, 0x0019, 0x0017, 0x0016, 0x0014, 0x0013, 0x0011, + 0x0010, 0x000f, 0x000d, 0x000c, 0x000b, 0x000a, 0x0009, 0x0008, + 0x0007, 0x0006, 0x0005, 0x0005, 0x0004, 0x0003, 0x0003, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0003, 0x0003, 0x0004, 0x0005, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000f, 0x0010, + 0x0011, 0x0013, 0x0014, 0x0016, 0x0017, 0x0019, 0x001b, 0x001d, + 0x001f, 0x0021, 0x0023, 0x0025, 0x0027, 0x0029, 0x002b, 0x002e, + 0x0030, 0x0033, 0x0035, 0x0038, 0x003b, 0x003e, 0x0040, 0x0043, + 0x0046, 0x004a, 0x004d, 0x0050, 0x0053, 0x0057, 0x005b, 0x005e, + 0x0062, 0x0066, 0x006a, 0x006e, 0x0072, 0x0076, 0x007a, 0x007f, + 0x0083, 0x0088, 0x008d, 0x0092, 0x0097, 0x009c, 0x00a1, 0x00a7, + 0x00ac, 0x00b2, 0x00b8, 0x00be, 0x00c4, 0x00ca, 0x00d1, 0x00d7, + 0x00de, 0x00e5, 0x00ec, 0x00f4, 0x00fb, 0x0103, 0x010b, 0x0114, + 0x011c, 0x0125, 0x012e, 0x0137, 0x0141, 0x014b, 0x0155, 0x0160, + 0x016b, 0x0177, 0x0182, 0x018f, 0x019b, 0x01a9, 0x01b7, 0x01c5, + 0x01d4, 0x01e4, 0x01f5, 0x0206, 0x0218, 0x022c, 0x0240, 0x0256, + 0x026d, 0x0286, 0x02a0, 0x02bd, 0x02dc, 0x02ff, 0x0324, 0x034e, + 0x037e, 0x03b5, 0x03f5, 0x0443, 0x04a6, 0x052e, 0x0607, 0x0859, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + // wave 6 + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + // wave 7 + 0x0000, 0x0008, 0x0010, 0x0018, 0x0020, 0x0028, 0x0030, 0x0038, + 0x0040, 0x0048, 0x0050, 0x0058, 0x0060, 0x0068, 0x0070, 0x0078, + 0x0080, 0x0088, 0x0090, 0x0098, 0x00a0, 0x00a8, 0x00b0, 0x00b8, + 0x00c0, 0x00c8, 0x00d0, 0x00d8, 0x00e0, 0x00e8, 0x00f0, 0x00f8, + 0x0100, 0x0108, 0x0110, 0x0118, 0x0120, 0x0128, 0x0130, 0x0138, + 0x0140, 0x0148, 0x0150, 0x0158, 0x0160, 0x0168, 0x0170, 0x0178, + 0x0180, 0x0188, 0x0190, 0x0198, 0x01a0, 0x01a8, 0x01b0, 0x01b8, + 0x01c0, 0x01c8, 0x01d0, 0x01d8, 0x01e0, 0x01e8, 0x01f0, 0x01f8, + 0x0200, 0x0208, 0x0210, 0x0218, 0x0220, 0x0228, 0x0230, 0x0238, + 0x0240, 0x0248, 0x0250, 0x0258, 0x0260, 0x0268, 0x0270, 0x0278, + 0x0280, 0x0288, 0x0290, 0x0298, 0x02a0, 0x02a8, 0x02b0, 0x02b8, + 0x02c0, 0x02c8, 0x02d0, 0x02d8, 0x02e0, 0x02e8, 0x02f0, 0x02f8, + 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338, + 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378, + 0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8, + 0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8, + 0x0400, 0x0408, 0x0410, 0x0418, 0x0420, 0x0428, 0x0430, 0x0438, + 0x0440, 0x0448, 0x0450, 0x0458, 0x0460, 0x0468, 0x0470, 0x0478, + 0x0480, 0x0488, 0x0490, 0x0498, 0x04a0, 0x04a8, 0x04b0, 0x04b8, + 0x04c0, 0x04c8, 0x04d0, 0x04d8, 0x04e0, 0x04e8, 0x04f0, 0x04f8, + 0x0500, 0x0508, 0x0510, 0x0518, 0x0520, 0x0528, 0x0530, 0x0538, + 0x0540, 0x0548, 0x0550, 0x0558, 0x0560, 0x0568, 0x0570, 0x0578, + 0x0580, 0x0588, 0x0590, 0x0598, 0x05a0, 0x05a8, 0x05b0, 0x05b8, + 0x05c0, 0x05c8, 0x05d0, 0x05d8, 0x05e0, 0x05e8, 0x05f0, 0x05f8, + 0x0600, 0x0608, 0x0610, 0x0618, 0x0620, 0x0628, 0x0630, 0x0638, + 0x0640, 0x0648, 0x0650, 0x0658, 0x0660, 0x0668, 0x0670, 0x0678, + 0x0680, 0x0688, 0x0690, 0x0698, 0x06a0, 0x06a8, 0x06b0, 0x06b8, + 0x06c0, 0x06c8, 0x06d0, 0x06d8, 0x06e0, 0x06e8, 0x06f0, 0x06f8, + 0x0700, 0x0708, 0x0710, 0x0718, 0x0720, 0x0728, 0x0730, 0x0738, + 0x0740, 0x0748, 0x0750, 0x0758, 0x0760, 0x0768, 0x0770, 0x0778, + 0x0780, 0x0788, 0x0790, 0x0798, 0x07a0, 0x07a8, 0x07b0, 0x07b8, + 0x07c0, 0x07c8, 0x07d0, 0x07d8, 0x07e0, 0x07e8, 0x07f0, 0x07f8, + 0x0800, 0x0808, 0x0810, 0x0818, 0x0820, 0x0828, 0x0830, 0x0838, + 0x0840, 0x0848, 0x0850, 0x0858, 0x0860, 0x0868, 0x0870, 0x0878, + 0x0880, 0x0888, 0x0890, 0x0898, 0x08a0, 0x08a8, 0x08b0, 0x08b8, + 0x08c0, 0x08c8, 0x08d0, 0x08d8, 0x08e0, 0x08e8, 0x08f0, 0x08f8, + 0x0900, 0x0908, 0x0910, 0x0918, 0x0920, 0x0928, 0x0930, 0x0938, + 0x0940, 0x0948, 0x0950, 0x0958, 0x0960, 0x0968, 0x0970, 0x0978, + 0x0980, 0x0988, 0x0990, 0x0998, 0x09a0, 0x09a8, 0x09b0, 0x09b8, + 0x09c0, 0x09c8, 0x09d0, 0x09d8, 0x09e0, 0x09e8, 0x09f0, 0x09f8, + 0x0a00, 0x0a08, 0x0a10, 0x0a18, 0x0a20, 0x0a28, 0x0a30, 0x0a38, + 0x0a40, 0x0a48, 0x0a50, 0x0a58, 0x0a60, 0x0a68, 0x0a70, 0x0a78, + 0x0a80, 0x0a88, 0x0a90, 0x0a98, 0x0aa0, 0x0aa8, 0x0ab0, 0x0ab8, + 0x0ac0, 0x0ac8, 0x0ad0, 0x0ad8, 0x0ae0, 0x0ae8, 0x0af0, 0x0af8, + 0x0b00, 0x0b08, 0x0b10, 0x0b18, 0x0b20, 0x0b28, 0x0b30, 0x0b38, + 0x0b40, 0x0b48, 0x0b50, 0x0b58, 0x0b60, 0x0b68, 0x0b70, 0x0b78, + 0x0b80, 0x0b88, 0x0b90, 0x0b98, 0x0ba0, 0x0ba8, 0x0bb0, 0x0bb8, + 0x0bc0, 0x0bc8, 0x0bd0, 0x0bd8, 0x0be0, 0x0be8, 0x0bf0, 0x0bf8, + 0x0c00, 0x0c08, 0x0c10, 0x0c18, 0x0c20, 0x0c28, 0x0c30, 0x0c38, + 0x0c40, 0x0c48, 0x0c50, 0x0c58, 0x0c60, 0x0c68, 0x0c70, 0x0c78, + 0x0c80, 0x0c88, 0x0c90, 0x0c98, 0x0ca0, 0x0ca8, 0x0cb0, 0x0cb8, + 0x0cc0, 0x0cc8, 0x0cd0, 0x0cd8, 0x0ce0, 0x0ce8, 0x0cf0, 0x0cf8, + 0x0d00, 0x0d08, 0x0d10, 0x0d18, 0x0d20, 0x0d28, 0x0d30, 0x0d38, + 0x0d40, 0x0d48, 0x0d50, 0x0d58, 0x0d60, 0x0d68, 0x0d70, 0x0d78, + 0x0d80, 0x0d88, 0x0d90, 0x0d98, 0x0da0, 0x0da8, 0x0db0, 0x0db8, + 0x0dc0, 0x0dc8, 0x0dd0, 0x0dd8, 0x0de0, 0x0de8, 0x0df0, 0x0df8, + 0x0e00, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38, + 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78, + 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8, + 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8, + 0x0f00, 0x0f08, 0x0f10, 0x0f18, 0x0f20, 0x0f28, 0x0f30, 0x0f38, + 0x0f40, 0x0f48, 0x0f50, 0x0f58, 0x0f60, 0x0f68, 0x0f70, 0x0f78, + 0x0f80, 0x0f88, 0x0f90, 0x0f98, 0x0fa0, 0x0fa8, 0x0fb0, 0x0fb8, + 0x0fc0, 0x0fc8, 0x0fd0, 0x0fd8, 0x0fe0, 0x0fe8, 0x0ff0, 0x0ff8, + 0x8ff8, 0x8ff0, 0x8fe8, 0x8fe0, 0x8fd8, 0x8fd0, 0x8fc8, 0x8fc0, + 0x8fb8, 0x8fb0, 0x8fa8, 0x8fa0, 0x8f98, 0x8f90, 0x8f88, 0x8f80, + 0x8f78, 0x8f70, 0x8f68, 0x8f60, 0x8f58, 0x8f50, 0x8f48, 0x8f40, + 0x8f38, 0x8f30, 0x8f28, 0x8f20, 0x8f18, 0x8f10, 0x8f08, 0x8f00, + 0x8ef8, 0x8ef0, 0x8ee8, 0x8ee0, 0x8ed8, 0x8ed0, 0x8ec8, 0x8ec0, + 0x8eb8, 0x8eb0, 0x8ea8, 0x8ea0, 0x8e98, 0x8e90, 0x8e88, 0x8e80, + 0x8e78, 0x8e70, 0x8e68, 0x8e60, 0x8e58, 0x8e50, 0x8e48, 0x8e40, + 0x8e38, 0x8e30, 0x8e28, 0x8e20, 0x8e18, 0x8e10, 0x8e08, 0x8e00, + 0x8df8, 0x8df0, 0x8de8, 0x8de0, 0x8dd8, 0x8dd0, 0x8dc8, 0x8dc0, + 0x8db8, 0x8db0, 0x8da8, 0x8da0, 0x8d98, 0x8d90, 0x8d88, 0x8d80, + 0x8d78, 0x8d70, 0x8d68, 0x8d60, 0x8d58, 0x8d50, 0x8d48, 0x8d40, + 0x8d38, 0x8d30, 0x8d28, 0x8d20, 0x8d18, 0x8d10, 0x8d08, 0x8d00, + 0x8cf8, 0x8cf0, 0x8ce8, 0x8ce0, 0x8cd8, 0x8cd0, 0x8cc8, 0x8cc0, + 0x8cb8, 0x8cb0, 0x8ca8, 0x8ca0, 0x8c98, 0x8c90, 0x8c88, 0x8c80, + 0x8c78, 0x8c70, 0x8c68, 0x8c60, 0x8c58, 0x8c50, 0x8c48, 0x8c40, + 0x8c38, 0x8c30, 0x8c28, 0x8c20, 0x8c18, 0x8c10, 0x8c08, 0x8c00, + 0x8bf8, 0x8bf0, 0x8be8, 0x8be0, 0x8bd8, 0x8bd0, 0x8bc8, 0x8bc0, + 0x8bb8, 0x8bb0, 0x8ba8, 0x8ba0, 0x8b98, 0x8b90, 0x8b88, 0x8b80, + 0x8b78, 0x8b70, 0x8b68, 0x8b60, 0x8b58, 0x8b50, 0x8b48, 0x8b40, + 0x8b38, 0x8b30, 0x8b28, 0x8b20, 0x8b18, 0x8b10, 0x8b08, 0x8b00, + 0x8af8, 0x8af0, 0x8ae8, 0x8ae0, 0x8ad8, 0x8ad0, 0x8ac8, 0x8ac0, + 0x8ab8, 0x8ab0, 0x8aa8, 0x8aa0, 0x8a98, 0x8a90, 0x8a88, 0x8a80, + 0x8a78, 0x8a70, 0x8a68, 0x8a60, 0x8a58, 0x8a50, 0x8a48, 0x8a40, + 0x8a38, 0x8a30, 0x8a28, 0x8a20, 0x8a18, 0x8a10, 0x8a08, 0x8a00, + 0x89f8, 0x89f0, 0x89e8, 0x89e0, 0x89d8, 0x89d0, 0x89c8, 0x89c0, + 0x89b8, 0x89b0, 0x89a8, 0x89a0, 0x8998, 0x8990, 0x8988, 0x8980, + 0x8978, 0x8970, 0x8968, 0x8960, 0x8958, 0x8950, 0x8948, 0x8940, + 0x8938, 0x8930, 0x8928, 0x8920, 0x8918, 0x8910, 0x8908, 0x8900, + 0x88f8, 0x88f0, 0x88e8, 0x88e0, 0x88d8, 0x88d0, 0x88c8, 0x88c0, + 0x88b8, 0x88b0, 0x88a8, 0x88a0, 0x8898, 0x8890, 0x8888, 0x8880, + 0x8878, 0x8870, 0x8868, 0x8860, 0x8858, 0x8850, 0x8848, 0x8840, + 0x8838, 0x8830, 0x8828, 0x8820, 0x8818, 0x8810, 0x8808, 0x8800, + 0x87f8, 0x87f0, 0x87e8, 0x87e0, 0x87d8, 0x87d0, 0x87c8, 0x87c0, + 0x87b8, 0x87b0, 0x87a8, 0x87a0, 0x8798, 0x8790, 0x8788, 0x8780, + 0x8778, 0x8770, 0x8768, 0x8760, 0x8758, 0x8750, 0x8748, 0x8740, + 0x8738, 0x8730, 0x8728, 0x8720, 0x8718, 0x8710, 0x8708, 0x8700, + 0x86f8, 0x86f0, 0x86e8, 0x86e0, 0x86d8, 0x86d0, 0x86c8, 0x86c0, + 0x86b8, 0x86b0, 0x86a8, 0x86a0, 0x8698, 0x8690, 0x8688, 0x8680, + 0x8678, 0x8670, 0x8668, 0x8660, 0x8658, 0x8650, 0x8648, 0x8640, + 0x8638, 0x8630, 0x8628, 0x8620, 0x8618, 0x8610, 0x8608, 0x8600, + 0x85f8, 0x85f0, 0x85e8, 0x85e0, 0x85d8, 0x85d0, 0x85c8, 0x85c0, + 0x85b8, 0x85b0, 0x85a8, 0x85a0, 0x8598, 0x8590, 0x8588, 0x8580, + 0x8578, 0x8570, 0x8568, 0x8560, 0x8558, 0x8550, 0x8548, 0x8540, + 0x8538, 0x8530, 0x8528, 0x8520, 0x8518, 0x8510, 0x8508, 0x8500, + 0x84f8, 0x84f0, 0x84e8, 0x84e0, 0x84d8, 0x84d0, 0x84c8, 0x84c0, + 0x84b8, 0x84b0, 0x84a8, 0x84a0, 0x8498, 0x8490, 0x8488, 0x8480, + 0x8478, 0x8470, 0x8468, 0x8460, 0x8458, 0x8450, 0x8448, 0x8440, + 0x8438, 0x8430, 0x8428, 0x8420, 0x8418, 0x8410, 0x8408, 0x8400, + 0x83f8, 0x83f0, 0x83e8, 0x83e0, 0x83d8, 0x83d0, 0x83c8, 0x83c0, + 0x83b8, 0x83b0, 0x83a8, 0x83a0, 0x8398, 0x8390, 0x8388, 0x8380, + 0x8378, 0x8370, 0x8368, 0x8360, 0x8358, 0x8350, 0x8348, 0x8340, + 0x8338, 0x8330, 0x8328, 0x8320, 0x8318, 0x8310, 0x8308, 0x8300, + 0x82f8, 0x82f0, 0x82e8, 0x82e0, 0x82d8, 0x82d0, 0x82c8, 0x82c0, + 0x82b8, 0x82b0, 0x82a8, 0x82a0, 0x8298, 0x8290, 0x8288, 0x8280, + 0x8278, 0x8270, 0x8268, 0x8260, 0x8258, 0x8250, 0x8248, 0x8240, + 0x8238, 0x8230, 0x8228, 0x8220, 0x8218, 0x8210, 0x8208, 0x8200, + 0x81f8, 0x81f0, 0x81e8, 0x81e0, 0x81d8, 0x81d0, 0x81c8, 0x81c0, + 0x81b8, 0x81b0, 0x81a8, 0x81a0, 0x8198, 0x8190, 0x8188, 0x8180, + 0x8178, 0x8170, 0x8168, 0x8160, 0x8158, 0x8150, 0x8148, 0x8140, + 0x8138, 0x8130, 0x8128, 0x8120, 0x8118, 0x8110, 0x8108, 0x8100, + 0x80f8, 0x80f0, 0x80e8, 0x80e0, 0x80d8, 0x80d0, 0x80c8, 0x80c0, + 0x80b8, 0x80b0, 0x80a8, 0x80a0, 0x8098, 0x8090, 0x8088, 0x8080, + 0x8078, 0x8070, 0x8068, 0x8060, 0x8058, 0x8050, 0x8048, 0x8040, + 0x8038, 0x8030, 0x8028, 0x8020, 0x8018, 0x8010, 0x8008, 0x8000, }; /* @@ -104,40 +1106,42 @@ static const uint16_t logsinrom[256] = { * Nuked OPL3 source code. * TODO: Verify if ESFM uses an exponent table or if it possibly uses another * method to skirt around Yamaha's patents? + * Optimization: All entries are shifted left by one from the actual data in + * OPL3's ROM. */ static const uint16_t exprom[256] = { - 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4, - 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9, - 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f, - 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756, - 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e, - 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706, - 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0, - 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba, - 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695, - 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671, - 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e, - 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b, - 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609, - 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8, - 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8, - 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8, - 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589, - 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b, - 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d, - 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530, - 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514, - 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8, - 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc, - 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2, - 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8, - 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e, - 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475, - 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d, - 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445, - 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d, - 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416, - 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400 + 0xff4, 0xfea, 0xfde, 0xfd4, 0xfc8, 0xfbe, 0xfb4, 0xfa8, + 0xf9e, 0xf92, 0xf88, 0xf7e, 0xf72, 0xf68, 0xf5c, 0xf52, + 0xf48, 0xf3e, 0xf32, 0xf28, 0xf1e, 0xf14, 0xf08, 0xefe, + 0xef4, 0xeea, 0xee0, 0xed4, 0xeca, 0xec0, 0xeb6, 0xeac, + 0xea2, 0xe98, 0xe8e, 0xe84, 0xe7a, 0xe70, 0xe66, 0xe5c, + 0xe52, 0xe48, 0xe3e, 0xe34, 0xe2a, 0xe20, 0xe16, 0xe0c, + 0xe04, 0xdfa, 0xdf0, 0xde6, 0xddc, 0xdd2, 0xdca, 0xdc0, + 0xdb6, 0xdac, 0xda4, 0xd9a, 0xd90, 0xd88, 0xd7e, 0xd74, + 0xd6a, 0xd62, 0xd58, 0xd50, 0xd46, 0xd3c, 0xd34, 0xd2a, + 0xd22, 0xd18, 0xd10, 0xd06, 0xcfe, 0xcf4, 0xcec, 0xce2, + 0xcda, 0xcd0, 0xcc8, 0xcbe, 0xcb6, 0xcae, 0xca4, 0xc9c, + 0xc92, 0xc8a, 0xc82, 0xc78, 0xc70, 0xc68, 0xc60, 0xc56, + 0xc4e, 0xc46, 0xc3c, 0xc34, 0xc2c, 0xc24, 0xc1c, 0xc12, + 0xc0a, 0xc02, 0xbfa, 0xbf2, 0xbea, 0xbe0, 0xbd8, 0xbd0, + 0xbc8, 0xbc0, 0xbb8, 0xbb0, 0xba8, 0xba0, 0xb98, 0xb90, + 0xb88, 0xb80, 0xb78, 0xb70, 0xb68, 0xb60, 0xb58, 0xb50, + 0xb48, 0xb40, 0xb38, 0xb32, 0xb2a, 0xb22, 0xb1a, 0xb12, + 0xb0a, 0xb02, 0xafc, 0xaf4, 0xaec, 0xae4, 0xade, 0xad6, + 0xace, 0xac6, 0xac0, 0xab8, 0xab0, 0xaa8, 0xaa2, 0xa9a, + 0xa92, 0xa8c, 0xa84, 0xa7c, 0xa76, 0xa6e, 0xa68, 0xa60, + 0xa58, 0xa52, 0xa4a, 0xa44, 0xa3c, 0xa36, 0xa2e, 0xa28, + 0xa20, 0xa18, 0xa12, 0xa0c, 0xa04, 0x9fe, 0x9f6, 0x9f0, + 0x9e8, 0x9e2, 0x9da, 0x9d4, 0x9ce, 0x9c6, 0x9c0, 0x9b8, + 0x9b2, 0x9ac, 0x9a4, 0x99e, 0x998, 0x990, 0x98a, 0x984, + 0x97c, 0x976, 0x970, 0x96a, 0x962, 0x95c, 0x956, 0x950, + 0x948, 0x942, 0x93c, 0x936, 0x930, 0x928, 0x922, 0x91c, + 0x916, 0x910, 0x90a, 0x904, 0x8fc, 0x8f6, 0x8f0, 0x8ea, + 0x8e4, 0x8de, 0x8d8, 0x8d2, 0x8cc, 0x8c6, 0x8c0, 0x8ba, + 0x8b4, 0x8ae, 0x8a8, 0x8a2, 0x89c, 0x896, 0x890, 0x88a, + 0x884, 0x87e, 0x878, 0x872, 0x86c, 0x866, 0x860, 0x85a, + 0x854, 0x850, 0x84a, 0x844, 0x83e, 0x838, 0x832, 0x82c, + 0x828, 0x822, 0x81c, 0x816, 0x810, 0x80c, 0x806, 0x800 }; /* @@ -178,185 +1182,25 @@ static const uint8_t eg_incstep[4][4] = { { 1, 1, 1, 0 } }; -typedef int13(*envelope_sinfunc)(uint10 phase, uint10 envelope); - /* ------------------------------------------------------------------------- */ -static uint12 -ESFM_envelope_calc_exp(uint16 level) +static inline int13 +ESFM_envelope_wavegen(uint3 waveform, int16 phase, uint10 envelope) { + int13 out; + uint16 lookup = logsinrom[((uint16)waveform << 10) | (phase & 0x3ff)]; + uint16 level = (lookup & 0x1fff) + (envelope << 3); if (level > 0x1fff) { level = 0x1fff; } - return (exprom[level & 0xff] << 1) >> (level >> 8); + out = exprom[level & 0xff] >> (level >> 8); + if (lookup & 0x8000) + { + out = -out; + } + return out; } -/* ------------------------------------------------------------------------- */ -static int13 -ESFM_envelope_calc_sin0(uint10 phase, uint10 envelope) -{ - uint16 out = 0; - int13 neg = 1; - phase &= 0x3ff; - if (phase & 0x200) - { - neg = -1; - } - if (phase & 0x100) - { - out = logsinrom[(phase & 0xff) ^ 0xff]; - } - else - { - out = logsinrom[phase & 0xff]; - } - return ESFM_envelope_calc_exp(out + (envelope << 3)) * neg; -} - -/* ------------------------------------------------------------------------- */ -static int13 -ESFM_envelope_calc_sin1(uint10 phase, uint10 envelope) -{ - uint16 out = 0; - phase &= 0x3ff; - if (phase & 0x200) - { - out = 0x1000; - } - else if (phase & 0x100) - { - out = logsinrom[(phase & 0xff) ^ 0xff]; - } - else - { - out = logsinrom[phase & 0xff]; - } - return ESFM_envelope_calc_exp(out + (envelope << 3)); -} - -/* ------------------------------------------------------------------------- */ -static int13 -ESFM_envelope_calc_sin2(uint10 phase, uint10 envelope) -{ - uint16 out = 0; - phase &= 0x3ff; - if (phase & 0x100) - { - out = logsinrom[(phase & 0xff) ^ 0xff]; - } - else - { - out = logsinrom[phase & 0xff]; - } - return ESFM_envelope_calc_exp(out + (envelope << 3)); -} - -/* ------------------------------------------------------------------------- */ -static int13 -ESFM_envelope_calc_sin3(uint10 phase, uint10 envelope) -{ - uint16 out = 0; - phase &= 0x3ff; - if (phase & 0x100) - { - out = 0x1000; - } - else - { - out = logsinrom[phase & 0xff]; - } - return ESFM_envelope_calc_exp(out + (envelope << 3)); -} - -/* ------------------------------------------------------------------------- */ -static int13 -ESFM_envelope_calc_sin4(uint10 phase, uint10 envelope) -{ - uint16 out = 0; - int13 neg = 1; - phase &= 0x3ff; - if ((phase & 0x300) == 0x100) - { - neg = -1; - } - if (phase & 0x200) - { - out = 0x1000; - } - else if (phase & 0x80) - { - out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; - } - else - { - out = logsinrom[(phase << 1) & 0xff]; - } - return ESFM_envelope_calc_exp(out + (envelope << 3)) * neg; -} - -/* ------------------------------------------------------------------------- */ -static int13 -ESFM_envelope_calc_sin5(uint10 phase, uint10 envelope) -{ - uint16 out = 0; - phase &= 0x3ff; - if (phase & 0x200) - { - out = 0x1000; - } - else if (phase & 0x80) - { - out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; - } - else - { - out = logsinrom[(phase << 1) & 0xff]; - } - return ESFM_envelope_calc_exp(out + (envelope << 3)); -} - -/* ------------------------------------------------------------------------- */ -static int13 -ESFM_envelope_calc_sin6(uint10 phase, uint10 envelope) -{ - int13 neg = 1; - phase &= 0x3ff; - if (phase & 0x200) - { - neg = -1; - } - return ESFM_envelope_calc_exp(envelope << 3) * neg; -} - -/* ------------------------------------------------------------------------- */ -static int13 -ESFM_envelope_calc_sin7(uint10 phase, uint10 envelope) -{ - uint16 out = 0; - int13 neg = 1; - phase &= 0x3ff; - if (phase & 0x200) - { - neg = -1; - phase = (phase & 0x1ff) ^ 0x1ff; - } - out = phase << 3; - return ESFM_envelope_calc_exp(out + (envelope << 3)) * neg; -} - -/* ------------------------------------------------------------------------- */ -static const envelope_sinfunc envelope_sin[8] = -{ - ESFM_envelope_calc_sin0, - ESFM_envelope_calc_sin1, - ESFM_envelope_calc_sin2, - ESFM_envelope_calc_sin3, - ESFM_envelope_calc_sin4, - ESFM_envelope_calc_sin5, - ESFM_envelope_calc_sin6, - ESFM_envelope_calc_sin7 -}; - /* ------------------------------------------------------------------------- */ static void ESFM_envelope_calc(esfm_slot *slot) @@ -665,8 +1509,8 @@ ESFM_phase_generate(esfm_slot *slot) chip->rm_tc_bit5 = (prev_slot->in.phase_out >> 5) & 1; rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) - | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) - | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); + | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) + | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); switch(slot->rhy_noise) { @@ -779,8 +1623,8 @@ ESFM_phase_generate_emu(esfm_slot *slot) if (chip->emu_rhy_mode_flags & 0x20) { rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) - | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) - | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); + | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) + | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); if (slot->channel->channel_idx == 7) { if (slot->slot_idx == 0) { @@ -823,7 +1667,6 @@ static int16 ESFM_slot3_noise3_mod_input_calc(esfm_slot *slot) { esfm_channel *channel = slot->channel; - envelope_sinfunc wavegen = envelope_sin[channel->slots[2].waveform]; int16 phase; int13 output_buf = *channel->slots[1].in.mod_input; int i; @@ -838,7 +1681,7 @@ ESFM_slot3_noise3_mod_input_calc(esfm_slot *slot) { phase += output_buf >> (7 - channel->slots[i].mod_in_level); } - output_buf = wavegen((uint10)(phase & 0x3ff), channel->slots[i].in.eg_output); + output_buf = ESFM_envelope_wavegen(channel->slots[2].waveform, phase, channel->slots[i].in.eg_output); } return output_buf >> (8 - slot->mod_in_level); @@ -848,7 +1691,6 @@ ESFM_slot3_noise3_mod_input_calc(esfm_slot *slot) static void ESFM_slot_generate(esfm_slot *slot) { - envelope_sinfunc wavegen = envelope_sin[slot->waveform]; int16 phase = slot->in.phase_out; if (slot->mod_in_level) { @@ -861,7 +1703,7 @@ ESFM_slot_generate(esfm_slot *slot) phase += *slot->in.mod_input >> (7 - slot->mod_in_level); } } - slot->in.output = wavegen((uint10)(phase & 0x3ff), slot->in.eg_output); + slot->in.output = ESFM_envelope_wavegen(slot->waveform, phase, slot->in.eg_output); if (slot->output_level) { int13 output_value = slot->in.output >> (7 - slot->output_level); @@ -874,16 +1716,15 @@ ESFM_slot_generate(esfm_slot *slot) static void ESFM_slot_generate_emu(esfm_slot *slot) { - esfm_chip *chip = slot->chip; - envelope_sinfunc wavegen = envelope_sin[ - slot->waveform & (chip->emu_newmode != 0 ? 0x07 : 0x03)]; + const esfm_chip *chip = slot->chip; + uint3 waveform = slot->waveform & (chip->emu_newmode != 0 ? 0x07 : 0x03); bool rhythm_slot_double_volume = (slot->chip->emu_rhy_mode_flags & 0x20) != 0 && slot->channel->channel_idx >= 6 && slot->channel->channel_idx < 9; int16 phase = slot->in.phase_out; int14 output_value; phase += *slot->in.mod_input & slot->in.emu_mod_enable; - slot->in.output = wavegen((uint10)(phase & 0x3ff), slot->in.eg_output); + slot->in.output = ESFM_envelope_wavegen(waveform, phase, slot->in.eg_output); output_value = (slot->in.output & slot->in.emu_output_enable) << rhythm_slot_double_volume; if (chip->emu_newmode) { @@ -899,57 +1740,257 @@ ESFM_slot_generate_emu(esfm_slot *slot) /* ------------------------------------------------------------------------- */ static void -ESFM_slot_calc_feedback(esfm_slot *slot) +ESFM_process_feedback(esfm_chip *chip) { - esfm_chip *chip = slot->chip; - uint32 basefreq, phase_offset; - uint3 block; - uint10 f_num; - int13 in1 = 0, in2 = 0, wave_out; - int16 phase, phase_feedback; - uint19 regressed_phase; - int iter_counter; - envelope_sinfunc wavegen; + int channel_idx; - if (slot->mod_in_level) + for (channel_idx = 0; channel_idx < 18; channel_idx++) { - if (chip->native_mode) - { - wavegen = envelope_sin[slot->waveform]; - } - else - { - wavegen = envelope_sin[slot->waveform & (0x03 | (0x02 << (chip->emu_newmode != 0)))]; - } - f_num = slot->f_num; - block = slot->block; - basefreq = (f_num << block) >> 1; - phase_offset = (basefreq * mt[slot->mult]) >> 1; + esfm_slot *slot = &chip->channels[channel_idx].slots[0]; + uint32 basefreq, phase_offset; + uint3 block; + uint10 f_num; + int32_t wave_out, wave_last; + int32_t phase_feedback; + uint32_t iter_counter; + uint3 waveform; + uint3 mod_in_shift; + uint32_t phase, phase_acc; + uint10 eg_output; - for (iter_counter = 28; iter_counter >= 0; iter_counter--) + if (slot->mod_in_level && (chip->native_mode || (slot->in.mod_input == &slot->in.feedback_buf))) { - regressed_phase = (uint19)((uint32)slot->in.phase_acc - iter_counter * phase_offset) & ((1 << 19) - 1); - phase = (int16)(regressed_phase >> 9); - phase_feedback = (in1 + in2) >> 2; - phase += phase_feedback >> (7 - slot->mod_in_level); - wave_out = wavegen((uint10)(phase & 0x3ff), slot->in.eg_output); - in2 = in1; - in1 = wave_out; - } + if (chip->native_mode) + { + waveform = slot->waveform; + } + else + { + waveform = slot->waveform & (0x03 | (0x02 << (chip->emu_newmode != 0))); + } + f_num = slot->f_num; + block = slot->block; + basefreq = (f_num << block) >> 1; + phase_offset = (basefreq * mt[slot->mult]) >> 1; + mod_in_shift = 7 - slot->mod_in_level; + phase_acc = (uint32_t)(slot->in.phase_acc - phase_offset * 28); + eg_output = slot->in.eg_output; - // TODO: Figure out - is this how the ESFM chip does it, like the - // patent literally says? (it's really hacky...) - // slot->in.output = wave_out; + // ASM optimizaions! +#if defined(__GNUC__) && defined(__x86_64__) + asm ( + "movzbq %[wave], %%r8 \n\t" + "shll $11, %%r8d \n\t" + "leaq %[sinrom], %%rax \n\t" + "addq %%rax, %%r8 \n\t" + "leaq %[exprom], %%r9 \n\t" + "movzwl %[eg_out], %%r10d \n\t" + "shll $3, %%r10d \n\t" + "xorl %%r11d, %%r11d \n\t" + "movl %%r11d, %[out] \n\t" + "movl $29, %%edx \n" + "1: \n\t" + // phase_feedback = (wave_out + wave_last) >> 2; + "movl %[out], %[p_fb] \n\t" + "addl %%r11d, %[p_fb] \n\t" + "sarl $2, %[p_fb] \n\t" + // wave_last = wave_out + "movl %[out], %%r11d \n\t" + // phase = phase_feedback >> mod_in_shift; + "movl %[p_fb], %%eax \n\t" + "movb %[mod_in], %%cl \n\t" + "sarl %%cl, %%eax \n\t" + // phase += phase_acc >> 9; + "movl %[p_acc], %%ebx \n\t" + "sarl $9, %%ebx \n\t" + "addl %%ebx, %%eax \n\t" + // lookup = logsinrom[(waveform << 10) | (phase & 0x3ff)]; + "andq $0x3ff, %%rax \n\t" + "movzwl (%%r8, %%rax, 2), %%ebx \n\t" + "movl %%ebx, %%eax \n\t" + // level = (lookup & 0x1fff) + (envelope << 3); + "movl $0x1fff, %%ecx \n\t" + "andl %%ecx, %%eax \n\t" + "addl %%r10d, %%eax \n\t" + // if (level > 0x1fff) level = 0x1fff; + "cmpl %%ecx, %%eax \n\t" + "cmoval %%ecx, %%eax \n\t" + // wave_out = exprom[level & 0xff] >> (level >> 8); + "movb %%ah, %%cl \n\t" + "movzbl %%al, %%eax \n\t" + "movzwl (%%r9, %%rax, 2), %[out] \n\t" + "shrl %%cl, %[out] \n\t" + // if (lookup & 0x8000) wave_out = -wave_out; + // in other words, lookup is negative + "movl %[out], %%ecx \n\t" + "negl %%ecx \n\t" + "testw %%bx, %%bx \n\t" + "cmovsl %%ecx, %[out] \n\t" + // phase_acc += phase_offset + "addl %[p_off], %[p_acc] \n\t" + // loop + "decl %%edx \n\t" + "jne 1b \n\t" + : [p_fb] "=&r" (phase_feedback), + [p_acc] "+r" (phase_acc), + [out] "=&r" (wave_out) + : [p_off] "r" (phase_offset), + [mod_in] "r" (mod_in_shift), + [wave] "g" (waveform), + [eg_out] "g" (eg_output), + [sinrom] "m" (logsinrom), + [exprom] "m" (exprom) + : "cc", "ax", "bx", "cx", "dx", "r8", "r9", "r10", "r11" + ); +#elif defined(__GNUC__) && defined(__i386__) + asm ( + "movzbl %b[wave], %%eax \n\t" + "shll $11, %%eax \n\t" + "leal %[sinrom], %%edi \n\t" + "addl %%eax, %%edi \n\t" + "shlw $3, %[eg_out] \n\t" + "xorl %[out], %[out] \n\t" + "movl %[out], %[last] \n\t" + "movl $29, %[i] \n" + "1: \n\t" + // phase_feedback = (wave_out + wave_last) >> 2; + "movl %[out], %%eax \n\t" + "addl %[last], %%eax \n\t" + "sarl $2, %%eax \n\t" + "movl %%eax, %[p_fb] \n\t" + // wave_last = wave_out + "movl %[out], %[last] \n\t" + // phase = phase_feedback >> mod_in_shift; + "movb %[mod_in], %%cl \n\t" + "sarl %%cl, %%eax \n\t" + // phase += phase_acc >> 9; + "movl %[p_acc], %%ebx \n\t" + "shrl $9, %%ebx \n\t" + "addl %%ebx, %%eax \n\t" + // lookup = logsinrom[(waveform << 10) | (phase & 0x3ff)]; + "andl $0x3ff, %%eax \n\t" + "movzwl (%%edi, %%eax, 2), %%ebx \n\t" + "movl %%ebx, %%eax \n\t" + // level = (lookup & 0x1fff) + (envelope << 3); + "movl $0x1fff, %%ecx \n\t" + "andl %%ecx, %%eax \n\t" + "addw %[eg_out], %%ax \n\t" + // if (level > 0x1fff) level = 0x1fff; + "cmpl %%ecx, %%eax \n\t" + "cmoval %%ecx, %%eax \n\t" + // wave_out = exprom[level & 0xff] >> (level >> 8); + "movb %%ah, %%cl \n\t" + "movzbl %%al, %%eax \n\t" + "leal %[exprom], %[out] \n\t" + "movzwl (%[out], %%eax, 2), %[out] \n\t" + "shrl %%cl, %[out] \n\t" + // if (lookup & 0x8000) wave_out = -wave_out; + // in other words, lookup is negative + "movl %[out], %%ecx \n\t" + "negl %%ecx \n\t" + "testw %%bx, %%bx \n\t" + "cmovsl %%ecx, %[out] \n\t" + // phase_acc += phase_offset + "addl %[p_off], %[p_acc] \n\t" + // loop + "decl %[i] \n\t" + "jne 1b \n\t" + : [p_fb] "=&m" (phase_feedback), + [p_acc] "+r" (phase_acc), + [out] "=&r" (wave_out), + [last] "=&m" (wave_last), + [eg_out] "+m" (eg_output) + : [p_off] "m" (phase_offset), + [mod_in] "m" (mod_in_shift), + [wave] "m" (waveform), + [sinrom] "m" (logsinrom), + [exprom] "m" (exprom), + [i] "m" (iter_counter) + : "cc", "ax", "bx", "cx", "di" + ); +#elif defined(__GNUC__) && defined(__arm__) + asm ( + "ldr r3, =%[sinrom] \n\t" + "ldrb r0, %[wave] \n\t" + "add r3, r3, r0, lsl #11 \n\t" + "mov r4, #0 \n\t" + "mov %[out], #0 \n\t" + "ldr r5, =0x1fff<<1 \n\t" + "ldr r6, =0xff<<1 \n\t" + "mov r2, #29 \n" + "1: \n\t" + // phase_feedback = (wave_out + wave_last) >> 2; + "add %[p_fb], %[out], r4 \n\t" + "asr %[p_fb], %[p_fb], #2 \n\t" + // wave_last = wave_out + "mov r4, %[out] \n\t" + // phase = phase_feedback >> mod_in_shift; + "asr r0, %[p_fb], %[mod_in] \n\t" + // phase += phase_acc >> 9; + "add r0, r0, %[p_acc], asr #9 \n\t" + // lookup = logsinrom[(waveform << 10) | (phase & 0x3ff)]; + "lsl r0, r0, #22 \n\t" + "add r0, r3, r0, lsr #21 \n\t" + "ldrsh r1, [r0] \n\t" + // level = (lookup & 0x1fff) + (envelope << 3); + "and r0, r5, r1, lsl #1 \n\t" + "add r0, r0, %[eg_out], lsl #4 \n\t" + // if (level > 0x1fff) level = 0x1fff; + "cmp r0, r5 \n\t" + "movhi r0, r5 \n\t" + // wave_out = exprom[level & 0xff] >> (level >> 8); + "lsr %[out], r0, #9 \n\t" + "and r0, r0, r6 \n\t" + "ldrh r0, [%[exprom], r0] \n\t" + "lsr %[out], r0, %[out] \n\t" + // if (lookup & 0x8000) wave_out = -wave_out; + // in other words, lookup is negative + "tst r1, r1 \n\t" + "negmi %[out], %[out] \n\t" + // phase_acc += phase_offset + "add %[p_acc], %[p_acc], %[p_off]\n\t" + // loop + "subs r2, r2, #1 \n\t" + "bne 1b \n\t" + : [p_fb] "=&r" (phase_feedback), + [p_acc] "+r" (phase_acc), + [out] "=&r" (wave_out) + : [p_off] "r" (phase_offset), + [mod_in] "r" (mod_in_shift), + [wave] "m" (waveform), + [eg_out] "r" (eg_output), + [sinrom] "i" (logsinrom), + [exprom] "r" (exprom) + : "cc", "r0", "r1", "r2", "r3", "r4", "r5", "r6" + ); +#else + wave_out = 0; + wave_last = 0; + for (iter_counter = 0; iter_counter < 29; iter_counter++) + { + phase_feedback = (wave_out + wave_last) >> 2; + wave_last = wave_out; + phase = phase_feedback >> mod_in_shift; + phase += phase_acc >> 9; + wave_out = ESFM_envelope_wavegen(waveform, phase, eg_output); + phase_acc += phase_offset; + } +#endif - // This would be the more canonical way to do it, reusing the rest of - // the synthesis pipeline to finish the calculation: - if (chip->native_mode) - { - slot->in.feedback_buf = phase_feedback; - } - else - { - slot->in.feedback_buf = phase_feedback >> (7 - slot->mod_in_level); + // TODO: Figure out - is this how the ESFM chip does it, like the + // patent literally says? (it's really hacky...) + // slot->in.output = wave_out; + + // This would be the more canonical way to do it, reusing the rest of + // the synthesis pipeline to finish the calculation: + if (chip->native_mode) + { + slot->in.feedback_buf = phase_feedback; + } + else + { + slot->in.feedback_buf = phase_feedback >> (7 - slot->mod_in_level); + } } } } @@ -973,8 +2014,6 @@ ESFM_process_channel(esfm_channel *channel) // ESFM feedback calculation takes a large number of clock cycles, so // defer slot 0 generation to the end // TODO: verify this behavior on real hardware - ESFM_slot_calc_feedback(&channel->slots[0]); - ESFM_slot_generate(&channel->slots[0]); } /* ------------------------------------------------------------------------- */ @@ -996,11 +2035,6 @@ ESFM_process_channel_emu(esfm_channel *channel) // ESFM feedback calculation takes a large number of clock cycles, so // defer slot 0 generation to the end // TODO: verify this behavior on real hardware - if (channel->slots[0].in.mod_input == &channel->slots[0].in.feedback_buf) - { - ESFM_slot_calc_feedback(&channel->slots[0]); - } - ESFM_slot_generate_emu(&channel->slots[0]); } /* ------------------------------------------------------------------------- */ @@ -1183,7 +2217,19 @@ ESFM_generate(esfm_chip *chip, int16_t *buf) { ESFM_process_channel_emu(channel); } - + } + ESFM_process_feedback(chip); + for (channel_idx = 0; channel_idx < 18; channel_idx++) + { + esfm_channel *channel = &chip->channels[channel_idx]; + if (chip->native_mode) + { + ESFM_slot_generate(&channel->slots[0]); + } + else + { + ESFM_slot_generate_emu(&channel->slots[0]); + } chip->output_accm[0] += channel->output[0]; chip->output_accm[1] += channel->output[1]; } diff --git a/extern/ESFMu/esfm.h b/extern/ESFMu/esfm.h index f410558eb..f33567aeb 100644 --- a/extern/ESFMu/esfm.h +++ b/extern/ESFMu/esfm.h @@ -72,37 +72,6 @@ int16_t ESFM_get_channel_output_native(esfm_chip *chip, int channel_idx); // These are fake types just for syntax sugar. // Beware of their underlying types when reading/writing to them. -#ifndef __NO_ESFM_FAST_TYPES -#ifndef __ESFM_FAST_TYPES -#define __ESFM_FAST_TYPES -#endif -#endif - -#ifdef __ESFM_FAST_TYPES - -typedef uint_fast8_t flag; -typedef uint_fast8_t uint2; -typedef uint_fast8_t uint3; -typedef uint_fast8_t uint4; -typedef uint_fast8_t uint5; -typedef uint_fast8_t uint6; -typedef uint_fast8_t uint8; -typedef uint_fast16_t uint9; -typedef uint_fast16_t uint10; -typedef uint_fast16_t uint11; -typedef uint_fast16_t uint12; -typedef uint_fast16_t uint16; -typedef uint_fast32_t uint19; -typedef uint_fast32_t uint23; -typedef uint_fast32_t uint32; -typedef uint_fast64_t uint36; - -typedef int_fast16_t int13; -typedef int_fast16_t int14; -typedef int_fast16_t int16; -typedef int_fast32_t int32; - -#else typedef uint8_t flag; typedef uint8_t uint2; typedef uint8_t uint3; @@ -125,8 +94,6 @@ typedef int16_t int14; typedef int16_t int16; typedef int32_t int32; -#endif - enum eg_states { EG_ATTACK, diff --git a/extern/YMF262-LLE/fmopl3.c b/extern/YMF262-LLE/fmopl3.c index f534ba724..69a4224e0 100644 --- a/extern/YMF262-LLE/fmopl3.c +++ b/extern/YMF262-LLE/fmopl3.c @@ -1442,6 +1442,8 @@ void FMOPL3_Clock(fmopl3_t *chip) int sign; + chip->op_value_debug = chip->op_value; + int accm_a = chip->fsm_out[6] ? 0 : chip->accm_a[1]; accm_a += (chip->pan_l[1][1] & 1) != 0 ? value : 0; chip->accm_a[0] = accm_a; diff --git a/extern/YMF262-LLE/fmopl3.h b/extern/YMF262-LLE/fmopl3.h index 6fcf95f57..051515593 100644 --- a/extern/YMF262-LLE/fmopl3.h +++ b/extern/YMF262-LLE/fmopl3.h @@ -297,6 +297,7 @@ typedef struct int op_mod[2]; int op_value; + int op_value_debug; int accm_a[2]; int accm_b[2]; diff --git a/extern/backward/backward.hpp b/extern/backward/backward.hpp index 1e2251369..096e9d73d 100644 --- a/extern/backward/backward.hpp +++ b/extern/backward/backward.hpp @@ -4253,7 +4253,7 @@ public: } #ifdef _WIN32 - MessageBox(NULL,"Error","Furnace has crashed! please report this to the issue tracker immediately:\r\nhttps://github.com/tildearrow/furnace/issues/new\r\n\r\na file called furnace_crash.txt will be created in your user directory.\r\nthis will be important for locating the origin of the crash.",MB_OK|MB_ICONERROR); + MessageBox(NULL,"Error","Furnace has crashed! please report this to the issue tracker immediately:\r\nhttps://github.com/tildearrow/furnace/issues/new\r\n\r\na file called furnace_crash.txt will be created in your user directory.\r\nthis will be important for locating the origin of the crash.\r\n\r\nif Furnace keeps crashing and you believe it is caused by a configuration problem, you may start Furnace with the -safemode parameter.",MB_OK|MB_ICONERROR); std::string crashLocation; char* userProfile=getenv("USERPROFILE"); if (userProfile==NULL) { @@ -4492,7 +4492,7 @@ private: printer.print(st, std::cerr); #ifdef _WIN32 - MessageBox(NULL,"Furnace has crashed! please report this to the issue tracker immediately:\r\nhttps://github.com/tildearrow/furnace/issues/new\r\n\r\na file called furnace_crash.txt will be created in your user directory.\r\nthis will be important for locating the origin of the crash.","Error",MB_OK|MB_ICONERROR); + MessageBox(NULL,"Furnace has crashed! please report this to the issue tracker immediately:\r\nhttps://github.com/tildearrow/furnace/issues/new\r\n\r\na file called furnace_crash.txt will be created in your user directory.\r\nthis will be important for locating the origin of the crash.\r\n\r\nif Furnace keeps crashing and you believe it is caused by a configuration problem, you may start Furnace with the -safemode parameter.","Error",MB_OK|MB_ICONERROR); std::string crashLocation; char* userProfile=getenv("USERPROFILE"); if (userProfile==NULL) { diff --git a/extern/pwrnoise/LICENSE b/extern/pwrnoise/LICENSE new file mode 100644 index 000000000..3f7589ab1 --- /dev/null +++ b/extern/pwrnoise/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2024 scratchminer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/extern/pwrnoise/README.md b/extern/pwrnoise/README.md new file mode 100644 index 000000000..b6d9e86d1 --- /dev/null +++ b/extern/pwrnoise/README.md @@ -0,0 +1,4 @@ +# pwrnoise +An emulator for the PowerNoise fantasy sound chip, part of the [Hexheld](https://github.com/Hexheld/) fantasy console. + +Design by [jvsTSX](https://github.com/jvsTSX/), code by scratchminer. \ No newline at end of file diff --git a/extern/pwrnoise/pwrnoise.c b/extern/pwrnoise/pwrnoise.c new file mode 100644 index 000000000..40ef9a6ab --- /dev/null +++ b/extern/pwrnoise/pwrnoise.c @@ -0,0 +1,238 @@ +#include +#include +#include +#include + +#include "pwrnoise.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void pwrnoise_noise_write(noise_channel_t *chan, uint8_t reg, uint8_t val) { + switch (reg & 0x1f) { + case 1: + chan->enable = (val & 0x80) != 0; + chan->am = (val & 0x02) != 0; + chan->tapb_enable = (val & 0x01) != 0; + break; + case 2: + chan->period = (chan->period & 0xf00) | val; + break; + case 3: + chan->period = (chan->period & 0xff) | (((uint16_t)val << 8) & 0xf00); + chan->octave = val >> 4; + break; + case 4: + chan->lfsr = (chan->lfsr & 0xff00) | val; + break; + case 5: + chan->lfsr = (chan->lfsr & 0x00ff) | ((uint16_t)val << 8); + break; + case 6: + chan->tapa = val >> 4; + chan->tapb = val & 0x0f; + break; + case 7: + chan->vol = val; + break; + default: break; + } +} + +void pwrnoise_noise_step(noise_channel_t *chan, uint16_t cycles) { + if (!chan->enable) { + chan->out_latch = 0; + return; + } + + chan->octave_counter += cycles; + if (((cycles >= 2) && ((cycles >> (chan->octave + 1)) != 0)) || (!(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { + chan->period_counter += (cycles >> (chan->octave + 1)); + if ((cycles >> (chan->octave + 1)) == 0) ++chan->period_counter; + + while (chan->period_counter >= 4096) { + chan->prev = (uint8_t)(chan->lfsr >> 15); + uint16_t in = ((chan->lfsr >> chan->tapa) ^ (chan->tapb_enable ? (chan->lfsr >> chan->tapb) : 0)) & 0x0001; + chan->lfsr = (chan->lfsr << 1) | in; + chan->period_counter -= 4096 - chan->period; + } + } + + chan->out_latch = chan->prev ? chan->vol : 0; +} + +void pwrnoise_slope_write(slope_channel_t *chan, uint8_t reg, uint8_t val) { + switch (reg & 0x1f) { + case 0: + chan->accum = val & 0x7f; + break; + case 1: + chan->enable = (val & 0x80) != 0; + if ((val & 0x40) != 0) { + chan->a = 0; + chan->b = 0; + chan->portion = false; + } + chan->flags = val & 0x3f; + break; + case 2: + chan->period = (chan->period & 0xf00) | val; + break; + case 3: + chan->period = (chan->period & 0xff) | (((uint16_t)val << 8) & 0xf00); + chan->octave = val >> 4; + break; + case 4: + chan->alength = val; + break; + case 5: + chan->blength = val; + break; + case 6: + chan->aoffset = val >> 4; + chan->boffset = val & 0x0f; + break; + case 7: + chan->vol = val; + break; + default: break; + } +} + +void pwrnoise_slope_step(slope_channel_t *chan, uint16_t cycles, bool force_zero) { + if (!chan->enable) { + chan->out_latch = 0; + return; + } + + chan->octave_counter += cycles; + if (((cycles >= 2) && ((cycles >> (chan->octave + 1)) != 0)) || (!(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { + chan->period_counter += (cycles >> (chan->octave + 1)); + if ((cycles >> (chan->octave + 1)) == 0) ++chan->period_counter; + + while (chan->period_counter >= 4096) { + if (!chan->portion) { + if ((chan->flags & 0x02) != 0) chan->accum -= chan->aoffset; + else chan->accum += chan->aoffset; + + if ((chan->flags & 0x20) != 0 && chan->accum > 0x7f) chan->accum = (chan->flags & 0x02) ? 0x00 : 0x7f; + chan->accum &= 0x7f; + + if (++chan->a > chan->alength) { + if ((chan->flags & 0x04) != 0) chan->accum = (chan->flags & 0x01) ? 0x7f : 0x00; + chan->b = 0x00; + chan->portion = true; + } + } + else { + if ((chan->flags & 0x01) != 0) chan->accum -= chan->boffset; + else chan->accum += chan->boffset; + + if ((chan->flags & 0x10) != 0 && chan->accum > 0x7f) chan->accum = (chan->flags & 0x01) ? 0x00 : 0x7f; + chan->accum &= 0x7f; + + if (++chan->b > chan->blength) { + if ((chan->flags & 0x08) != 0) chan->accum = (chan->flags & 0x02) ? 0x7f : 0x00; + chan->a = 0x00; + chan->portion = false; + } + } + + chan->period_counter -= 4096 - chan->period; + + uint8_t left = chan->accum >> 3; + uint8_t right = chan->accum >> 3; + + switch (chan->vol >> 4) { + case 0: + case 1: + left >>= 1; + case 2: + case 3: + left >>= 1; + case 4: + case 5: + case 6: + case 7: + left >>= 1; + default: break; + } + switch (chan->vol & 0xf) { + case 0: + case 1: + right >>= 1; + case 2: + case 3: + right >>= 1; + case 4: + case 5: + case 6: + case 7: + right >>= 1; + default: break; + } + + left &= (chan->vol >> 4); + right &= (chan->vol & 0xf); + chan->prev = (left << 4) | right; + } + } + + chan->out_latch = force_zero ? 0 : chan->prev; +} + +void pwrnoise_reset(power_noise_t *pn) { + memset(pn, 0, sizeof(power_noise_t)); +} + +void pwrnoise_write(power_noise_t *pn, uint8_t reg, uint8_t val) { + reg &= 0x1f; + + if (reg == 0x00) { + pn->flags = val; + } + else if (reg == 0x08 && !(pn->flags & 0x20)) { + pn->gpioa = val; + } + else if (reg == 0x10 && !(pn->flags & 0x40)) { + pn->gpiob = val; + } + else if (reg < 0x08) { + pwrnoise_noise_write(&pn->n1, reg % 8, val); + } + else if (reg < 0x10) { + pwrnoise_noise_write(&pn->n2, reg % 8, val); + } + else if (reg < 0x18) { + pwrnoise_noise_write(&pn->n3, reg % 8, val); + } + else { + pwrnoise_slope_write(&pn->s, reg % 8, val); + } +} + +void pwrnoise_step(power_noise_t *pn, uint16_t cycles, int16_t *left, int16_t *right) { + int32_t final_left, final_right; + + if ((pn->flags & 0x80) != 0) { + pwrnoise_noise_step(&pn->n1, cycles); + pwrnoise_noise_step(&pn->n2, cycles); + pwrnoise_noise_step(&pn->n3, cycles); + pwrnoise_slope_step(&pn->s, cycles, (pn->n1.am && !(pn->n1.prev)) || (pn->n2.am && !(pn->n2.prev)) || (pn->n3.am && !(pn->n3.prev))); + + final_left = (pn->n1.out_latch >> 4) + (pn->n2.out_latch >> 4) + (pn->n3.out_latch >> 4) + (pn->s.out_latch >> 4); + final_right = (pn->n1.out_latch & 0xf) + (pn->n2.out_latch & 0xf) + (pn->n3.out_latch & 0xf) + (pn->s.out_latch & 0xf); + } + else { + final_left = 0; + final_right = 0; + } + + *left = (int16_t)((final_left * 65535 / 63 - 32768) * (pn->flags & 0x7) / 7); + *right = (int16_t)((final_right * 65535 / 63 - 32768) * (pn->flags & 0x7) / 7); +} + +#ifdef __cplusplus +} +#endif diff --git a/extern/pwrnoise/pwrnoise.h b/extern/pwrnoise/pwrnoise.h new file mode 100644 index 000000000..360176e40 --- /dev/null +++ b/extern/pwrnoise/pwrnoise.h @@ -0,0 +1,77 @@ +#ifndef PWRNOISE_H +#define PWRNOISE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct { + bool enable; + bool am; + + uint16_t period; + uint16_t period_counter; + + uint8_t octave; + uint16_t octave_counter; + + uint8_t tapa; + uint8_t tapb; + bool tapb_enable; + + uint16_t lfsr; + uint8_t vol; + + uint8_t out_latch; + uint8_t prev; +} noise_channel_t; + +typedef struct { + bool enable; + uint8_t flags; + + uint16_t period; + uint16_t period_counter; + + uint8_t octave; + uint16_t octave_counter; + + uint8_t alength; + uint8_t blength; + uint16_t a; + uint16_t b; + bool portion; + + uint8_t aoffset; + uint8_t boffset; + + uint8_t accum; + uint8_t vol; + + uint8_t out_latch; + uint8_t prev; +} slope_channel_t; + +typedef struct { + uint8_t flags; + uint8_t gpioa; + uint8_t gpiob; + + noise_channel_t n1; + noise_channel_t n2; + noise_channel_t n3; + slope_channel_t s; +} power_noise_t; + +void pwrnoise_reset(power_noise_t *pn); +void pwrnoise_step(power_noise_t *pn, uint16_t cycles, int16_t *left, int16_t *right); +void pwrnoise_write(power_noise_t *pn, uint8_t reg, uint8_t val); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/papers/clipboard-format.md b/papers/clipboard-format.md index 06d1c4f4a..7182b59e2 100644 --- a/papers/clipboard-format.md +++ b/papers/clipboard-format.md @@ -6,7 +6,7 @@ when copying pattern data from Furnace, it's stored in the clipboard as plain te org.tildearrow.furnace - Pattern Data (144) ``` -this top line of text is always the same except for the number in parentheses, which is the internal build number. for example, 0.6 is `181`. +this top line of text is always the same except for the number in parentheses, which is the internal build number. for example, 0.6.1 is `192`. the second line is a number between 0 and 18 (decimal) which indicates which column the clip starts from. - `0`: note. diff --git a/papers/format.md b/papers/format.md index 1a5a466a0..149c49869 100644 --- a/papers/format.md +++ b/papers/format.md @@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1). the format versions are: +- 192: Furnace 0.6.1 - 181: Furnace 0.6 - 180: Furnace 0.6pre18 - 179: Furnace 0.6pre17 @@ -234,7 +235,9 @@ size | description | - 0xce: Namco C140 - 24 channels | - 0xcf: Namco C219 - 16 channels | - 0xd0: Namco C352 - 32 channels (UNAVAILABLE) - | - 0xd1: ESFM - 18 channels (UNAVAILABLE) + | - 0xd1: ESFM - 18 channels + | - 0xd2: Ensoniq ES5503 (hard pan) - 32 channels (UNAVAILABLE) + | - 0xd4: PowerNoise - 4 channels | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - 0xfc: Pong - 1 channel @@ -503,628 +506,7 @@ see [newIns.md](newIns.md) for more information. # old instrument (<127) -notes: - -- the entire instrument is stored, regardless of instrument type. -- the macro range varies depending on the instrument type. -- "macro open" indicates whether the macro is collapsed or not in the instrument editor. - - as of format version 120, bit 1-2 indicates macro mode: - - 0: sequence (normal) - - 1: ADSR - - 2: LFO - - see sub-section for information on how to interpret parameters. -- FM operator order is: - - 1/3/2/4 (internal order) for OPN, OPM, OPZ and OPL 4-op - - 1/2/?/? (? = unused) for OPL 2-op and OPLL -- meaning of extended macros varies depending on instrument type. -- meaning of panning macros varies depending on instrument type: - - for hard-panned chips (e.g. FM and Game Boy): left panning is 2-bit panning macro (left/right) - - otherwise both left and right panning macros are used - -``` -size | description ------|------------------------------------ - 4 | "INST" block ID - 4 | size of this block - 2 | format version (see header) - 1 | instrument type - | - 0: SN76489/standard - | - 1: FM (OPN) - | - 2: Game Boy - | - 3: C64 - | - 4: Amiga/sample - | - 5: PC Engine - | - 6: AY-3-8910 - | - 7: AY8930 - | - 8: TIA - | - 9: SAA1099 - | - 10: VIC - | - 11: PET - | - 12: VRC6 - | - 13: OPLL - | - 14: OPL - | - 15: FDS - | - 16: Virtual Boy - | - 17: Namco 163 - | - 18: SCC - | - 19: OPZ - | - 20: POKEY - | - 21: PC Speaker - | - 22: WonderSwan - | - 23: Lynx - | - 24: VERA - | - 25: X1-010 - | - 26: VRC6 (saw) - | - 27: ES5506 - | - 28: MultiPCM - | - 29: SNES - | - 30: Sound Unit - | - 31: Namco WSG - | - 32: OPL (drums) - | - 33: FM (OPM) - | - 34: NES - | - 35: MSM6258 - | - 36: MSM6295 - | - 37: ADPCM-A - | - 38: ADPCM-B - | - 39: SegaPCM - | - 40: QSound - | - 41: YMZ280B - | - 42: RF5C68 - | - 43: MSM5232 - | - 44: T6W28 - 1 | reserved - STR | instrument name - --- | **FM instrument data** - 1 | alg (SUS on OPLL) - 1 | feedback - 1 | fms (DC on OPLL) - 1 | ams (DM on OPLL) - 1 | operator count - | - this is either 2 or 4, and is ignored on non-OPL systems. - | - always read 4 ops regardless of this value. - 1 | OPLL preset (>=60) or reserved - | - 0: custom - | - 1-15: pre-defined patches - | - 16: drums (compatibility only!) - 2 | reserved - --- | **FM operator data** × 4 - 1 | am - 1 | ar - 1 | dr - 1 | mult - 1 | rr - 1 | sl - 1 | tl - 1 | dt2 - 1 | rs - 1 | dt - 1 | d2r - 1 | ssgEnv - | - bit 4: on (EG-S on OPLL) - | - bit 0-3: envelope type - 1 | dam (for YMU759 compat; REV on OPZ) - 1 | dvb (for YMU759 compat; FINE on OPZ) - 1 | egt (for YMU759 compat; FixedFreq on OPZ) - 1 | ksl (EGShift on OPZ) - 1 | sus - 1 | vib - 1 | ws - 1 | ksr - 1 | operator enabled (>=114) or reserved - 1 | KVS mode (>=115) or reserved - | - 0: off - | - 1: on - | - 2: auto (depending on alg) - 10 | reserved - --- | **Game Boy instrument data** - 1 | volume - 1 | direction - 1 | length - 1 | sound length - --- | **C64 instrument data** - 1 | triangle - 1 | saw - 1 | pulse - 1 | noise - 1 | attack - 1 | decay - 1 | sustain - 1 | release - 2 | duty - 1 | ring mod - 1 | osc sync - 1 | to filter - 1 | init filter - 1 | vol macro is cutoff (<187) or reserved - | - from version 187 onwards, volume and cutoff macros are separate. - | - if this is on and the version is less than 187, move the volume macro into the ALG one. - 1 | resonance - 1 | low pass - 1 | band pass - 1 | high pass - 1 | channel 3 off - 2 | cutoff - 1 | duty macro is absolute - 1 | filter macro is absolute - --- | **Amiga instrument data** - 2 | initial sample - 1 | mode (>=82) or reserved - | - 0: sample - | - 1: wavetable - 1 | wavetable length (-1) (>=82) or reserved - 12 | reserved - --- | **standard instrument data** - 4 | volume macro length - 4 | arp macro length - 4 | duty macro length - 4 | wave macro length - 4 | pitch macro length (>=17) - 4 | extra 1 macro length (>=17) - 4 | extra 2 macro length (>=17) - 4 | extra 3 macro length (>=17) - 4 | volume macro loop - 4 | arp macro loop - 4 | duty macro loop - 4 | wave macro loop - 4 | pitch macro loop (>=17) - 4 | extra 1 macro loop (>=17) - 4 | extra 2 macro loop (>=17) - 4 | extra 3 macro loop (>=17) - 1 | arp macro mode (<112) or reserved - | - treat this value in a special way. - | - before version 112, this byte indicates whether the arp macro mode is fixed or not. - | - from that version onwards, the fixed mode is part of the macro values. - | - to convert a <112 macro mode to a modern one, do the following: - | - is the macro mode set to fixed? - | - if yes, then: - | - set bit 30 of all arp macro values (this is the fixed mode bit) - | - does the macro loop? - | - if yes, then do nothing else - | - if no, then add one to the macro length, and set the last macro value to 0 - | - if no, then do nothing - 1 | reserved (>=17) or volume macro height (>=15) or reserved - 1 | reserved (>=17) or duty macro height (>=15) or reserved - 1 | reserved (>=17) or wave macro height (>=15) or reserved - 4?? | volume macro - | - before version 87, if this is the C64 relative cutoff macro, its values were stored offset by 18. - 4?? | arp macro - | - before version 31, this macro's values were stored offset by 12. - | - from version 112 onward, bit 30 of a value indicates fixed mode. - 4?? | duty macro - | - before version 87, if this is the C64 relative duty macro, its values were stored offset by 12. - 4?? | wave macro - 4?? | pitch macro (>=17) - 4?? | extra 1 macro (>=17) - 4?? | extra 2 macro (>=17) - 4?? | extra 3 macro (>=17) - 4 | alg macro length (>=29) - 4 | fb macro length (>=29) - 4 | fms macro length (>=29) - 4 | ams macro length (>=29) - 4 | alg macro loop (>=29) - 4 | fb macro loop (>=29) - 4 | fms macro loop (>=29) - 4 | ams macro loop (>=29) - 1 | volume macro open (>=29) - 1 | arp macro open (>=29) - 1 | duty macro open (>=29) - 1 | wave macro open (>=29) - 1 | pitch macro open (>=29) - 1 | extra 1 macro open (>=29) - 1 | extra 2 macro open (>=29) - 1 | extra 3 macro open (>=29) - 1 | alg macro open (>=29) - 1 | fb macro open (>=29) - 1 | fms macro open (>=29) - 1 | ams macro open (>=29) - 4?? | alg macro (>=29) - 4?? | fb macro (>=29) - 4?? | fms macro (>=29) - 4?? | ams macro (>=29) - --- | **operator macro headers** × 4 (>=29) - 4 | AM macro length - 4 | AR macro length - 4 | DR macro length - 4 | MULT macro length - 4 | RR macro length - 4 | SL macro length - 4 | TL macro length - 4 | DT2 macro length - 4 | RS macro length - 4 | DT macro length - 4 | D2R macro length - 4 | SSG-EG macro length - 4 | AM macro loop - 4 | AR macro loop - 4 | DR macro loop - 4 | MULT macro loop - 4 | RR macro loop - 4 | SL macro loop - 4 | TL macro loop - 4 | DT2 macro loop - 4 | RS macro loop - 4 | DT macro loop - 4 | D2R macro loop - 4 | SSG-EG macro loop - 1 | AM macro open - 1 | AR macro open - 1 | DR macro open - 1 | MULT macro open - 1 | RR macro open - 1 | SL macro open - 1 | TL macro open - 1 | DT2 macro open - 1 | RS macro open - 1 | DT macro open - 1 | D2R macro open - 1 | SSG-EG macro open - --- | **operator macros** × 4 (>=29) - 1?? | AM macro - 1?? | AR macro - 1?? | DR macro - 1?? | MULT macro - 1?? | RR macro - 1?? | SL macro - 1?? | TL macro - 1?? | DT2 macro - 1?? | RS macro - 1?? | DT macro - 1?? | D2R macro - 1?? | SSG-EG macro - --- | **release points** (>=44) - 4 | volume macro release - 4 | arp macro release - 4 | duty macro release - 4 | wave macro release - 4 | pitch macro release - 4 | extra 1 macro release - 4 | extra 2 macro release - 4 | extra 3 macro release - 4 | alg macro release - 4 | fb macro release - 4 | fms macro release - 4 | ams macro release - --- | **operator release points** × 4 (>=44) - 4 | AM macro release - 4 | AR macro release - 4 | DR macro release - 4 | MULT macro release - 4 | RR macro release - 4 | SL macro release - 4 | TL macro release - 4 | DT2 macro release - 4 | RS macro release - 4 | DT macro release - 4 | D2R macro release - 4 | SSG-EG macro release - --- | **extended op macro headers** × 4 (>=61) - 4 | DAM macro length - 4 | DVB macro length - 4 | EGT macro length - 4 | KSL macro length - 4 | SUS macro length - 4 | VIB macro length - 4 | WS macro length - 4 | KSR macro length - 4 | DAM macro loop - 4 | DVB macro loop - 4 | EGT macro loop - 4 | KSL macro loop - 4 | SUS macro loop - 4 | VIB macro loop - 4 | WS macro loop - 4 | KSR macro loop - 4 | DAM macro release - 4 | DVB macro release - 4 | EGT macro release - 4 | KSL macro release - 4 | SUS macro release - 4 | VIB macro release - 4 | WS macro release - 4 | KSR macro release - 1 | DAM macro open - 1 | DVB macro open - 1 | EGT macro open - 1 | KSL macro open - 1 | SUS macro open - 1 | VIB macro open - 1 | WS macro open - 1 | KSR macro open - --- | **extended op macros** × 4 (>=61) - 1?? | DAM macro - 1?? | DVB macro - 1?? | EGT macro - 1?? | KSL macro - 1?? | SUS macro - 1?? | VIB macro - 1?? | WS macro - 1?? | KSR macro - --- | **OPL drums mode data** (>=63) - 1 | fixed frequency mode - 1 | reserved - 2 | kick frequency - 2 | snare/hi-hat frequency - 2 | tom/top frequency - --- | **Sample instrument extra data** (>=67) - 1 | use note map - | - only read the following two data structures if this is true! - 4?? | note frequency × 120 - | - 480 bytes - 2?? | note sample × 120 - | - 240 bytes - --- | **Namco 163 data** (>=73) - 4 | initial waveform - 1 | wave position - 1 | wave length - 1 | wave mode: - | - bit 1: update on change - | - bit 0: load on playback - 1 | reserved - --- | **even more macros** (>=76) - 4 | left panning macro length - 4 | right panning macro length - 4 | phase reset macro length - 4 | extra 4 macro length - 4 | extra 5 macro length - 4 | extra 6 macro length - 4 | extra 7 macro length - 4 | extra 8 macro length - 4 | left panning macro loop - 4 | right panning macro loop - 4 | phase reset macro loop - 4 | extra 4 macro loop - 4 | extra 5 macro loop - 4 | extra 6 macro loop - 4 | extra 7 macro loop - 4 | extra 8 macro loop - 4 | left panning macro release - 4 | right panning macro release - 4 | phase reset macro release - 4 | extra 4 macro release - 4 | extra 5 macro release - 4 | extra 6 macro release - 4 | extra 7 macro release - 4 | extra 8 macro release - 1 | left panning macro open - 1 | right panning macro open - 1 | phase reset macro open - 1 | extra 4 macro open - 1 | extra 5 macro open - 1 | extra 6 macro open - 1 | extra 7 macro open - 1 | extra 8 macro open - --- | **even more macro data** (>=76) - 4?? | left panning macro - 4?? | right panning macro - 4?? | phase reset macro - 4?? | extra 4 macro - 4?? | extra 5 macro - 4?? | extra 6 macro - 4?? | extra 7 macro - 4?? | extra 8 macro - --- | **FDS instrument data** (>=76) - 4 | modulation speed - 4 | modulation depth - 1 | init modulation table with first wave - 3 | reserved - 32 | modulation table - --- | **OPZ instrument extra data** (>=77) - 1 | fms2 - 1 | ams2 - --- | **wavetable synth data** (>=79) - 4 | first wave - 4 | second wave - 1 | rate divider - 1 | effect - | - bit 7: single or dual effect - 1 | enabled - 1 | global - 1 | speed (+1) - 1 | parameter 1 - 1 | parameter 2 - 1 | parameter 3 - 1 | parameter 4 - --- | **additional macro mode flags** (>=84) - 1 | volume macro mode - 1 | duty macro mode - 1 | wave macro mode - 1 | pitch macro mode - 1 | extra 1 macro mode - 1 | extra 2 macro mode - 1 | extra 3 macro mode - 1 | alg macro mode - 1 | fb macro mode - 1 | fms macro mode - 1 | ams macro mode - 1 | left panning macro mode - 1 | right panning macro mode - 1 | phase reset macro mode - 1 | extra 4 macro mode - 1 | extra 5 macro mode - 1 | extra 6 macro mode - 1 | extra 7 macro mode - 1 | extra 8 macro mode - --- | **extra C64 data** (>=89) - 1 | don't test/gate before new note - --- | **MultiPCM data** (>=93) - 1 | attack rate - 1 | decay 1 rate - 1 | decay level - 1 | decay 2 rate - 1 | release rate - 1 | rate correction - 1 | lfo rate - 1 | vib depth - 1 | am depth - 23 | reserved - --- | **Sound Unit data** (>=104) - 1 | use sample - 1 | switch roles of phase reset timer and frequency - --- | **Game Boy envelope sequence** (>=105) - 1 | length - ??? | hardware sequence data - | size is length*3: - | 1 byte: command - | - 0: set envelope - | - 1: set sweep - | - 2: wait - | - 3: wait for release - | - 4: loop - | - 5: loop until release - | 2 bytes: data - | - for set envelope: - | - 1 byte: parameter - | - bit 4-7: volume - | - bit 3: direction - | - bit 0-2: length - | - 1 byte: sound length - | - for set sweep: - | - 1 byte: parameter - | - bit 4-6: length - | - bit 3: direction - | - bit 0-2: shift - | - 1 byte: nothing - | - for wait: - | - 1 byte: length (in ticks) - | - 1 byte: nothing - | - for wait for release: - | - 2 bytes: nothing - | - for loop/loop until release: - | - 2 bytes: position - --- | **Game Boy extra flags** (>=106) - 1 | use software envelope - 1 | always init hard env on new note - --- | **ES5506 data** (>=107) - 1 | filter mode - | - 0: HPK2_HPK2 - | - 1: HPK2_LPK1 - | - 2: LPK2_LPK2 - | - 3: LPK2_LPK1 - 2 | K1 - 2 | K2 - 2 | envelope count - 1 | left volume ramp - 1 | right volume ramp - 1 | K1 ramp - 1 | K2 ramp - 1 | K1 slow - 1 | K2 slow - --- | **SNES data** (>=109) - 1 | use envelope - 1 | gain mode - 1 | gain - 1 | attack - 1 | decay - 1 | sustain - | - bit 3: sustain mode (>=118) - 1 | release - --- | **macro speeds/delays** (>=111) - 1 | volume macro speed - 1 | arp macro speed - 1 | duty macro speed - 1 | wave macro speed - 1 | pitch macro speed - 1 | extra 1 macro speed - 1 | extra 2 macro speed - 1 | extra 3 macro speed - 1 | alg macro speed - 1 | fb macro speed - 1 | fms macro speed - 1 | ams macro speed - 1 | left panning macro speed - 1 | right panning macro speed - 1 | phase reset macro speed - 1 | extra 4 macro speed - 1 | extra 5 macro speed - 1 | extra 6 macro speed - 1 | extra 7 macro speed - 1 | extra 8 macro speed - 1 | volume macro delay - 1 | arp macro delay - 1 | duty macro delay - 1 | wave macro delay - 1 | pitch macro delay - 1 | extra 1 macro delay - 1 | extra 2 macro delay - 1 | extra 3 macro delay - 1 | alg macro delay - 1 | fb macro delay - 1 | fms macro delay - 1 | ams macro delay - 1 | left panning macro delay - 1 | right panning macro delay - 1 | phase reset macro delay - 1 | extra 4 macro delay - 1 | extra 5 macro delay - 1 | extra 6 macro delay - 1 | extra 7 macro delay - 1 | extra 8 macro delay - --- | **operator macro speeds/delay** × 4 (>=111) - 1 | AM macro speed - 1 | AR macro speed - 1 | DR macro speed - 1 | MULT macro speed - 1 | RR macro speed - 1 | SL macro speed - 1 | TL macro speed - 1 | DT2 macro speed - 1 | RS macro speed - 1 | DT macro speed - 1 | D2R macro speed - 1 | SSG-EG macro speed - 1 | DAM macro speed - 1 | DVB macro speed - 1 | EGT macro speed - 1 | KSL macro speed - 1 | SUS macro speed - 1 | VIB macro speed - 1 | WS macro speed - 1 | KSR macro speed - 1 | AM macro delay - 1 | AR macro delay - 1 | DR macro delay - 1 | MULT macro delay - 1 | RR macro delay - 1 | SL macro delay - 1 | TL macro delay - 1 | DT2 macro delay - 1 | RS macro delay - 1 | DT macro delay - 1 | D2R macro delay - 1 | SSG-EG macro delay - 1 | DAM macro delay - 1 | DVB macro delay - 1 | EGT macro delay - 1 | KSL macro delay - 1 | SUS macro delay - 1 | VIB macro delay - 1 | WS macro delay - 1 | KSR macro delay -``` - -## interpreting macro mode values - -- sequence (normal): I think this is obvious... -- ADSR: - - `val[0]`: bottom - - `val[1]`: top - - `val[2]`: attack - - `val[3]`: hold time - - `val[4]`: decay - - `val[5]`: sustain level - - `val[6]`: sustain hold time - - `val[7]`: decay 2 - - `val[8]`: release -- LFO: - - `val[11]`: speed - - `val[12]`: waveform - - 0: triangle - - 1: saw - - 2: pulse - - `val[13]`: phase - - `val[14]`: loop - - `val[15]`: global (not sure how will I implement this) +instruments in older versions of Furnace used a different format. see [oldIns.md](oldIns.md) for more information. ## C64 compatibility note (>=187) @@ -1332,26 +714,6 @@ size | description STR | pattern name (>=51) ``` -# the Furnace instrument format (.fui) - -the instrument format is pretty similar to the file format, but it also stores wavetables and samples used by the instrument. - -``` -size | description ------|------------------------------------ - 16 | "-Furnace instr.-" format magic - 2 | format version - 2 | reserved - 4 | pointer to instrument data - 2 | wavetable count - 2 | sample count - 4 | reserved - 4?? | pointers to wavetables - 4?? | pointers to samples -``` - -instrument data follows. - # the Furnace wavetable format (.fuw) similar to the instrument format... diff --git a/papers/newIns.md b/papers/newIns.md index 8111ac4a4..d301d27c2 100644 --- a/papers/newIns.md +++ b/papers/newIns.md @@ -121,6 +121,9 @@ the following instrument types are available: - 52: TED - 53: C140 - 54: C219 +- 55: ESFM +- 56: PowerNoise (noise) +- 57: PowerNoise (slope) the following feature codes are recognized: @@ -146,6 +149,8 @@ the following feature codes are recognized: - `ES`: ES5506 ins data - `X1`: X1-010 ins data - `NE`: NES DPCM sample map data +- `EF`: ESFM ins data +- `PN`: PowerNoise ins data - `EN`: end of features - if you find this feature code, stop reading the instrument. - it will usually appear only when there sample/wave lists. @@ -653,3 +658,11 @@ size | description if some fields are missing, that's because they are defined in the SM feature. NES instruments with DPCM sample maps have both SM and NE features. + +# PowerNoise data (PN) + +``` +size | description +-----|------------------------------------ + 1 | octave +``` diff --git a/papers/oldIns.md b/papers/oldIns.md new file mode 100644 index 000000000..9d9ef5e58 --- /dev/null +++ b/papers/oldIns.md @@ -0,0 +1,651 @@ +# old instrument format (<127) + +this format is used in older versions of Furnace. + +# header + +.fui files use the following header: + +``` +size | description +-----|------------------------------------ + 16 | "-Furnace instr.-" format magic + 2 | format version + 2 | reserved + 4 | pointer to instrument data + 2 | wavetable count + 2 | sample count + 4 | reserved + 4?? | pointers to wavetables + 4?? | pointers to samples +``` + +instrument data follows. + +this header is not present on instruments inside a .fur file. + +# data + +notes: + +- the entire instrument is stored, regardless of instrument type. +- the macro range varies depending on the instrument type. +- "macro open" indicates whether the macro is collapsed or not in the instrument editor. + - as of format version 120, bit 1-2 indicates macro mode: + - 0: sequence (normal) + - 1: ADSR + - 2: LFO + - see sub-section for information on how to interpret parameters. +- FM operator order is: + - 1/3/2/4 (internal order) for OPN, OPM, OPZ and OPL 4-op + - 1/2/?/? (? = unused) for OPL 2-op and OPLL +- meaning of extended macros varies depending on instrument type. +- meaning of panning macros varies depending on instrument type: + - for hard-panned chips (e.g. FM and Game Boy): left panning is 2-bit panning macro (left/right) + - otherwise both left and right panning macros are used + +``` +size | description +-----|------------------------------------ + 4 | "INST" block ID + 4 | size of this block + 2 | format version (see header) + 1 | instrument type + | - 0: SN76489/standard + | - 1: FM (OPN) + | - 2: Game Boy + | - 3: C64 + | - 4: Amiga/sample + | - 5: PC Engine + | - 6: AY-3-8910 + | - 7: AY8930 + | - 8: TIA + | - 9: SAA1099 + | - 10: VIC + | - 11: PET + | - 12: VRC6 + | - 13: OPLL + | - 14: OPL + | - 15: FDS + | - 16: Virtual Boy + | - 17: Namco 163 + | - 18: SCC + | - 19: OPZ + | - 20: POKEY + | - 21: PC Speaker + | - 22: WonderSwan + | - 23: Lynx + | - 24: VERA + | - 25: X1-010 + | - 26: VRC6 (saw) + | - 27: ES5506 + | - 28: MultiPCM + | - 29: SNES + | - 30: Sound Unit + | - 31: Namco WSG + | - 32: OPL (drums) + | - 33: FM (OPM) + | - 34: NES + | - 35: MSM6258 + | - 36: MSM6295 + | - 37: ADPCM-A + | - 38: ADPCM-B + | - 39: SegaPCM + | - 40: QSound + | - 41: YMZ280B + | - 42: RF5C68 + | - 43: MSM5232 + | - 44: T6W28 + 1 | reserved + STR | instrument name + --- | **FM instrument data** + 1 | alg (SUS on OPLL) + 1 | feedback + 1 | fms (DC on OPLL) + 1 | ams (DM on OPLL) + 1 | operator count + | - this is either 2 or 4, and is ignored on non-OPL systems. + | - always read 4 ops regardless of this value. + 1 | OPLL preset (>=60) or reserved + | - 0: custom + | - 1-15: pre-defined patches + | - 16: drums (compatibility only!) + 2 | reserved + --- | **FM operator data** × 4 + 1 | am + 1 | ar + 1 | dr + 1 | mult + 1 | rr + 1 | sl + 1 | tl + 1 | dt2 + 1 | rs + 1 | dt + 1 | d2r + 1 | ssgEnv + | - bit 4: on (EG-S on OPLL) + | - bit 0-3: envelope type + 1 | dam (for YMU759 compat; REV on OPZ) + 1 | dvb (for YMU759 compat; FINE on OPZ) + 1 | egt (for YMU759 compat; FixedFreq on OPZ) + 1 | ksl (EGShift on OPZ) + 1 | sus + 1 | vib + 1 | ws + 1 | ksr + 1 | operator enabled (>=114) or reserved + 1 | KVS mode (>=115) or reserved + | - 0: off + | - 1: on + | - 2: auto (depending on alg) + 10 | reserved + --- | **Game Boy instrument data** + 1 | volume + 1 | direction + 1 | length + 1 | sound length + --- | **C64 instrument data** + 1 | triangle + 1 | saw + 1 | pulse + 1 | noise + 1 | attack + 1 | decay + 1 | sustain + 1 | release + 2 | duty + 1 | ring mod + 1 | osc sync + 1 | to filter + 1 | init filter + 1 | vol macro is cutoff (<187) or reserved + | - from version 187 onwards, volume and cutoff macros are separate. + | - if this is on and the version is less than 187, move the volume macro into the ALG one. + 1 | resonance + 1 | low pass + 1 | band pass + 1 | high pass + 1 | channel 3 off + 2 | cutoff + 1 | duty macro is absolute + 1 | filter macro is absolute + --- | **Amiga instrument data** + 2 | initial sample + 1 | mode (>=82) or reserved + | - 0: sample + | - 1: wavetable + 1 | wavetable length (-1) (>=82) or reserved + 12 | reserved + --- | **standard instrument data** + 4 | volume macro length + 4 | arp macro length + 4 | duty macro length + 4 | wave macro length + 4 | pitch macro length (>=17) + 4 | extra 1 macro length (>=17) + 4 | extra 2 macro length (>=17) + 4 | extra 3 macro length (>=17) + 4 | volume macro loop + 4 | arp macro loop + 4 | duty macro loop + 4 | wave macro loop + 4 | pitch macro loop (>=17) + 4 | extra 1 macro loop (>=17) + 4 | extra 2 macro loop (>=17) + 4 | extra 3 macro loop (>=17) + 1 | arp macro mode (<112) or reserved + | - treat this value in a special way. + | - before version 112, this byte indicates whether the arp macro mode is fixed or not. + | - from that version onwards, the fixed mode is part of the macro values. + | - to convert a <112 macro mode to a modern one, do the following: + | - is the macro mode set to fixed? + | - if yes, then: + | - set bit 30 of all arp macro values (this is the fixed mode bit) + | - does the macro loop? + | - if yes, then do nothing else + | - if no, then add one to the macro length, and set the last macro value to 0 + | - if no, then do nothing + 1 | reserved (>=17) or volume macro height (>=15) or reserved + 1 | reserved (>=17) or duty macro height (>=15) or reserved + 1 | reserved (>=17) or wave macro height (>=15) or reserved + 4?? | volume macro + | - before version 87, if this is the C64 relative cutoff macro, its values were stored offset by 18. + 4?? | arp macro + | - before version 31, this macro's values were stored offset by 12. + | - from version 112 onward, bit 30 of a value indicates fixed mode. + 4?? | duty macro + | - before version 87, if this is the C64 relative duty macro, its values were stored offset by 12. + 4?? | wave macro + 4?? | pitch macro (>=17) + 4?? | extra 1 macro (>=17) + 4?? | extra 2 macro (>=17) + 4?? | extra 3 macro (>=17) + 4 | alg macro length (>=29) + 4 | fb macro length (>=29) + 4 | fms macro length (>=29) + 4 | ams macro length (>=29) + 4 | alg macro loop (>=29) + 4 | fb macro loop (>=29) + 4 | fms macro loop (>=29) + 4 | ams macro loop (>=29) + 1 | volume macro open (>=29) + 1 | arp macro open (>=29) + 1 | duty macro open (>=29) + 1 | wave macro open (>=29) + 1 | pitch macro open (>=29) + 1 | extra 1 macro open (>=29) + 1 | extra 2 macro open (>=29) + 1 | extra 3 macro open (>=29) + 1 | alg macro open (>=29) + 1 | fb macro open (>=29) + 1 | fms macro open (>=29) + 1 | ams macro open (>=29) + 4?? | alg macro (>=29) + 4?? | fb macro (>=29) + 4?? | fms macro (>=29) + 4?? | ams macro (>=29) + --- | **operator macro headers** × 4 (>=29) + 4 | AM macro length + 4 | AR macro length + 4 | DR macro length + 4 | MULT macro length + 4 | RR macro length + 4 | SL macro length + 4 | TL macro length + 4 | DT2 macro length + 4 | RS macro length + 4 | DT macro length + 4 | D2R macro length + 4 | SSG-EG macro length + 4 | AM macro loop + 4 | AR macro loop + 4 | DR macro loop + 4 | MULT macro loop + 4 | RR macro loop + 4 | SL macro loop + 4 | TL macro loop + 4 | DT2 macro loop + 4 | RS macro loop + 4 | DT macro loop + 4 | D2R macro loop + 4 | SSG-EG macro loop + 1 | AM macro open + 1 | AR macro open + 1 | DR macro open + 1 | MULT macro open + 1 | RR macro open + 1 | SL macro open + 1 | TL macro open + 1 | DT2 macro open + 1 | RS macro open + 1 | DT macro open + 1 | D2R macro open + 1 | SSG-EG macro open + --- | **operator macros** × 4 (>=29) + 1?? | AM macro + 1?? | AR macro + 1?? | DR macro + 1?? | MULT macro + 1?? | RR macro + 1?? | SL macro + 1?? | TL macro + 1?? | DT2 macro + 1?? | RS macro + 1?? | DT macro + 1?? | D2R macro + 1?? | SSG-EG macro + --- | **release points** (>=44) + 4 | volume macro release + 4 | arp macro release + 4 | duty macro release + 4 | wave macro release + 4 | pitch macro release + 4 | extra 1 macro release + 4 | extra 2 macro release + 4 | extra 3 macro release + 4 | alg macro release + 4 | fb macro release + 4 | fms macro release + 4 | ams macro release + --- | **operator release points** × 4 (>=44) + 4 | AM macro release + 4 | AR macro release + 4 | DR macro release + 4 | MULT macro release + 4 | RR macro release + 4 | SL macro release + 4 | TL macro release + 4 | DT2 macro release + 4 | RS macro release + 4 | DT macro release + 4 | D2R macro release + 4 | SSG-EG macro release + --- | **extended op macro headers** × 4 (>=61) + 4 | DAM macro length + 4 | DVB macro length + 4 | EGT macro length + 4 | KSL macro length + 4 | SUS macro length + 4 | VIB macro length + 4 | WS macro length + 4 | KSR macro length + 4 | DAM macro loop + 4 | DVB macro loop + 4 | EGT macro loop + 4 | KSL macro loop + 4 | SUS macro loop + 4 | VIB macro loop + 4 | WS macro loop + 4 | KSR macro loop + 4 | DAM macro release + 4 | DVB macro release + 4 | EGT macro release + 4 | KSL macro release + 4 | SUS macro release + 4 | VIB macro release + 4 | WS macro release + 4 | KSR macro release + 1 | DAM macro open + 1 | DVB macro open + 1 | EGT macro open + 1 | KSL macro open + 1 | SUS macro open + 1 | VIB macro open + 1 | WS macro open + 1 | KSR macro open + --- | **extended op macros** × 4 (>=61) + 1?? | DAM macro + 1?? | DVB macro + 1?? | EGT macro + 1?? | KSL macro + 1?? | SUS macro + 1?? | VIB macro + 1?? | WS macro + 1?? | KSR macro + --- | **OPL drums mode data** (>=63) + 1 | fixed frequency mode + 1 | reserved + 2 | kick frequency + 2 | snare/hi-hat frequency + 2 | tom/top frequency + --- | **Sample instrument extra data** (>=67) + 1 | use note map + | - only read the following two data structures if this is true! + 4?? | note frequency × 120 + | - 480 bytes + 2?? | note sample × 120 + | - 240 bytes + --- | **Namco 163 data** (>=73) + 4 | initial waveform + 1 | wave position + 1 | wave length + 1 | wave mode: + | - bit 1: update on change + | - bit 0: load on playback + 1 | reserved + --- | **even more macros** (>=76) + 4 | left panning macro length + 4 | right panning macro length + 4 | phase reset macro length + 4 | extra 4 macro length + 4 | extra 5 macro length + 4 | extra 6 macro length + 4 | extra 7 macro length + 4 | extra 8 macro length + 4 | left panning macro loop + 4 | right panning macro loop + 4 | phase reset macro loop + 4 | extra 4 macro loop + 4 | extra 5 macro loop + 4 | extra 6 macro loop + 4 | extra 7 macro loop + 4 | extra 8 macro loop + 4 | left panning macro release + 4 | right panning macro release + 4 | phase reset macro release + 4 | extra 4 macro release + 4 | extra 5 macro release + 4 | extra 6 macro release + 4 | extra 7 macro release + 4 | extra 8 macro release + 1 | left panning macro open + 1 | right panning macro open + 1 | phase reset macro open + 1 | extra 4 macro open + 1 | extra 5 macro open + 1 | extra 6 macro open + 1 | extra 7 macro open + 1 | extra 8 macro open + --- | **even more macro data** (>=76) + 4?? | left panning macro + 4?? | right panning macro + 4?? | phase reset macro + 4?? | extra 4 macro + 4?? | extra 5 macro + 4?? | extra 6 macro + 4?? | extra 7 macro + 4?? | extra 8 macro + --- | **FDS instrument data** (>=76) + 4 | modulation speed + 4 | modulation depth + 1 | init modulation table with first wave + 3 | reserved + 32 | modulation table + --- | **OPZ instrument extra data** (>=77) + 1 | fms2 + 1 | ams2 + --- | **wavetable synth data** (>=79) + 4 | first wave + 4 | second wave + 1 | rate divider + 1 | effect + | - bit 7: single or dual effect + 1 | enabled + 1 | global + 1 | speed (+1) + 1 | parameter 1 + 1 | parameter 2 + 1 | parameter 3 + 1 | parameter 4 + --- | **additional macro mode flags** (>=84) + 1 | volume macro mode + 1 | duty macro mode + 1 | wave macro mode + 1 | pitch macro mode + 1 | extra 1 macro mode + 1 | extra 2 macro mode + 1 | extra 3 macro mode + 1 | alg macro mode + 1 | fb macro mode + 1 | fms macro mode + 1 | ams macro mode + 1 | left panning macro mode + 1 | right panning macro mode + 1 | phase reset macro mode + 1 | extra 4 macro mode + 1 | extra 5 macro mode + 1 | extra 6 macro mode + 1 | extra 7 macro mode + 1 | extra 8 macro mode + --- | **extra C64 data** (>=89) + 1 | don't test/gate before new note + --- | **MultiPCM data** (>=93) + 1 | attack rate + 1 | decay 1 rate + 1 | decay level + 1 | decay 2 rate + 1 | release rate + 1 | rate correction + 1 | lfo rate + 1 | vib depth + 1 | am depth + 23 | reserved + --- | **Sound Unit data** (>=104) + 1 | use sample + 1 | switch roles of phase reset timer and frequency + --- | **Game Boy envelope sequence** (>=105) + 1 | length + ??? | hardware sequence data + | size is length*3: + | 1 byte: command + | - 0: set envelope + | - 1: set sweep + | - 2: wait + | - 3: wait for release + | - 4: loop + | - 5: loop until release + | 2 bytes: data + | - for set envelope: + | - 1 byte: parameter + | - bit 4-7: volume + | - bit 3: direction + | - bit 0-2: length + | - 1 byte: sound length + | - for set sweep: + | - 1 byte: parameter + | - bit 4-6: length + | - bit 3: direction + | - bit 0-2: shift + | - 1 byte: nothing + | - for wait: + | - 1 byte: length (in ticks) + | - 1 byte: nothing + | - for wait for release: + | - 2 bytes: nothing + | - for loop/loop until release: + | - 2 bytes: position + --- | **Game Boy extra flags** (>=106) + 1 | use software envelope + 1 | always init hard env on new note + --- | **ES5506 data** (>=107) + 1 | filter mode + | - 0: HPK2_HPK2 + | - 1: HPK2_LPK1 + | - 2: LPK2_LPK2 + | - 3: LPK2_LPK1 + 2 | K1 + 2 | K2 + 2 | envelope count + 1 | left volume ramp + 1 | right volume ramp + 1 | K1 ramp + 1 | K2 ramp + 1 | K1 slow + 1 | K2 slow + --- | **SNES data** (>=109) + 1 | use envelope + 1 | gain mode + 1 | gain + 1 | attack + 1 | decay + 1 | sustain + | - bit 3: sustain mode (>=118) + 1 | release + --- | **macro speeds/delays** (>=111) + 1 | volume macro speed + 1 | arp macro speed + 1 | duty macro speed + 1 | wave macro speed + 1 | pitch macro speed + 1 | extra 1 macro speed + 1 | extra 2 macro speed + 1 | extra 3 macro speed + 1 | alg macro speed + 1 | fb macro speed + 1 | fms macro speed + 1 | ams macro speed + 1 | left panning macro speed + 1 | right panning macro speed + 1 | phase reset macro speed + 1 | extra 4 macro speed + 1 | extra 5 macro speed + 1 | extra 6 macro speed + 1 | extra 7 macro speed + 1 | extra 8 macro speed + 1 | volume macro delay + 1 | arp macro delay + 1 | duty macro delay + 1 | wave macro delay + 1 | pitch macro delay + 1 | extra 1 macro delay + 1 | extra 2 macro delay + 1 | extra 3 macro delay + 1 | alg macro delay + 1 | fb macro delay + 1 | fms macro delay + 1 | ams macro delay + 1 | left panning macro delay + 1 | right panning macro delay + 1 | phase reset macro delay + 1 | extra 4 macro delay + 1 | extra 5 macro delay + 1 | extra 6 macro delay + 1 | extra 7 macro delay + 1 | extra 8 macro delay + --- | **operator macro speeds/delay** × 4 (>=111) + 1 | AM macro speed + 1 | AR macro speed + 1 | DR macro speed + 1 | MULT macro speed + 1 | RR macro speed + 1 | SL macro speed + 1 | TL macro speed + 1 | DT2 macro speed + 1 | RS macro speed + 1 | DT macro speed + 1 | D2R macro speed + 1 | SSG-EG macro speed + 1 | DAM macro speed + 1 | DVB macro speed + 1 | EGT macro speed + 1 | KSL macro speed + 1 | SUS macro speed + 1 | VIB macro speed + 1 | WS macro speed + 1 | KSR macro speed + 1 | AM macro delay + 1 | AR macro delay + 1 | DR macro delay + 1 | MULT macro delay + 1 | RR macro delay + 1 | SL macro delay + 1 | TL macro delay + 1 | DT2 macro delay + 1 | RS macro delay + 1 | DT macro delay + 1 | D2R macro delay + 1 | SSG-EG macro delay + 1 | DAM macro delay + 1 | DVB macro delay + 1 | EGT macro delay + 1 | KSL macro delay + 1 | SUS macro delay + 1 | VIB macro delay + 1 | WS macro delay + 1 | KSR macro delay +``` + +## interpreting macro mode values + +- sequence (normal): I think this is obvious... +- ADSR: + - `val[0]`: bottom + - `val[1]`: top + - `val[2]`: attack + - `val[3]`: hold time + - `val[4]`: decay + - `val[5]`: sustain level + - `val[6]`: sustain hold time + - `val[7]`: decay 2 + - `val[8]`: release +- LFO: + - `val[11]`: speed + - `val[12]`: waveform + - 0: triangle + - 1: saw + - 2: pulse + - `val[13]`: phase + - `val[14]`: loop + - `val[15]`: global (not sure how will I implement this) + diff --git a/res/Info.plist b/res/Info.plist index 7895024da..20f1b73b5 100644 --- a/res/Info.plist +++ b/res/Info.plist @@ -15,17 +15,17 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString - 0.6 + 0.6.1 CFBundleName Furnace CFBundlePackageType APPL CFBundleShortVersionString - 0.6 + 0.6.1 CFBundleSignature ???? CFBundleVersion - 0.6 + 0.6.1 NSHumanReadableCopyright NSHighResolutionCapable diff --git a/res/docpdf/make_paper.py b/res/docpdf/make_paper.py index 72708a436..91e97e77c 100644 --- a/res/docpdf/make_paper.py +++ b/res/docpdf/make_paper.py @@ -325,7 +325,7 @@ if __name__ == "__main__":

Furnace
User Manual

- for version 0.6 + for version 0.6.1
@@ -348,7 +348,7 @@ if __name__ == "__main__":

this documentation is under the Creative Commons Attribution 3.0 Unported license.

you may reproduce, modify and/or distribute this documentation provided this copyright notice (including license and attribution) is present and any necessary disclaimers whether modifications have been made.

this documentation is provided as-is and without warranty of any kind.

-

this manual is written for version 0.6 of Furnace.
it may not necessarily apply to previous or future versions.

+

this manual is written for version 0.6.1 of Furnace.
it may not necessarily apply to previous or future versions.

%s diff --git a/res/furnace.appdata.xml.in b/res/furnace.appdata.xml.in index 6103fef1f..782c7448a 100644 --- a/res/furnace.appdata.xml.in +++ b/res/furnace.appdata.xml.in @@ -4,6 +4,10 @@ Furnace Open-source chiptune tracker + + tildearrow and contributors + + tildearrow and contributors https://github.com/tildearrow/furnace CC0-1.0 @@ -32,7 +36,8 @@ furnace.desktop - https://tildearrow.org/storage/images/furnace.png + Furnace during playback + https://raw.githubusercontent.com/tildearrow/furnace/94cce861800c0473bdddb8270e462ebcdd18202a/papers/screenshot3.png diff --git a/res/icons.sfd b/res/icons.sfd index 769b73fbc..558f8c051 100644 --- a/res/icons.sfd +++ b/res/icons.sfd @@ -21,7 +21,7 @@ OS2Version: 0 OS2_WeightWidthSlopeOnly: 0 OS2_UseTypoMetrics: 0 CreationTime: 1691897631 -ModificationTime: 1701817435 +ModificationTime: 1706927377 PfmFamily: 81 TTFWeight: 400 TTFWidth: 5 @@ -47,13 +47,13 @@ LangName: 1033 Encoding: UnicodeBmp UnicodeInterp: none NameList: AGL For New Fonts -DisplaySize: -96 +DisplaySize: -24 AntiAlias: 1 FitToEm: 0 -WinInfo: 57672 8 5 +WinInfo: 57540 21 12 BeginPrivate: 0 EndPrivate -BeginChars: 65536 96 +BeginChars: 65536 98 StartChar: space Encoding: 32 32 0 @@ -2270,15 +2270,15 @@ SplineSet 1653.60449219 798.544921875 1681.75488281 781.904296875 1709.90625 765.263671875 c 1 1688.8125 716.044921875 1660.609375 678.232421875 1625.453125 651.982421875 c 0 1590.21875 625.732421875 1544.125 612.685546875 1487.171875 612.685546875 c 0xcbcd -1289.23339844 435.383789062 m 1 - 1289.23339844 -25.1181640625 l 1 - 1585.29882812 247.456054688 l 1 - 1585.29882812 117.280273438 l 1 - 1187.45410156 -248.998046875 l 1xd3ce - 1187.45410156 211.504882812 l 1 - 891.388671875 -61.0693359375 l 1 - 891.388671875 69.1064453125 l 1 - 1289.23339844 435.383789062 l 1 +1289.23339844 435.383789062 m 5 + 1289.23339844 -25.1181640625 l 5 + 1585.29882812 247.456054688 l 5 + 1585.29882812 117.280273438 l 5 + 1187.45410156 -248.998046875 l 5xd3ce + 1187.45410156 211.504882812 l 5 + 891.388671875 -61.0693359375 l 5 + 891.388671875 69.1064453125 l 5 + 1289.23339844 435.383789062 l 5 EndSplineSet EndChar @@ -6736,54 +6736,8 @@ EndChar StartChar: uniE159 Encoding: 57689 57689 95 Width: 1792 -Flags: HWO +Flags: HW LayerCount: 2 -UndoRedoHistory -Layer: 0 -Undoes -UndoOperation -Index: 0 -Type: 1 -WasModified: 0 -WasOrder2: 0 -Layer: 1 -Width: 1792 -VWidth: 1792 -LBearingChange: 0 -UnicodeEnc: 0 -InstructionsLength: 0 -SplineSet -1632 70 m 1 - 1632 -50 l 1025 -160 1210 m 1 - 160 1330 l 1025 -EndSplineSet -EndUndoOperation -UndoOperation -Index: 1 -Type: 1 -WasModified: 0 -WasOrder2: 0 -Layer: 2 -Width: 1792 -VWidth: 1792 -LBearingChange: 0 -UnicodeEnc: 0 -InstructionsLength: 0 -SplineSet -160 1270 m 25 - 160 1270 560 1270 896 640 c 0 - 1232 10 1632 10 1632 10 c 1025 -1632 70 m 5 - 1632 -50 l 1029 -160 1210 m 5 - 160 1330 l 1029 -EndSplineSet -EndUndoOperation -EndUndoes -Redoes -EndRedoes -EndUndoRedoHistory Back SplineSet 160 1270 m 25 @@ -6794,314 +6748,6 @@ SplineSet 160 1210 m 1 160 1330 l 1025 EndSplineSet -UndoRedoHistory -Layer: 1 -Undoes -UndoOperation -Index: 0 -Type: 1 -WasModified: 1 -WasOrder2: 0 -Layer: 2 -Width: 1792 -VWidth: 1792 -LBearingChange: 0 -UnicodeEnc: 0 -InstructionsLength: 0 -SplineSet -1632 1210.01171875 m 1 - 1632 1329.98828125 l 1 - 1619.75878906 1329.98828125 1193.45019531 1325.24316406 843.048828125 668.2421875 c 0 - 755.229492188 503.581054688 663.75 384.705078125 577.82421875 298.780273438 c 0 - 473.91796875 194.873046875 377.610351562 138.502929688 303.73046875 107.581054688 c 0 - 262.674804688 90.396484375 228.48828125 81.078125 203.702148438 76.0087890625 c 0 - 176.166992188 70.3779296875 160.23046875 70 159.536132812 69.986328125 c 1 - 159.698242188 69.9873046875 160 69.98828125 160 69.98828125 c 1 - 160 -49.98828125 l 1 - 172.241210938 -49.98828125 598.549804688 -45.2431640625 948.951171875 611.7578125 c 0 - 1036.77050781 776.418945312 1128.25 895.294921875 1214.17578125 981.219726562 c 0 - 1318.08203125 1085.12695312 1414.38964844 1141.49707031 1488.26953125 1172.41894531 c 0 - 1529.32519531 1189.60351562 1563.51171875 1198.921875 1588.29785156 1203.99121094 c 0 - 1615.83300781 1209.62207031 1631.76953125 1210 1632.46386719 1210.01367188 c 1 - 1632.30175781 1210.01269531 1632 1210.01171875 1632 1210.01171875 c 1 -159.999999952 1210.01176537 m 1 - 159.999999952 1329.98823463 l 1 - 170.809951102 1329.98823463 504.536374276 1326.28810718 823.734604363 872.948478072 c 1 - 800.889124326 837.280193024 778.251310121 799.419416229 755.928040313 759.232259114 c 1 - 695.916689064 850.735795855 635.614260663 923.430234862 577.824293264 981.220202232 c 0 - 473.917602143 1085.12689335 377.61065751 1141.49706263 303.730893464 1172.41936621 c 0 - 262.674555191 1189.60345677 228.488523437 1198.92230209 203.702403502 1203.99126863 c 0 - 176.167181121 1209.62244935 160.230079948 1210.00005891 159.536222574 1210.01396385 c 1 - 159.698263781 1210.01275358 159.999999952 1210.01176537 159.999999952 1210.01176537 c 1 -1632 1090.03515625 m 5 - 1632 1449.96484375 l 5 - 1558.47725996 1449.96484375 1167.58409474 1409.79522486 823.734604363 872.948478072 c 5 - 865.988935134 812.936984165 907.988680731 745.046220598 948.951069976 668.241740764 c 4 - 977.706682766 614.324966782 1006.85482384 565.317167145 1036.07195969 520.767740886 c 5 - 1042.35821703 532.084488601 1048.61953126 543.58571637 1054.85351562 555.274414062 c 4 - 1093.06152344 626.915039062 1265.13574219 948.919921875 1534.69042969 1061.74121094 c 4 - 1536.87304688 1062.65429688 1591.23828125 1085.29492188 1632 1090.03515625 c 5 -1036.07195969 520.767740886 m 1 - 1096.08331094 429.264204145 1156.38573934 356.569765138 1214.17570674 298.779797768 c 0 - 1318.08239786 194.873106647 1414.38934249 138.502937368 1488.26910654 107.580633787 c 0 - 1529.32544481 90.3965432265 1563.51147656 81.0776979108 1588.2975965 76.0087313726 c 0 - 1615.83281888 70.3775506458 1631.76992005 69.9999410877 1632.46377743 69.986036148 c 1 - 1632.30173622 69.9872464204 1632.00000005 69.9882346324 1632.00000005 69.9882346324 c 1 - 1632.00000005 -49.9882346324 l 1 - 1621.1900489 -49.9882346324 1287.46362572 -46.2881071791 968.265395637 407.051521928 c 1 - 991.110875674 442.719806976 1013.74868988 480.580583771 1036.07195969 520.767740886 c 1 -968.265395637 407.051521928 m 5 - 926.011064866 467.063015835 884.011319269 534.953779402 843.048930024 611.758259236 c 4 - 814.293317234 665.675033218 785.145176159 714.682832855 755.928040313 759.232259114 c 5 - 749.641782968 747.9155114 743.380468741 736.41428363 737.146484375 724.725585938 c 4 - 698.938476562 653.084960938 526.864257812 331.080078125 257.309570312 218.258789062 c 4 - 255.126953125 217.345703125 200.76171875 194.705078125 160 189.96484375 c 5 - 160 -169.96484375 l 5 - 233.522740042 -169.96484375 624.415905262 -129.795224857 968.265395637 407.051521928 c 5 -EndSplineSet -EndUndoOperation -UndoOperation -Index: 1 -Type: 1 -WasModified: 0 -WasOrder2: 0 -Layer: 2 -Width: 1792 -VWidth: 1792 -LBearingChange: 0 -UnicodeEnc: 0 -InstructionsLength: 0 -SplineSet -1632 1090.03515625 m 5 - 1632 1449.96484375 l 5 - 1552.21679688 1449.96484375 1098.71386719 1402.66308594 737.146484375 724.725585938 c 4 - 698.938476562 653.084960938 526.864257812 331.080078125 257.309570312 218.258789062 c 4 - 255.126953125 217.345703125 200.76171875 194.705078125 160 189.96484375 c 5 - 160 -169.96484375 l 5 - 239.783203125 -169.96484375 693.286132812 -122.663085938 1054.85351562 555.274414062 c 4 - 1093.06152344 626.915039062 1265.13574219 948.919921875 1534.69042969 1061.74121094 c 4 - 1536.87304688 1062.65429688 1591.23828125 1085.29492188 1632 1090.03515625 c 5 -1632 1210.01171875 m 1 - 1632 1329.98828125 l 1 - 1619.75878906 1329.98828125 1193.45019531 1325.24316406 843.048828125 668.2421875 c 0 - 755.229492188 503.581054688 663.75 384.705078125 577.82421875 298.780273438 c 0 - 473.91796875 194.873046875 377.610351562 138.502929688 303.73046875 107.581054688 c 0 - 262.674804688 90.396484375 228.48828125 81.078125 203.702148438 76.0087890625 c 0 - 176.166992188 70.3779296875 160.23046875 70 159.536132812 69.986328125 c 1 - 159.698242188 69.9873046875 160 69.98828125 160 69.98828125 c 1 - 160 -49.98828125 l 1 - 172.241210938 -49.98828125 598.549804688 -45.2431640625 948.951171875 611.7578125 c 0 - 1036.77050781 776.418945312 1128.25 895.294921875 1214.17578125 981.219726562 c 0 - 1318.08203125 1085.12695312 1414.38964844 1141.49707031 1488.26953125 1172.41894531 c 0 - 1529.32519531 1189.60351562 1563.51171875 1198.921875 1588.29785156 1203.99121094 c 0 - 1615.83300781 1209.62207031 1631.76953125 1210 1632.46386719 1210.01367188 c 1 - 1632.30175781 1210.01269531 1632 1210.01171875 1632 1210.01171875 c 1 -159.999999952 1210.01176537 m 5 - 159.999999952 1329.98823463 l 5 - 172.240934135 1329.98823462 598.550052583 1325.24364837 948.951069976 668.241740764 c 4 - 1036.77007573 503.581104976 1128.25010681 384.70539769 1214.17570674 298.779797768 c 4 - 1318.08239786 194.873106647 1414.38934249 138.502937368 1488.26910654 107.580633787 c 4 - 1529.32544481 90.3965432265 1563.51147656 81.0776979108 1588.2975965 76.0087313726 c 4 - 1615.83281888 70.3775506458 1631.76992005 69.9999410877 1632.46377743 69.986036148 c 5 - 1632.30173622 69.9872464204 1632.00000005 69.9882346324 1632.00000005 69.9882346324 c 5 - 1632.00000005 -49.9882346324 l 5 - 1619.75906587 -49.9882346228 1193.44994742 -45.2436483754 843.048930024 611.758259236 c 4 - 755.229924271 776.418895024 663.749893187 895.29460231 577.824293264 981.220202232 c 4 - 473.917602143 1085.12689335 377.61065751 1141.49706263 303.730893464 1172.41936621 c 4 - 262.674555191 1189.60345677 228.488523437 1198.92230209 203.702403502 1203.99126863 c 4 - 176.167181121 1209.62244935 160.230079948 1210.00005891 159.536222574 1210.01396385 c 5 - 159.698263781 1210.01275358 159.999999952 1210.01176537 159.999999952 1210.01176537 c 5 -EndSplineSet -EndUndoOperation -UndoOperation -Index: 2 -Type: 1 -WasModified: 1 -WasOrder2: 0 -Layer: 2 -Width: 1792 -VWidth: 1792 -LBearingChange: 0 -UnicodeEnc: 0 -InstructionsLength: 0 -SplineSet -160 1270 m 29 - 160 1270 560 1270 896 640 c 4 - 1232 10 1632 10 1632 10 c 1029 -1632 1210.01171875 m 1 - 1632 1329.98828125 l 1 - 1619.75878906 1329.98828125 1193.45019531 1325.24316406 843.048828125 668.2421875 c 0 - 755.229492188 503.581054688 663.75 384.705078125 577.82421875 298.780273438 c 0 - 473.91796875 194.873046875 377.610351562 138.502929688 303.73046875 107.581054688 c 0 - 262.674804688 90.396484375 228.48828125 81.078125 203.702148438 76.0087890625 c 0 - 176.166992188 70.3779296875 160.23046875 70 159.536132812 69.986328125 c 1 - 159.698242188 69.9873046875 160 69.98828125 160 69.98828125 c 1 - 160 -49.98828125 l 1 - 172.241210938 -49.98828125 598.549804688 -45.2431640625 948.951171875 611.7578125 c 0 - 1036.77050781 776.418945312 1128.25 895.294921875 1214.17578125 981.219726562 c 0 - 1318.08203125 1085.12695312 1414.38964844 1141.49707031 1488.26953125 1172.41894531 c 0 - 1529.32519531 1189.60351562 1563.51171875 1198.921875 1588.29785156 1203.99121094 c 0 - 1615.83300781 1209.62207031 1631.76953125 1210 1632.46386719 1210.01367188 c 1 - 1632.30175781 1210.01269531 1632 1210.01171875 1632 1210.01171875 c 1 -1632 1090.03515625 m 1 - 1632 1449.96484375 l 1 - 1552.21679688 1449.96484375 1098.71386719 1402.66308594 737.146484375 724.725585938 c 0 - 698.938476562 653.084960938 526.864257812 331.080078125 257.309570312 218.258789062 c 0 - 255.126953125 217.345703125 200.76171875 194.705078125 160 189.96484375 c 1 - 160 -169.96484375 l 1 - 239.783203125 -169.96484375 693.286132812 -122.663085938 1054.85351562 555.274414062 c 0 - 1093.06152344 626.915039062 1265.13574219 948.919921875 1534.69042969 1061.74121094 c 0 - 1536.87304688 1062.65429688 1591.23828125 1085.29492188 1632 1090.03515625 c 1 -EndSplineSet -EndUndoOperation -UndoOperation -Index: 3 -Type: 3 -WasModified: 1 -WasOrder2: 0 -Layer: 2 -EndUndoOperation -UndoOperation -Index: 4 -Type: 1 -WasModified: 1 -WasOrder2: 0 -Layer: 2 -Width: 1792 -VWidth: 1792 -LBearingChange: 0 -UnicodeEnc: 0 -InstructionsLength: 0 -SplineSet -159.999999952 1210.01176537 m 5 - 159.999999952 1329.98823463 l 5 - 172.240934135 1329.98823462 598.550052583 1325.24364837 948.951069976 668.241740764 c 4 - 1036.77007573 503.581104976 1128.25010681 384.70539769 1214.17570674 298.779797768 c 4 - 1318.08239786 194.873106647 1414.38934249 138.502937368 1488.26910654 107.580633787 c 4 - 1529.32544481 90.3965432265 1563.51147656 81.0776979108 1588.2975965 76.0087313726 c 4 - 1615.83281888 70.3775506458 1631.76992005 69.9999410877 1632.46377743 69.986036148 c 5 - 1632.30173622 69.9872464204 1632.00000005 69.9882346324 1632.00000005 69.9882346324 c 5 - 1632.00000005 -49.9882346324 l 5 - 1619.75906587 -49.9882346228 1193.44994742 -45.2436483754 843.048930024 611.758259236 c 4 - 755.229924271 776.418895024 663.749893187 895.29460231 577.824293264 981.220202232 c 4 - 473.917602143 1085.12689335 377.61065751 1141.49706263 303.730893464 1172.41936621 c 4 - 262.674555191 1189.60345677 228.488523437 1198.92230209 203.702403502 1203.99126863 c 4 - 176.167181121 1209.62244935 160.230079948 1210.00005891 159.536222574 1210.01396385 c 5 - 159.698263781 1210.01275358 159.999999952 1210.01176537 159.999999952 1210.01176537 c 5 -159.999999857 1090.0352961 m 5 - 159.999999857 1449.9647039 l 5 - 239.782893731 1449.96470383 693.286263441 1402.66324695 1054.85320993 724.725222292 c 4 - 1093.06152816 653.084625607 1265.13598183 331.080480771 1534.69080463 218.25857115 c 4 - 1536.87267214 217.345352617 1591.23876559 194.705394151 1632.00000014 189.964703897 c 5 - 1632.00000014 -169.964703897 l 5 - 1552.21710627 -169.964703835 1098.71373656 -122.663246954 737.146790073 555.274777708 c 4 - 698.938471842 626.915374393 526.864018169 948.919519229 257.309195374 1061.74142885 c 4 - 255.127327856 1062.65464738 200.761234414 1085.29460585 159.999999857 1090.0352961 c 5 -EndSplineSet -EndUndoOperation -UndoOperation -Index: 5 -Type: 3 -WasModified: 1 -WasOrder2: 0 -Layer: 2 -EndUndoOperation -UndoOperation -Index: 6 -Type: 1 -WasModified: 1 -WasOrder2: 0 -Layer: 2 -Width: 1792 -VWidth: 1792 -LBearingChange: 0 -UnicodeEnc: 0 -InstructionsLength: 0 -SplineSet -160 1270 m 29 - 160 1270 560 1270 896 640 c 4 - 1232 10 1632 10 1632 10 c 1029 -159.999999952 1210.01176537 m 1 - 159.999999952 1329.98823463 l 1 - 172.240934135 1329.98823462 598.550052583 1325.24364837 948.951069976 668.241740764 c 0 - 1036.77007573 503.581104976 1128.25010681 384.70539769 1214.17570674 298.779797768 c 0 - 1318.08239786 194.873106647 1414.38934249 138.502937368 1488.26910654 107.580633787 c 0 - 1529.32544481 90.3965432265 1563.51147656 81.0776979108 1588.2975965 76.0087313726 c 0 - 1615.83281888 70.3775506458 1631.76992005 69.9999410877 1632.46377743 69.986036148 c 1 - 1632.30173622 69.9872464204 1632.00000005 69.9882346324 1632.00000005 69.9882346324 c 1 - 1632.00000005 -49.9882346324 l 1 - 1619.75906587 -49.9882346228 1193.44994742 -45.2436483754 843.048930024 611.758259236 c 0 - 755.229924271 776.418895024 663.749893187 895.29460231 577.824293264 981.220202232 c 0 - 473.917602143 1085.12689335 377.61065751 1141.49706263 303.730893464 1172.41936621 c 0 - 262.674555191 1189.60345677 228.488523437 1198.92230209 203.702403502 1203.99126863 c 0 - 176.167181121 1209.62244935 160.230079948 1210.00005891 159.536222574 1210.01396385 c 1 - 159.698263781 1210.01275358 159.999999952 1210.01176537 159.999999952 1210.01176537 c 1 -EndSplineSet -EndUndoOperation -UndoOperation -Index: 7 -Type: 3 -WasModified: 1 -WasOrder2: 0 -Layer: 2 -EndUndoOperation -UndoOperation -Index: 8 -Type: 1 -WasModified: 1 -WasOrder2: 0 -Layer: 2 -Width: 1792 -VWidth: 1792 -LBearingChange: 0 -UnicodeEnc: 0 -InstructionsLength: 0 -SplineSet -160 1270 m 29 - 160 1270 560 1270 896 640 c 4 - 1232 10 1632 10 1632 10 c 1029 -EndSplineSet -EndUndoOperation -UndoOperation -Index: 9 -Type: 3 -WasModified: 1 -WasOrder2: 0 -Layer: 2 -EndUndoOperation -UndoOperation -Index: 10 -Type: 2 -WasModified: 1 -WasOrder2: 0 -Layer: 2 -Width: 1792 -VWidth: 1792 -LBearingChange: 0 -UnicodeEnc: 0 -InstructionsLength: 0 -SplineSet -160 1270 m 29 - 160 1270 560 1270 896 640 c 4 - 1232 10 1632 10 1632 10 c 1029 -EndSplineSet -EndUndoOperation -UndoOperation -Index: 11 -Type: 1 -WasModified: 1 -WasOrder2: 0 -Layer: 1 -Width: 1792 -VWidth: 1792 -LBearingChange: 0 -UnicodeEnc: 0 -InstructionsLength: 0 -EndUndoOperation -EndUndoes -Redoes -EndRedoes -EndUndoRedoHistory Fore SplineSet 1632 1210.01171875 m 1 @@ -7139,5 +6785,102 @@ SplineSet 991.110875674 442.719806976 1013.74868988 480.580583771 1036.07195969 520.767740886 c 1 EndSplineSet EndChar + +StartChar: uniE15A +Encoding: 57690 57690 96 +Width: 1792 +Flags: HW +LayerCount: 2 +Fore +SplineSet +170.8046875 72.1796875 m 1 + 170.8046875 351.37890625 170.8046875 630.578125 170.8046875 909.77734375 c 1 + 290.791992188 909.77734375 410.778320312 909.77734375 530.765625 909.77734375 c 1 + 608.40234375 909.77734375 668.36328125 887.0234375 710.74609375 841.41796875 c 128 + 753.2265625 795.8125 774.41796875 734.58203125 774.41796875 657.82421875 c 256 + 774.41796875 580.96875 753.2265625 519.73828125 710.74609375 474.1328125 c 128 + 668.36328125 428.625 608.40234375 405.7734375 530.765625 405.7734375 c 1 + 455.9609375 405.7734375 381.15625 405.7734375 306.3515625 405.7734375 c 1 + 306.3515625 294.576171875 306.3515625 183.377929688 306.3515625 72.1796875 c 1 + 261.168945312 72.1796875 215.987304688 72.1796875 170.8046875 72.1796875 c 1 +306.3515625 524.62109375 m 1 + 378.356445312 524.62109375 450.362304688 524.62109375 522.3671875 524.62109375 c 1 + 556.7421875 524.62109375 583.59765625 533.60546875 602.73828125 551.57421875 c 128 + 621.9765625 569.54296875 631.546875 595.421875 631.546875 629.015625 c 1 + 631.546875 648.189453125 631.546875 667.362304688 631.546875 686.53515625 c 1 + 631.546875 720.2265625 621.9765625 746.0078125 602.73828125 763.9765625 c 128 + 583.59765625 781.9453125 556.7421875 791.02734375 522.3671875 791.02734375 c 1 + 450.362304688 791.02734375 378.356445312 791.02734375 306.3515625 791.02734375 c 1 + 306.3515625 702.225585938 306.3515625 613.423828125 306.3515625 524.62109375 c 1 +1196.390625 535.36328125 m 1 + 1165.59667969 594.576171875 1134.80175781 653.788085938 1104.0078125 713 c 1 + 1102.80371094 713 1101.59863281 713 1100.39453125 713 c 1 + 1100.39453125 499.393554688 1100.39453125 285.787109375 1100.39453125 72.1796875 c 1 + 1057.19824219 72.1796875 1014.00097656 72.1796875 970.8046875 72.1796875 c 1 + 970.8046875 351.37890625 970.8046875 630.578125 970.8046875 909.77734375 c 1 + 1021.1953125 909.77734375 1071.5859375 909.77734375 1121.9765625 909.77734375 c 1 + 1213.1875 755.3828125 1304.3984375 600.98828125 1395.609375 446.59375 c 1 + 1426.40332031 387.381835938 1457.19824219 328.169921875 1487.9921875 268.95703125 c 1 + 1489.19628906 268.95703125 1490.40136719 268.95703125 1491.60546875 268.95703125 c 1 + 1491.60546875 482.564453125 1491.60546875 696.170898438 1491.60546875 909.77734375 c 1 + 1534.80175781 909.77734375 1577.99902344 909.77734375 1621.1953125 909.77734375 c 1 + 1621.1953125 630.578125 1621.1953125 351.37890625 1621.1953125 72.1796875 c 1 + 1570.8046875 72.1796875 1520.4140625 72.1796875 1470.0234375 72.1796875 c 1 + 1378.8125 226.57421875 1287.6015625 380.96875 1196.390625 535.36328125 c 1 +EndSplineSet +EndChar + +StartChar: uniE15B +Encoding: 57691 57691 97 +Width: 1792 +Flags: HWO +LayerCount: 2 +Fore +SplineSet +938.209960938 435.383789062 m 1 + 938.209960938 -25.1181640625 l 1 + 1234.27539062 247.456054688 l 1 + 1234.27539062 117.280273438 l 1 + 836.430664062 -248.998046875 l 1xd3ce + 836.430664062 211.504882812 l 1 + 540.365234375 -61.0693359375 l 1 + 540.365234375 69.1064453125 l 1 + 938.209960938 435.383789062 l 1 +315.962890625 621 m 1 + 315.962890625 844.359375 315.962890625 1067.71875 315.962890625 1291.078125 c 1 + 411.952148438 1291.078125 507.942382812 1291.078125 603.931640625 1291.078125 c 1 + 666.041015625 1291.078125 714.009765625 1272.875 747.916015625 1236.390625 c 128 + 781.900390625 1199.90625 798.853515625 1150.921875 798.853515625 1089.515625 c 256 + 798.853515625 1028.03125 781.900390625 979.046875 747.916015625 942.5625 c 128 + 714.009765625 906.15625 666.041015625 887.875 603.931640625 887.875 c 1 + 544.087890625 887.875 484.244140625 887.875 424.400390625 887.875 c 1 + 424.400390625 798.916992188 424.400390625 709.958984375 424.400390625 621 c 1 + 388.254882812 621 352.108398438 621 315.962890625 621 c 1 +424.400390625 982.953125 m 1 + 482.004882812 982.953125 539.608398438 982.953125 597.212890625 982.953125 c 1 + 624.712890625 982.953125 646.197265625 990.140625 661.509765625 1004.515625 c 128 + 676.900390625 1018.890625 684.556640625 1039.59375 684.556640625 1066.46875 c 1 + 684.556640625 1081.80761719 684.556640625 1097.14648438 684.556640625 1112.484375 c 1 + 684.556640625 1139.4375 676.900390625 1160.0625 661.509765625 1174.4375 c 128 + 646.197265625 1188.8125 624.712890625 1196.078125 597.212890625 1196.078125 c 1 + 539.608398438 1196.078125 482.004882812 1196.078125 424.400390625 1196.078125 c 1 + 424.400390625 1125.03710938 424.400390625 1053.99511719 424.400390625 982.953125 c 1 +1136.43164062 991.546875 m 1 + 1111.79589844 1038.91699219 1087.16113281 1086.28710938 1062.52539062 1133.65625 c 1 + 1061.56152344 1133.65625 1060.59863281 1133.65625 1059.63476562 1133.65625 c 1 + 1059.63476562 962.771484375 1059.63476562 791.885742188 1059.63476562 621 c 1 + 1025.07714844 621 990.520507812 621 955.962890625 621 c 1 + 955.962890625 844.359375 955.962890625 1067.71875 955.962890625 1291.078125 c 1 + 996.275390625 1291.078125 1036.58789062 1291.078125 1076.90039062 1291.078125 c 1 + 1149.86914062 1167.5625 1222.83789062 1044.046875 1295.80664062 920.53125 c 1 + 1320.44238281 873.162109375 1345.07714844 825.791992188 1369.71289062 778.421875 c 1 + 1370.67675781 778.421875 1371.63964844 778.421875 1372.60351562 778.421875 c 1 + 1372.60351562 949.307617188 1372.60351562 1120.19335938 1372.60351562 1291.078125 c 1 + 1407.16113281 1291.078125 1441.71777344 1291.078125 1476.27539062 1291.078125 c 1 + 1476.27539062 1067.71875 1476.27539062 844.359375 1476.27539062 621 c 1 + 1435.96289062 621 1395.65039062 621 1355.33789062 621 c 1 + 1282.36914062 744.515625 1209.40039062 868.03125 1136.43164062 991.546875 c 1 +EndSplineSet +EndChar EndChars EndSplineFont diff --git a/res/icons.ttf b/res/icons.ttf index 499cb84a1..07f6313f2 100644 Binary files a/res/icons.ttf and b/res/icons.ttf differ diff --git a/res/releaseReadme/stable-linux.txt b/res/releaseReadme/stable-linux.txt index be3b0078a..e93f352cc 100644 --- a/res/releaseReadme/stable-linux.txt +++ b/res/releaseReadme/stable-linux.txt @@ -16,9 +16,9 @@ if you find issues (e.g. bugs or annoyances), report them. links below. - Furnace on GitHub (project page and issue tracker): https://github.com/tildearrow/furnace - issues: https://github.com/tildearrow/furnace/issues - discussion: https://github.com/tildearrow/furnace/discussions -- Furnace on Revolt: https://rvlt.gg/GRPS6tmc - Furnace on Discord: https://discord.gg/EfrwT2wq7z -- online manual: https://tildearrow.org/furnace/doc/v0.6/ +- Furnace on Revolt: https://rvlt.gg/GRPS6tmc +- online manual: https://tildearrow.org/furnace/doc/v0.6.1/ # notes diff --git a/res/releaseReadme/stable-mac.txt b/res/releaseReadme/stable-mac.txt index 63bad6b0d..623a59d0f 100644 --- a/res/releaseReadme/stable-mac.txt +++ b/res/releaseReadme/stable-mac.txt @@ -26,9 +26,9 @@ if you find issues (e.g. bugs or annoyances), report them. links below. - Furnace on GitHub (project page and issue tracker): https://github.com/tildearrow/furnace - issues: https://github.com/tildearrow/furnace/issues - discussion: https://github.com/tildearrow/furnace/discussions -- Furnace on Revolt: https://rvlt.gg/GRPS6tmc - Furnace on Discord: https://discord.gg/EfrwT2wq7z -- online manual: https://tildearrow.org/furnace/doc/v0.6/ +- Furnace on Revolt: https://rvlt.gg/GRPS6tmc +- online manual: https://tildearrow.org/furnace/doc/v0.6.1/ # notes diff --git a/res/releaseReadme/stable-win.txt b/res/releaseReadme/stable-win.txt index c691defec..4c1174803 100644 --- a/res/releaseReadme/stable-win.txt +++ b/res/releaseReadme/stable-win.txt @@ -16,9 +16,9 @@ if you find issues (e.g. bugs or annoyances), report them. links below. - Furnace on GitHub (project page and issue tracker): https://github.com/tildearrow/furnace - issues: https://github.com/tildearrow/furnace/issues - discussion: https://github.com/tildearrow/furnace/discussions -- Furnace on Revolt: https://rvlt.gg/GRPS6tmc - Furnace on Discord: https://discord.gg/EfrwT2wq7z -- online manual: https://tildearrow.org/furnace/doc/v0.6/ +- Furnace on Revolt: https://rvlt.gg/GRPS6tmc +- online manual: https://tildearrow.org/furnace/doc/v0.6.1/ # notes diff --git a/scripts/release-linux-AppImage.sh b/scripts/release-linux-AppImage.sh index c535d2f6e..f5fbfeeb7 100755 --- a/scripts/release-linux-AppImage.sh +++ b/scripts/release-linux-AppImage.sh @@ -8,11 +8,11 @@ fi cd /tmp/furnace -if [ ! -e linuxbuild ]; then - mkdir linuxbuild || exit 1 +if [ ! -e aibuild ]; then + mkdir aibuild || exit 1 fi -cd linuxbuild +cd aibuild cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Werror" -DWITH_DEMOS=OFF -DWITH_INSTRUMENTS=OFF -DWITH_WAVETABLES=OFF .. || exit 1 make -j4 || exit 1 @@ -20,7 +20,7 @@ make -j4 || exit 1 cd .. mkdir -p release/linux/furnace.AppDir || exit 1 -cd linuxbuild +cd aibuild make DESTDIR=/tmp/furnace/release/linux/furnace.AppDir install || exit 1 diff --git a/src/divasm/divasm.cpp b/src/divasm/divasm.cpp new file mode 100644 index 000000000..3970669c8 --- /dev/null +++ b/src/divasm/divasm.cpp @@ -0,0 +1,22 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 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 "divasm.h" + + \ No newline at end of file diff --git a/src/divasm/divasm.h b/src/divasm/divasm.h index 2846c98f8..646ed089f 100644 --- a/src/divasm/divasm.h +++ b/src/divasm/divasm.h @@ -1,3 +1,27 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 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. + */ + +// this is a mini-assembler written for Furnace. +// it will be used in future ROM export (yes, that's right, ROM is baked at export time). + +#include "../engine/safeWriter.h" + struct DivASMResult { int line, err; DivASMResult(): @@ -5,26 +29,34 @@ struct DivASMResult { err(0) {} }; -struct DivASMFile { - String name; - SafeReader* data; -}; - enum DivASMTarget { DIV_ASM_TARGET_DUMMY=0, DIV_ASM_TARGET_6502, - DIV_ASM_TARGET_SPC700 + DIV_ASM_TARGET_SPC700, + DIV_ASM_TARGET_Z80 +}; + +struct DivLabel { + String name; + unsigned int location; + bool direct; +}; + +struct DivOper { + String operation; + int line; }; class DivASM { - std::vector files; SafeWriter* result; + std::vector labels; + std::vector ops; + public: DivASMResult getError(); - SafeWriter* assemble(String name); - void addFile(String name, SafeReader* data); + SafeWriter* assemble(SafeReader* data); - DivASM(DivASMTarget target); + DivASM(); ~DivASM(); }; diff --git a/src/divasm/standalone.cpp b/src/divasm/standalone.cpp new file mode 100644 index 000000000..17b0bf631 --- /dev/null +++ b/src/divasm/standalone.cpp @@ -0,0 +1,30 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 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 "divasm.h" + +int main(int argc, char** argv) { + if (argc<2) { + printf("usage: %s file\n",argv[0]); + return 1; + } + + + return 0; +} \ No newline at end of file diff --git a/src/engine/bsr.h b/src/engine/bsr.h index 437b02870..dcc865112 100644 --- a/src/engine/bsr.h +++ b/src/engine/bsr.h @@ -24,33 +24,58 @@ static inline int bsr(unsigned short v) { unsigned long idx; if (_BitScanReverse(&idx,(unsigned long)v)) { - return idx; + return idx+1; } else { return -1; } } +static inline int bsr32(unsigned int v) { + unsigned long idx; + if (_BitScanReverse(&idx,(unsigned long)v)) { + return idx+1; + } else { + return -1; + } +} + #elif defined( __GNUC__ ) -static inline int bsr(unsigned short v) -{ +static inline int bsr(unsigned short v) { if (v) { return 32 - __builtin_clz(v); + } else { + return -1; } - else{ +} + +static inline int bsr32(unsigned int v) { + if (v) { + return 32 - __builtin_clz(v); + } else { return -1; } } #else -static inline int bsr(unsigned short v) -{ +static inline int bsr(unsigned short v) { unsigned short mask = 0x8000; - for (int i = 15; i >= 0; --i) { + for (int i = 16; i >= 0; --i) { if (v&mask) - return (int)i; + return i; + mask>>=1; + } + + return -1; +} + +static inline int bsr32(unsigned int v) { + unsigned int mask = 0x80000000; + for (int i = 32; i >= 0; --i) { + if (v&mask) + return i; mask>>=1; } diff --git a/src/engine/chipUtils.h b/src/engine/chipUtils.h index d7cef0e28..84e2cf6b2 100644 --- a/src/engine/chipUtils.h +++ b/src/engine/chipUtils.h @@ -20,6 +20,7 @@ #ifndef _CHIP_UTILS_H #define _CHIP_UTILS_H +#include "defines.h" #include "macroInt.h" // custom clock limits @@ -29,7 +30,7 @@ // common shared channel struct template struct SharedChannel { int freq, baseFreq, baseNoteOverride, pitch, pitch2, arpOff; - int ins, note; + int ins, note, sampleNote, sampleNoteDelta; bool active, insChanged, freqChanged, fixedArp, keyOn, keyOff, portaPause, inPorta; T vol, outVol; DivMacroInt std; @@ -71,6 +72,8 @@ template struct SharedChannel { arpOff(0), ins(-1), note(0), + sampleNote(DIV_NOTE_NULL), + sampleNoteDelta(0), active(false), insChanged(true), freqChanged(false), diff --git a/src/engine/defines.h b/src/engine/defines.h index 8d9a501db..2b150ed45 100644 --- a/src/engine/defines.h +++ b/src/engine/defines.h @@ -36,5 +36,6 @@ // dispatch #define DIV_MAX_OUTPUTS 16 +#define DIV_NOTE_NULL 0x7fffffff #endif diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index c459cc3b0..fd9697375 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -25,11 +25,10 @@ #include "../pch.h" #include "config.h" #include "chipUtils.h" +#include "defines.h" #define ONE_SEMITONE 2200 -#define DIV_NOTE_NULL 0x7fffffff - #define addWrite(a,v) regWrites.push_back(DivRegWrite(a,v)); // HOW TO ADD A NEW COMMAND: @@ -250,6 +249,9 @@ enum DivDispatchCmds { DIV_CMD_MACRO_RESTART, // (which) + DIV_CMD_POWERNOISE_COUNTER_LOAD, // (which, val) + DIV_CMD_POWERNOISE_IO_WRITE, // (port, value) + DIV_CMD_MAX }; diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index ffcbe91eb..06049a69f 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -83,6 +83,7 @@ #include "platform/c140.h" #include "platform/pcmdac.h" #include "platform/esfm.h" +#include "platform/powernoise.h" #include "platform/dummy.h" #include "../ta-log.h" #include "song.h" @@ -648,6 +649,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_ESFM: dispatch=new DivPlatformESFM; break; + case DIV_SYSTEM_POWERNOISE: + dispatch=new DivPlatformPowerNoise; + break; case DIV_SYSTEM_DUMMY: dispatch=new DivPlatformDummy; break; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 971559719..eb02287ab 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -3412,6 +3412,10 @@ void DivEngine::setMidiCallback(std::function what) { midiCallback=what; } +void DivEngine::setMidiDebug(bool enable) { + midiDebug=enable; +} + bool DivEngine::sendMidiMessage(TAMidiMessage& msg) { if (output==NULL) { logW("output is NULL!"); @@ -3749,12 +3753,16 @@ bool DivEngine::preInit(bool noSafeMode) { initConfDir(); logD("config path: %s",configPath.c_str()); + // TODO: re-enable with a better approach + // see issue #1581 + /* if (!noSafeMode) { String safeModePath=configPath+DIR_SEPARATOR_STR+"safemode"; if (touchFile(safeModePath.c_str())==-EEXIST) { wantSafe=true; } } + */ String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log"; startLogFile(logPath.c_str()); @@ -3778,8 +3786,12 @@ bool DivEngine::preInit(bool noSafeMode) { } void DivEngine::everythingOK() { + // TODO: re-enable with a better approach + // see issue #1581 + /* String safeModePath=configPath+DIR_SEPARATOR_STR+"safemode"; deleteFile(safeModePath.c_str()); + */ } bool DivEngine::init() { diff --git a/src/engine/engine.h b/src/engine/engine.h index 1e13e3ccb..4ce839c0b 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -52,10 +52,10 @@ class DivWorkPool; #define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock(); #define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false; -#define DIV_UNSTABLE +//#define DIV_UNSTABLE -#define DIV_VERSION "dev191" -#define DIV_ENGINE_VERSION 191 +#define DIV_VERSION "0.6.1" +#define DIV_ENGINE_VERSION 192 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 @@ -174,7 +174,7 @@ struct DivChannelState { struct DivNoteEvent { signed char channel; - unsigned char ins; + short ins; signed char note, volume; bool on, nop, insChange, fromMIDI; DivNoteEvent(int c, int i, int n, int v, bool o, bool ic=false, bool fm=false): @@ -497,6 +497,7 @@ class DivEngine { short effectSlotMap[4096]; int midiBaseChan; bool midiPoly; + bool midiDebug; size_t midiAgeCounter; blip_buffer_t* samp_bb; @@ -1208,6 +1209,9 @@ class DivEngine { // send MIDI message bool sendMidiMessage(TAMidiMessage& msg); + // enable MIDI debug + void setMidiDebug(bool enable); + // perform secure/sync operation void synchronized(const std::function& what); @@ -1329,6 +1333,7 @@ class DivEngine { cmdStreamInt(NULL), midiBaseChan(0), midiPoly(true), + midiDebug(false), midiAgeCounter(0), samp_bb(NULL), samp_bbInLen(0), diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 71aaeb012..990af8da3 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -253,6 +253,10 @@ bool DivInstrumentESFM::Operator::operator==(const DivInstrumentESFM::Operator& ); } +bool DivInstrumentPowerNoise::operator==(const DivInstrumentPowerNoise& other) { + return _C(octave); +} + #undef _C #define FEATURE_BEGIN(x) \ @@ -782,6 +786,14 @@ void DivInstrument::writeFeatureEF(SafeWriter* w) { FEATURE_END; } +void DivInstrument::writeFeaturePN(SafeWriter* w) { + FEATURE_BEGIN("PN"); + + w->writeC(powernoise.octave); + + FEATURE_END; +} + void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bool insName) { size_t blockStartSeek=0; size_t blockEndSeek=0; @@ -826,6 +838,7 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo bool featureX1=false; bool featureNE=false; bool featureEF=false; + bool featurePN=false; bool checkForWL=false; @@ -1043,6 +1056,12 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo featureFM=true; featureEF=true; break; + case DIV_INS_POWERNOISE: + featurePN=true; + break; + case DIV_INS_POWERNOISE_SLOPE: + featurePN=true; + break; case DIV_INS_MAX: break; case DIV_INS_NULL: @@ -1093,6 +1112,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo if (esfm!=defaultIns.esfm) { featureEF=true; } + if (powernoise!=defaultIns.powernoise) { + featurePN=true; + } } // check ins name @@ -1238,6 +1260,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo if (featureEF) { writeFeatureEF(w); } + if (featurePN) { + writeFeaturePN(w); + } if (fui && (featureSL || featureWL)) { w->write("EN",2); @@ -2673,6 +2698,14 @@ void DivInstrument::readFeatureEF(SafeReader& reader, short version) { READ_FEAT_END; } +void DivInstrument::readFeaturePN(SafeReader& reader, short version) { + READ_FEAT_BEGIN; + + powernoise.octave=reader.readC(); + + READ_FEAT_END; +} + DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song) { unsigned char featCode[2]; bool volIsCutoff=false; @@ -2743,6 +2776,8 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b readFeatureNE(reader,version); } else if (memcmp(featCode,"EF",2)==0) { // ESFM readFeatureEF(reader,version); + } else if (memcmp(featCode,"PN",2)==0) { // PowerNoise + readFeaturePN(reader,version); } else { if (song==NULL && (memcmp(featCode,"SL",2)==0 || (memcmp(featCode,"WL",2)==0))) { // nothing diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 4304d5526..80bd8f251 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -86,6 +86,8 @@ enum DivInstrumentType: unsigned short { DIV_INS_C140=53, DIV_INS_C219=54, DIV_INS_ESFM=55, + DIV_INS_POWERNOISE=56, + DIV_INS_POWERNOISE_SLOPE=57, DIV_INS_MAX, DIV_INS_NULL }; @@ -822,6 +824,17 @@ struct DivInstrumentESFM { } }; +struct DivInstrumentPowerNoise { + unsigned char octave; + + bool operator==(const DivInstrumentPowerNoise& other); + bool operator!=(const DivInstrumentPowerNoise& other) { + return !(*this==other); + } + DivInstrumentPowerNoise(): + octave(0) {} +}; + struct DivInstrument { String name; DivInstrumentType type; @@ -839,6 +852,7 @@ struct DivInstrument { DivInstrumentES5506 es5506; DivInstrumentSNES snes; DivInstrumentESFM esfm; + DivInstrumentPowerNoise powernoise; /** * these are internal functions. @@ -864,6 +878,7 @@ struct DivInstrument { void writeFeatureX1(SafeWriter* w); void writeFeatureNE(SafeWriter* w); void writeFeatureEF(SafeWriter* w); + void writeFeaturePN(SafeWriter* w); void readFeatureNA(SafeReader& reader, short version); void readFeatureFM(SafeReader& reader, short version); @@ -885,6 +900,7 @@ struct DivInstrument { void readFeatureX1(SafeReader& reader, short version); void readFeatureNE(SafeReader& reader, short version); void readFeatureEF(SafeReader& reader, short version); + void readFeaturePN(SafeReader& reader, short version); DivDataErrors readInsDataOld(SafeReader& reader, short version); DivDataErrors readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song); diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 95c9d49d5..9555019bd 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -370,20 +370,10 @@ void DivPlatformAmiga::tick(bool sysTick) { chan[i].outVol=((chan[i].vol%65)*MIN(64,chan[i].std.vol.val))>>6; chan[i].writeVol=true; } - double off=1.0; - if (!chan[i].useWave && chan[i].sample>=0 && chan[i].samplesong.sampleLen) { - DivSample* s=parent->getSample(chan[i].sample); - if (s->centerRate<1) { - off=1.0; - } else { - off=8363.0/(double)s->centerRate; - } - } if (NEW_ARP_STRAT) { chan[i].handleArp(); } else if (chan[i].std.arp.had) { - // TODO: why the off mult? this may be a bug! - chan[i].baseFreq=round(off*NOTE_PERIODIC_NOROUND(parent->calcArp(chan[i].note,chan[i].std.arp.val))); + chan[i].baseFreq=round(NOTE_PERIODIC_NOROUND(parent->calcArp(chan[i].note,chan[i].std.arp.val))); chan[i].freqChanged=true; } if (chan[i].useWave && chan[i].std.wave.had) { @@ -577,10 +567,14 @@ int DivPlatformAmiga::dispatch(DivCommand c) { chan[c.chan].updateWave=true; } } + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; } else { if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } chan[c.chan].useWave=false; } @@ -657,9 +651,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { chan[c.chan].updateWave=true; break; case DIV_CMD_NOTE_PORTA: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); - chan[c.chan].sample=ins->amiga.getSample(c.value2); - int destFreq=round(NOTE_PERIODIC_NOROUND(c.value2)); + int destFreq=round(NOTE_PERIODIC_NOROUND(c.value2+chan[c.chan].sampleNoteDelta)); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -682,7 +674,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=round(NOTE_PERIODIC_NOROUND(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)))); + chan[c.chan].baseFreq=round(NOTE_PERIODIC_NOROUND(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index df75c4af8..6f50214cc 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -405,7 +405,12 @@ int DivPlatformAY8910::dispatch(DivCommand c) { if (!parent->song.disableSampleMacro && (ins->type==DIV_INS_AMIGA || ins->amiga.useSample)) { if (c.value!=DIV_NOTE_NULL) { chan[c.chan].dac.sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + chan[c.chan].dac.sample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); } if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) { chan[c.chan].dac.sample=-1; @@ -457,6 +462,8 @@ int DivPlatformAY8910::dispatch(DivCommand c) { break; } if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; @@ -526,7 +533,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_PERIODIC(c.value2); + int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -549,7 +556,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta); chan[c.chan].freqChanged=true; break; } diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 1a6072717..6fca42e06 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -406,7 +406,12 @@ int DivPlatformAY8930::dispatch(DivCommand c) { if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) { if (c.value!=DIV_NOTE_NULL) { chan[c.chan].dac.sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + chan[c.chan].dac.sample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); } if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) { chan[c.chan].dac.sample=-1; @@ -458,6 +463,8 @@ int DivPlatformAY8930::dispatch(DivCommand c) { break; } if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; @@ -520,7 +527,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_PERIODIC(c.value2); + int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -543,7 +550,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta); chan[c.chan].freqChanged=true; break; } diff --git a/src/engine/platform/c140.cpp b/src/engine/platform/c140.cpp index 14d203587..c83a93247 100644 --- a/src/engine/platform/c140.cpp +++ b/src/engine/platform/c140.cpp @@ -320,7 +320,9 @@ int DivPlatformC140::dispatch(DivCommand c) { chan[c.chan].macroPanMul=ins->type==DIV_INS_AMIGA?127:255; if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); @@ -393,7 +395,7 @@ int DivPlatformC140::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_FREQUENCY(c.value2); + int destFreq=NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -416,7 +418,7 @@ int DivPlatformC140::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/es5506.cpp b/src/engine/platform/es5506.cpp index 6c6e1732f..5a7c01c08 100644 --- a/src/engine/platform/es5506.cpp +++ b/src/engine/platform/es5506.cpp @@ -719,27 +719,51 @@ void DivPlatformES5506::tick(bool sysTick) { } } +// man this code +// part of the reason why it's so messy is because the chip is +// overly complex and because when this pull request was made, +// Furnace still was in an early state with no support for sample +// maps or whatever... +// one day I'll come back and clean this up int DivPlatformES5506::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_ES5506); bool sampleValid=false; - if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) || - ((!ins->amiga.useNoteMap) && (ins->amiga.initSample>=0 && ins->amiga.initSamplesong.sampleLen))) { + if (c.value!=DIV_NOTE_NULL) { int sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; if (sample>=0 && samplesong.sampleLen) { sampleValid=true; chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xfff; chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xfff; chan[c.chan].pcm.next=ins->amiga.useNoteMap?c.value:sample; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; chan[c.chan].pcm.note=c.value; chan[c.chan].filter=ins->es5506.filter; chan[c.chan].envelope=ins->es5506.envelope; + } else { + chan[c.chan].sampleNoteDelta=0; + } + } else { + int sample=ins->amiga.getSample(chan[c.chan].sampleNote); + if (sample>=0 && samplesong.sampleLen) { + sampleValid=true; + chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xfff; + chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xfff; + chan[c.chan].pcm.next=ins->amiga.useNoteMap?chan[c.chan].sampleNote:sample; + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); + chan[c.chan].pcm.note=c.value; + chan[c.chan].filter=ins->es5506.filter; + chan[c.chan].envelope=ins->es5506.envelope; + } else { + chan[c.chan].sampleNoteDelta=0; } } if (!sampleValid) { chan[c.chan].pcm.index=chan[c.chan].pcm.next=-1; + chan[c.chan].sampleNoteDelta=0; chan[c.chan].filter=DivInstrumentES5506::Filter(); chan[c.chan].envelope=DivInstrumentES5506::Envelope(); } @@ -962,7 +986,7 @@ int DivPlatformES5506::dispatch(DivCommand c) { break; case DIV_CMD_NOTE_PORTA: { int nextFreq=chan[c.chan].baseFreq; - const int destFreq=NOTE_ES5506(c.chan,c.value2); + const int destFreq=NOTE_ES5506(c.chan,c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>nextFreq) { nextFreq+=c.value; @@ -987,7 +1011,7 @@ int DivPlatformES5506::dispatch(DivCommand c) { } case DIV_CMD_LEGATO: { chan[c.chan].note=c.value; - chan[c.chan].nextNote=chan[c.chan].note+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)); + chan[c.chan].nextNote=chan[c.chan].note+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)); chan[c.chan].noteChanged.note=1; break; } diff --git a/src/engine/platform/ga20.cpp b/src/engine/platform/ga20.cpp index df8677584..2098f140b 100644 --- a/src/engine/platform/ga20.cpp +++ b/src/engine/platform/ga20.cpp @@ -207,7 +207,9 @@ int DivPlatformGA20::dispatch(DivCommand c) { chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255; if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); @@ -263,7 +265,7 @@ int DivPlatformGA20::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - const int destFreq=NOTE_PERIODIC(c.value2); + const int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -286,7 +288,7 @@ int DivPlatformGA20::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 57bb640e6..f59cf1975 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -704,8 +704,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } else if (chan[c.chan].furnaceDac) { chan[c.chan].dacMode=0; rWrite(0x2b,0<<7); + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; } else if (!chan[c.chan].dacMode) { rWrite(0x2b,0<<7); + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; } } if (c.chan>=5 && chan[c.chan].dacMode) { @@ -713,7 +717,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) { if (ins->type==DIV_INS_AMIGA) { // Furnace mode if (c.value!=DIV_NOTE_NULL) { chan[c.chan].dacSample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + chan[c.chan].dacSample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); } if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { chan[c.chan].dacSample=-1; @@ -752,6 +761,8 @@ int DivPlatformGenesis::dispatch(DivCommand c) { if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; } + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; chan[c.chan].dacSample=12*chan[c.chan].sampleBank+chan[c.chan].note%12; if (chan[c.chan].dacSample>=parent->song.sampleLen) { chan[c.chan].dacSample=-1; @@ -863,7 +874,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } case DIV_CMD_NOTE_PORTA: { if (parent->song.linearPitch==2) { - int destFreq=NOTE_FREQUENCY(c.value2); + int destFreq=NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -909,7 +920,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { break; } if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) { - int destFreq=parent->calcBaseFreq(1,1,c.value2,false); + int destFreq=parent->calcBaseFreq(1,1,c.value2+chan[c.chan].sampleNoteDelta,false); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value*16; @@ -963,7 +974,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { if (c.chan==csmChan) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); } else if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) { - chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value+chan[c.chan].sampleNoteDelta,false); } else { if (chan[c.chan].insChanged) { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); diff --git a/src/engine/platform/k007232.cpp b/src/engine/platform/k007232.cpp index eb2157e05..3f3ebc6d9 100644 --- a/src/engine/platform/k007232.cpp +++ b/src/engine/platform/k007232.cpp @@ -281,7 +281,9 @@ int DivPlatformK007232::dispatch(DivCommand c) { chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:15; if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); @@ -347,7 +349,7 @@ int DivPlatformK007232::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - const int destFreq=NOTE_PERIODIC(c.value2); + const int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -370,7 +372,7 @@ int DivPlatformK007232::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp index b084f3a23..68704c9ee 100644 --- a/src/engine/platform/k053260.cpp +++ b/src/engine/platform/k053260.cpp @@ -230,7 +230,9 @@ int DivPlatformK053260::dispatch(DivCommand c) { chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127; if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); @@ -291,7 +293,7 @@ int DivPlatformK053260::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_PERIODIC(c.value2); + int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -314,7 +316,7 @@ int DivPlatformK053260::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 68f3ac762..5d9100f8c 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -228,19 +228,28 @@ int DivPlatformLynx::dispatch(DivCommand c) { WRITE_CONTROL(c.chan,0x18); WRITE_BACKUP(c.chan,0); } else { + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; WRITE_FEEDBACK(c.chan,chan[c.chan].duty.feedback); WRITE_CONTROL(c.chan,(chan[c.chan].fd.clockDivider|0x18|chan[c.chan].duty.int_feedback7)); WRITE_BACKUP(c.chan,chan[c.chan].fd.backup); } } - if (c.value!=DIV_NOTE_NULL) { - if (chan[c.chan].pcm) { + if (chan[c.chan].pcm) { + if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; chan[c.chan].sampleBaseFreq=NOTE_FREQUENCY(c.value); - chan[c.chan].sampleAccum=0; - chan[c.chan].samplePos=0; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + chan[c.chan].sample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); } + chan[c.chan].sampleAccum=0; + chan[c.chan].samplePos=0; + } + if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; @@ -301,7 +310,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_PERIODIC(c.value2); + int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -327,7 +336,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - int whatAMess=c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)); + int whatAMess=c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)); chan[c.chan].baseFreq=NOTE_PERIODIC(whatAMess); if (chan[c.chan].pcm) { chan[c.chan].sampleBaseFreq=NOTE_FREQUENCY(whatAMess); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 899bf7d8a..49a19e8c8 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -178,7 +178,12 @@ int DivPlatformMMC5::dispatch(DivCommand c) { if (ins->type==DIV_INS_AMIGA) { if (c.value!=DIV_NOTE_NULL) { dacSample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + dacSample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); } if (dacSample<0 || dacSample>=parent->song.sampleLen) { dacSample=-1; @@ -189,8 +194,8 @@ int DivPlatformMMC5::dispatch(DivCommand c) { } dacPos=0; dacPeriod=0; - chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } @@ -270,7 +275,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=(c.chan==2)?(parent->calcBaseFreq(1,1,c.value2,false)):(NOTE_PERIODIC(c.value2)); + int destFreq=(c.chan==2)?(parent->calcBaseFreq(1,1,c.value2+chan[c.chan].sampleNoteDelta,false)):(NOTE_PERIODIC(c.value2)); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -304,7 +309,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { break; case DIV_CMD_LEGATO: if (c.chan==2) { - chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)),false); + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)),false); } else { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); } diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 6abdf1dc9..526944a3d 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -407,7 +407,14 @@ int DivPlatformNES::dispatch(DivCommand c) { if (c.value!=DIV_NOTE_NULL) { dacSample=ins->amiga.getSample(c.value); if (ins->type==DIV_INS_AMIGA) { + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + dacSample=ins->amiga.getSample(chan[c.chan].sampleNote); + if (ins->type==DIV_INS_AMIGA) { + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); } } if (dacSample<0 || dacSample>=parent->song.sampleLen) { @@ -535,7 +542,7 @@ int DivPlatformNES::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=(c.chan==4)?(parent->calcBaseFreq(1,1,c.value2,false)):(NOTE_PERIODIC(c.value2)); + int destFreq=(c.chan==4)?(parent->calcBaseFreq(1,1,c.value2+chan[c.chan].sampleNoteDelta,false)):(NOTE_PERIODIC(c.value2)); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -636,7 +643,7 @@ int DivPlatformNES::dispatch(DivCommand c) { case DIV_CMD_LEGATO: if (c.chan==3) break; if (c.chan==4) { - chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)),false); + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)),false); } else { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); } diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index fbad13218..d62161827 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -515,6 +515,18 @@ static const int cycleMapDrums[18]={ 0, 1, 2, 3, 4, 5, 3, 4, 5, }; +static const int cycleMap3[36]={ + 14, 12, 13, 14, 0, 2, 4, 0, 2, + 4, 1, 3, 5, 1, 3, 5, 15, 16, + 17, 15, 16, 17, 6, 8, 10, 6, 8, 10, 7, 9, 11, 7, 9, 11, 12, 13 +}; + +static const int cycleMap3Drums[36]={ + 14, 12, 13, 14, 0, 2, 4, 0, 2, + 4, 1, 3, 5, 1, 3, 5, 15, 19, + 17, 15, 16, 18, 6, 8, 10, 6, 8, 10, 7, 9, 11, 7, 9, 11, 12, 13 +}; + void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { int chOut[11]; thread_local ymfm::ymfm_output<2> aOut; @@ -662,10 +674,11 @@ void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { void DivPlatformOPL::acquire_nukedLLE3(short** buf, size_t len) { int chOut[20]; + int ch=0; for (size_t h=0; h0) ch|=1; + + if (ch>=12 || (ch&1) || !chan[ch&(~1)].fourOp) { + if (fm_lle3.op_value_debug&0x1000) { + chOut[ch]+=(fm_lle3.op_value_debug|0xfffff000)<<1; + } else { + chOut[ch]+=(fm_lle3.op_value_debug)<<1; + } + } curCycle++; - }*/ + } if (fm_lle3.o_sy && !lastSY) { dacVal>>=1; @@ -746,11 +773,11 @@ void DivPlatformOPL::acquire_nukedLLE3(short** buf, size_t len) { } for (int i=0; i<20; i++) { - if (i>=15 && properDrums) { + /*if (i>=15 && properDrums) { chOut[i]<<=1; } else { chOut[i]<<=2; - } + }*/ if (chOut[i]<-32768) chOut[i]=-32768; if (chOut[i]>32767) chOut[i]=32767; oscBuf[i]->data[oscBuf[i]->needle++]=chOut[i]; diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index d8be4dd21..60800b625 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -278,6 +278,8 @@ int DivPlatformPCE::dispatch(DivCommand c) { chan[c.chan].pcm=true; } else if (chan[c.chan].furnaceDac) { chan[c.chan].pcm=false; + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; } if (chan[c.chan].pcm) { if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) { @@ -285,7 +287,12 @@ int DivPlatformPCE::dispatch(DivCommand c) { if (skipRegisterWrites) break; if (c.value!=DIV_NOTE_NULL) { chan[c.chan].dacSample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + chan[c.chan].dacSample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); } if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { chan[c.chan].dacSample=-1; @@ -312,6 +319,8 @@ int DivPlatformPCE::dispatch(DivCommand c) { //chan[c.chan].keyOn=true; } else { chan[c.chan].furnaceDac=false; + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; if (skipRegisterWrites) break; if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; @@ -334,6 +343,8 @@ int DivPlatformPCE::dispatch(DivCommand c) { } break; } + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; @@ -413,7 +424,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { updateLFO=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_PERIODIC(c.value2); + int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -454,7 +465,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/pcmdac.cpp b/src/engine/platform/pcmdac.cpp index eebcd3860..17776acaa 100644 --- a/src/engine/platform/pcmdac.cpp +++ b/src/engine/platform/pcmdac.cpp @@ -314,6 +314,8 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[0].ins,DIV_INS_AMIGA); if (ins->amiga.useWave) { + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; chan[0].useWave=true; chan[0].audLen=ins->amiga.waveLen+1; if (chan[0].insChanged) { @@ -326,7 +328,12 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) { } else { if (c.value!=DIV_NOTE_NULL) { chan[0].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + chan[c.chan].sample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); } chan[0].useWave=false; } @@ -335,6 +342,8 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) { } if (chan[0].useWave || chan[0].sample<0 || chan[0].sample>=parent->song.sampleLen) { chan[0].sample=-1; + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; } if (chan[0].setPos) { chan[0].setPos=false; @@ -402,9 +411,7 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) { chan[0].ws.changeWave1(chan[0].wave); break; case DIV_CMD_NOTE_PORTA: { - DivInstrument* ins=parent->getIns(chan[0].ins,DIV_INS_AMIGA); - chan[0].sample=ins->amiga.getSample(c.value2); - int destFreq=round(NOTE_FREQUENCY(c.value2)); + int destFreq=round(NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta)); bool return2=false; if (destFreq>chan[0].baseFreq) { chan[0].baseFreq+=c.value; @@ -427,7 +434,7 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[0].baseFreq=round(NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[0].std.arp.val):(0)))); + chan[0].baseFreq=round(NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[0].std.arp.val):(0)))); chan[0].freqChanged=true; chan[0].note=c.value; break; diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp new file mode 100644 index 000000000..353d1ffe8 --- /dev/null +++ b/src/engine/platform/powernoise.cpp @@ -0,0 +1,541 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 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 "powernoise.h" +#include "../engine.h" +#include "../../ta-log.h" +#include "furIcons.h" +#include +#include "../bsr.h" + +#define rWrite(a,v) if (!skipRegisterWrites) {regPool[a]=(v); pwrnoise_write(&pn,(unsigned char)(a),(unsigned char)(v)); if (dumpWrites) {addWrite(a,v);}} +#define chWrite(c,a,v) rWrite((c<<3)|((a)+1),(v)) +#define noiseCtl(enable,am,tapB) (((enable)?0x80:0x00)|((am)?0x02:0x00)|((tapB)?0x01:0x00)) +#define slopeCtl(enable,rst,a,b) (((enable)?0x80:0x00)| \ + (rst?0x40:0x00)| \ + (a.clip?0x20:0x00)| \ + (b.clip?0x10:0x00)| \ + (a.reset?0x08:0x00)| \ + (b.reset?0x04:0x00)| \ + (a.dir?0x02:0x00)| \ + (b.dir?0x01:0x00)) +#define volPan(v,p) (((v*(p>>4)/15)<<4)|((v*(p&0xf)/15)&0xf)) +// TODO: optimize! +#define mapAmp(a) ((((a)*65535/63-32768)*(pn.flags&0x7)/7)>>1) + +const char* regCheatSheetPowerNoise[]={ + "ACTL", "00", + "N1CTL", "01", + "N1FL", "02", + "N1FH", "03", + "N1SRL", "04", + "N1SRH", "05", + "N1TAP", "06", + "N1V", "07", + "IOA", "08", + "N2CTL", "09", + "N2FL", "0A", + "N2FH", "0B", + "N2SRL", "0C", + "N2SRH", "0D", + "N2TAP", "0E", + "N2V", "0F", + "IOB", "10", + "N3CTL", "11", + "N3FL", "12", + "N3FH", "13", + "N3SRL", "14", + "N3SRH", "15", + "N3TAP", "16", + "N3V", "17", + "SLACC", "18", + "SLCTL", "19", + "SLFL", "1A", + "SLFH", "1B", + "SLPA", "1C", + "SLPB", "1D", + "SLPO", "1E", + "SLV", "1F", + NULL +}; + +const char** DivPlatformPowerNoise::getRegisterSheet() { + return regCheatSheetPowerNoise; +} + +void DivPlatformPowerNoise::acquire(short** buf, size_t len) { + short left, right; + + for (size_t h=0; hdata[oscBuf[0]->needle++]=mapAmp((pn.n1.out_latch&0xf)+(pn.n1.out_latch>>4)); + oscBuf[1]->data[oscBuf[1]->needle++]=mapAmp((pn.n2.out_latch&0xf)+(pn.n2.out_latch>>4)); + oscBuf[2]->data[oscBuf[2]->needle++]=mapAmp((pn.n3.out_latch&0xf)+(pn.n3.out_latch>>4)); + oscBuf[3]->data[oscBuf[3]->needle++]=mapAmp((pn.s.out_latch&0xf)+(pn.s.out_latch>>4)); + + buf[0][h]=left; + buf[1][h]=right; + } +} + +/* macros: + * EX1 - control (0-63) + * EX2 - portion A length (0-255) - slope only + * EX3 - portion B length (0-255) - slope only + * EX4 - tap A location (0-15) - noise only + * EX5 - tap B location (0-15) - noise only + * EX6 - portion A offset (0-15) - slope only + * EX7 - portion B offset (0-15) - slope only + * EX8 - load LFSR (0-65535) - noise only + */ + +void DivPlatformPowerNoise::tick(bool sysTick) { + for (int i=0; i<4; i++) { + int CHIP_DIVIDER=2; + if (i==3) CHIP_DIVIDER=128; + chan[i].std.next(); + + if (chan[i].std.ex1.had) { + int val=chan[i].std.ex1.val; + + if (chan[i].slope) { + chan[i].slopeA.clip=(val&0x20); + chan[i].slopeB.clip=(val&0x10); + chan[i].slopeA.reset=(val&0x08); + chan[i].slopeB.reset=(val&0x04); + chan[i].slopeA.dir=(val&0x02); + chan[i].slopeB.dir=(val&0x01); + chWrite(i,0x00,slopeCtl(chan[i].active,false,chan[i].slopeA,chan[i].slopeB)); + } else { + chan[i].am=(val&0x02); + chan[i].tapBEnable=(val&0x01); + chWrite(i,0x00,noiseCtl(chan[i].active, chan[i].am, chan[i].tapBEnable)); + } + } + if (chan[i].slope) { + if (chan[i].std.ex2.had) { + chan[i].slopeA.len=chan[i].std.ex2.val; + chWrite(i,0x03,chan[i].std.ex2.val); + } + if (chan[i].std.ex3.had) { + chan[i].slopeB.len=chan[i].std.ex3.val; + chWrite(i,0x04,chan[i].std.ex3.val); + } + if (chan[i].std.ex6.had) { + chan[i].slopeA.offset=chan[i].std.ex6.val; + } + if (chan[i].std.ex7.had) { + chan[i].slopeB.offset=chan[i].std.ex7.val; + } + if (chan[i].std.ex6.had || chan[i].std.ex7.had) { + chWrite(i,0x05,(chan[i].slopeA.offset<<4)|chan[i].slopeB.offset); + } + } else { + if (chan[i].std.ex4.had) { + chan[i].tapA=chan[i].std.ex4.val; + } + if (chan[i].std.ex5.had) { + chan[i].tapB=chan[i].std.ex5.val; + } + if (chan[i].std.ex4.had || chan[i].std.ex5.had) { + chWrite(i,0x05,(chan[i].tapA<<4)|chan[i].tapB); + } + if (chan[i].std.ex8.had) { + if (chan[i].initLFSR!=(chan[i].std.ex8.val&0xffff)) { + chan[i].initLFSR=chan[i].std.ex8.val&0xffff; + chWrite(i,0x03,chan[i].std.ex8.val&0xff); + chWrite(i,0x04,chan[i].std.ex8.val>>8); + } + } + } + + if (chan[i].std.vol.had) { + chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15); + if (chan[i].outVol<0) chan[i].outVol=0; + } + if (NEW_ARP_STRAT) { + chan[i].handleArp(); + } else if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val)); + } + chan[i].freqChanged=true; + } + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + } + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + } + + if (chan[i].std.vol.had || chan[i].std.panL.had || chan[i].std.panR.had) { + chWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol,chan[i].pan)); + } + if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-32768,32767); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) { + if (chan[i].slope) { + chWrite(i,0x00,slopeCtl(chan[i].active,true,chan[i].slopeA,chan[i].slopeB)); + chan[i].keyOn=true; + } else { + chWrite(i,0x03,chan[i].initLFSR&0xff); + chWrite(i,0x04,chan[i].initLFSR>>8); + } + } + + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); + chan[i].freq>>=chan[i].octaveOff; + + if (chan[i].freq<0) chan[i].freq=0; + if (chan[i].freq>0x7ffffff) chan[i].freq=0x7ffffff; + int bsr32Val=bsr32(chan[i].freq); + chan[i].octave=MAX(bsr32Val-12,0); + if (chan[i].octave>15) chan[i].octave=15; + chan[i].fNum=0x1000-(chan[i].freq>>chan[i].octave); + if (chan[i].fNum<0) chan[i].fNum=0; + if (chan[i].fNum>4095) chan[i].fNum=4095; + + chWrite(i,0x01,chan[i].fNum&0xff); + chWrite(i,0x02,(chan[i].fNum>>8)|(chan[i].octave<<4)); + + if (chan[i].keyOn) { + if (chan[i].slope) { + chWrite(i,0x00,slopeCtl(true,false,chan[i].slopeA,chan[i].slopeB)); + } else { + chWrite(i,0x00,noiseCtl(true,chan[i].am,chan[i].tapBEnable)); + } + } + if (chan[i].keyOff) { + if (chan[i].slope) { + chWrite(i,0x00,slopeCtl(false,false,chan[i].slopeA,chan[i].slopeB)); + } else { + chWrite(i,0x00,noiseCtl(false,chan[i].am,chan[i].tapBEnable)); + } + } + + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + + if (chan[i].slope) { + unsigned char counter=pn.s.accum; + regPool[0x18]=counter; + } else { + unsigned short lfsr; + if (i==0) { + lfsr=pn.n1.lfsr; + } else if (i==1) { + lfsr=pn.n2.lfsr; + } else { + lfsr=pn.n3.lfsr; + } + regPool[(i<<3)+0x4]=lfsr&0xff; + regPool[(i<<3)+0x5]=lfsr>>8; + } + } +} + +int DivPlatformPowerNoise::dispatch(DivCommand c) { + int CHIP_DIVIDER=2; + if (c.chan==3) CHIP_DIVIDER=128; + + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_POWERNOISE); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + if (chan[c.chan].insChanged) { + chan[c.chan].octaveOff=ins->powernoise.octave; + } + chan[c.chan].active=true; + chan[c.chan].macroInit(ins); + if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + } + chan[c.chan].keyOn=true; + chan[c.chan].insChanged=false; + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].macroInit(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + chan[c.chan].std.release(); + break; + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value || c.value2==1) { + chan[c.chan].ins=c.value; + chan[c.chan].insChanged=true; + } + break; + case DIV_CMD_VOLUME: + if (chan[c.chan].vol!=c.value) { + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.vol.has) { + chan[c.chan].outVol=c.value; + } + } + break; + case DIV_CMD_GET_VOLUME: + if (chan[c.chan].std.vol.has) { + return chan[c.chan].vol; + } + return chan[c.chan].outVol; + break; + case DIV_CMD_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_PERIODIC(c.value2); + + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value; + if (chan[c.chan].baseFreq>0x7ffffff) { + chan[c.chan].baseFreq=0x7ffffff; + } + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + if (chan[c.chan].baseFreq<0) { + chan[c.chan].baseFreq=0; + } + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_PANNING: + chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); + break; + case DIV_CMD_LEGATO: { + int whatAMess=c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)); + + chan[c.chan].baseFreq=NOTE_PERIODIC(whatAMess); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + break; + } + case DIV_CMD_PRE_PORTA: { + if (chan[c.chan].active && c.value2) { + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POWERNOISE)); + } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + } + chan[c.chan].inPorta=c.value; + break; + } + case DIV_CMD_GET_VOLMAX: + return 15; + break; + case DIV_CMD_POWERNOISE_COUNTER_LOAD: { + if (chan[c.chan].slope && c.value==0) { + rWrite(0x18,c.value2&0x7f); + } else if (!chan[c.chan].slope) { + chWrite(c.chan,0x03+c.value,c.value2); + } + break; + } + case DIV_CMD_POWERNOISE_IO_WRITE: + rWrite(0x08+(c.value<<3),c.value2); + break; + case DIV_CMD_MACRO_OFF: + chan[c.chan].std.mask(c.value,true); + break; + case DIV_CMD_MACRO_ON: + chan[c.chan].std.mask(c.value,false); + break; + case DIV_CMD_MACRO_RESTART: + chan[c.chan].std.restart(c.value); + break; + default: + break; + } + return 1; +} + +void DivPlatformPowerNoise::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + chWrite(ch,0x06,isMuted[ch]?0:volPan(chan[ch].outVol,chan[ch].pan)); +} + +void DivPlatformPowerNoise::forceIns() { + for (int i=0; i<4; i++) { + chan[i].insChanged=true; + chan[i].freqChanged=true; + if (i<3) { + chWrite(i,0x03,chan[i].initLFSR&0xff); + chWrite(i,0x04,chan[i].initLFSR>>8); + } + } +} + +void* DivPlatformPowerNoise::getChanState(int ch) { + return &chan[ch]; +} + +DivMacroInt* DivPlatformPowerNoise::getChanMacroInt(int ch) { + return &chan[ch].std; +} + +unsigned short DivPlatformPowerNoise::getPan(int ch) { + return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15); +} + +DivChannelModeHints DivPlatformPowerNoise::getModeHints(int ch) { + DivChannelModeHints ret; + ret.count=0; + return ret; +} + +bool DivPlatformPowerNoise::getDCOffRequired() { + return true; +} + +DivDispatchOscBuffer* DivPlatformPowerNoise::getOscBuffer(int ch) { + return oscBuf[ch]; +} + +unsigned char* DivPlatformPowerNoise::getRegisterPool() { + return regPool; +} + +int DivPlatformPowerNoise::getRegisterPoolSize() { + return 32; +} + +void DivPlatformPowerNoise::reset() { + memset(regPool,0,32); + for (int i=0; i<4; i++) { + chan[i]=DivPlatformPowerNoise::Channel(); + chan[i].std.setEngine(parent); + chan[i].slope=(i==3); + } + + pwrnoise_reset(&pn); + + // set master volume to full + rWrite(0,0x87); + // set per-channel panning + for (int i=0; i<4; i++) { + chWrite(i,0x06,volPan(chan[i].outVol,chan[i].pan)); + } + // set default params so we have sound + // noise + for (int i=0; i<3; i++) { + chWrite(i,0x03,chan[i].initLFSR&0xff); + chWrite(i,0x04,chan[i].initLFSR>>8); + chWrite(i,0x05,(chan[i].tapA<<4)|chan[i].tapB); + } + // slope + chWrite(3,0x03,chan[3].slopeA.len); + chWrite(3,0x04,chan[3].slopeB.len); + chWrite(3,0x05,(chan[3].slopeA.offset<<4)|chan[3].slopeB.offset); + + addWrite(0xffffffff,0); +} + +int DivPlatformPowerNoise::getOutputCount() { + return 2; +} + +bool DivPlatformPowerNoise::keyOffAffectsArp(int ch) { + return true; +} + +void DivPlatformPowerNoise::notifyWaveChange(int wave) { +} + +void DivPlatformPowerNoise::notifyInsDeletion(void* ins) { + for (int i=0; i<4; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformPowerNoise::setFlags(const DivConfig& flags) { + chipClock=16000000; + + CHECK_CUSTOM_CLOCK; + rate=chipClock/32; + + for (int i=0; i<4; i++) { + oscBuf[i]->rate=rate; + } +} + +void DivPlatformPowerNoise::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformPowerNoise::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformPowerNoise::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + + for (int i=0; i<4; i++) { + oscBuf[i]=new DivDispatchOscBuffer; + isMuted[i]=false; + } + setFlags(flags); + reset(); + return 4; +} + +void DivPlatformPowerNoise::quit() { + for (int i=0; i<4; i++) { + delete oscBuf[i]; + } +} + +DivPlatformPowerNoise::~DivPlatformPowerNoise() { +} diff --git a/src/engine/platform/powernoise.h b/src/engine/platform/powernoise.h new file mode 100644 index 000000000..72f1562e9 --- /dev/null +++ b/src/engine/platform/powernoise.h @@ -0,0 +1,108 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _POWERNOISE_H +#define _POWERNOISE_H + +#include "../dispatch.h" +#include "../../../extern/pwrnoise/pwrnoise.h" + +class DivPlatformPowerNoise: public DivDispatch { + struct SlopePortion { + unsigned char len, offset; + bool clip, reset, dir; + + SlopePortion(unsigned char l, unsigned char o, bool c, bool r, bool d): + len(l), + offset(o), + clip(c), + reset(r), + dir(d) {} + + SlopePortion(): + len(0), + offset(0), + clip(false), + reset(false), + dir(false) {} + }; + + struct Channel: public SharedChannel { + int fNum; + unsigned short initLFSR; + unsigned char octave, pan, tapA, tapB, octaveOff; + bool slope, am, tapBEnable, keyOn, keyOff; + SlopePortion slopeA, slopeB; + + Channel(): + SharedChannel(15), + fNum(0), + initLFSR(0x5555), + octave(0), + pan(255), + tapA(1), + tapB(2), + octaveOff(0), + slope(false), + am(false), + tapBEnable(false), + keyOn(false), + keyOff(false), + slopeA(255,1,false,false,false), + slopeB() {} + }; + + Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; + bool isMuted[4]; + unsigned char regPool[32]; + + power_noise_t pn; + + friend void putDispatchChip(void*,int); + friend void putDispatchChan(void*,int,int); + public: + void acquire(short** buf, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); + DivChannelModeHints getModeHints(int chan); + bool getDCOffRequired(); + DivDispatchOscBuffer* getOscBuffer(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + int getOutputCount(); + bool keyOffAffectsArp(int ch); + void setFlags(const DivConfig& flags); + void notifyWaveChange(int wave); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); + void quit(); + ~DivPlatformPowerNoise(); +}; + +#endif diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 4709fb668..152afb932 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -451,7 +451,9 @@ int DivPlatformQSound::dispatch(DivCommand c) { chan[c.chan].isNewQSound=(ins->type==DIV_INS_QSOUND); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value); @@ -543,7 +545,7 @@ int DivPlatformQSound::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=QS_NOTE_FREQUENCY(c.value2); + int destFreq=QS_NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -566,7 +568,7 @@ int DivPlatformQSound::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); + chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/rf5c68.cpp b/src/engine/platform/rf5c68.cpp index 06f9ab53e..148df4c28 100644 --- a/src/engine/platform/rf5c68.cpp +++ b/src/engine/platform/rf5c68.cpp @@ -184,7 +184,9 @@ int DivPlatformRF5C68::dispatch(DivCommand c) { chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255; if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); @@ -243,7 +245,7 @@ int DivPlatformRF5C68::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_FREQUENCY(c.value2); + int destFreq=NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -266,7 +268,7 @@ int DivPlatformRF5C68::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 5c5170186..19ec2bbb7 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -189,7 +189,9 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { chan[c.chan].isNewSegaPCM=(ins->type==DIV_INS_SEGAPCM); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].pcm.sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) { chan[c.chan].pcm.sample=-1; @@ -297,7 +299,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=(c.value2<<7); + int destFreq=((c.value2+chan[c.chan].sampleNoteDelta)<<7); int newFreq; int mul=(oldSlides || parent->song.linearPitch!=2)?8:1; bool return2=false; @@ -323,7 +325,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=(c.value<<7); + chan[c.chan].baseFreq=((c.value+chan[c.chan].sampleNoteDelta)<<7); chan[c.chan].freqChanged=true; break; } diff --git a/src/engine/platform/snes.cpp b/src/engine/platform/snes.cpp index 8cc205a16..cf1143c7f 100644 --- a/src/engine/platform/snes.cpp +++ b/src/engine/platform/snes.cpp @@ -333,6 +333,8 @@ int DivPlatformSNES::dispatch(DivCommand c) { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SNES); if (ins->amiga.useWave) { chan[c.chan].useWave=true; + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; chan[c.chan].wtLen=ins->amiga.waveLen+1; if (chan[c.chan].insChanged) { if (chan[c.chan].wave<0) { @@ -345,7 +347,9 @@ int DivPlatformSNES::dispatch(DivCommand c) { } else { if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } chan[c.chan].useWave=false; } @@ -424,7 +428,7 @@ int DivPlatformSNES::dispatch(DivCommand c) { chan[c.chan].ws.changeWave1(chan[c.chan].wave); break; case DIV_CMD_NOTE_PORTA: { - int destFreq=round(NOTE_FREQUENCY(c.value2)); + int destFreq=round(NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta)); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -447,7 +451,7 @@ int DivPlatformSNES::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)))); + chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.h b/src/engine/platform/sound/ymfm/ymfm_fm.h index 3ac36b50d..f77e89434 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.h +++ b/src/engine/platform/sound/ymfm/ymfm_fm.h @@ -459,7 +459,7 @@ protected: uint8_t m_clock_prescale; // prescale factor (2/3/6) uint8_t m_irq_mask; // mask of which bits signal IRQs uint8_t m_irq_state; // current IRQ state - uint8_t m_timer_running[2]; // current timer running state + uint8_t m_timer_running[4]; // current timer running state uint8_t m_total_clocks; // low 8 bits of the total number of clocks processed uint32_t m_active_channels; // mask of active channels (computed by prepare) uint32_t m_modified_channels; // mask of channels that have been modified diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.ipp b/src/engine/platform/sound/ymfm/ymfm_fm.ipp index 7a37ec8a3..a18eafdfb 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.ipp +++ b/src/engine/platform/sound/ymfm/ymfm_fm.ipp @@ -1198,7 +1198,7 @@ fm_engine_base::fm_engine_base(ymfm_interface &intf) : m_clock_prescale(RegisterType::DEFAULT_PRESCALE), m_irq_mask(STATUS_TIMERA | STATUS_TIMERB), m_irq_state(0), - m_timer_running{0,0}, + m_timer_running{0,0,0,0}, m_total_clocks(0), m_active_channels(ALL_CHANNELS), m_modified_channels(ALL_CHANNELS), diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 5ac95d3cd..8a80ee7c2 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -282,8 +282,13 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { if (chan[c.chan].pcm) { if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } + } else { + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value); @@ -450,7 +455,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { } break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_SU(c.chan,c.value2); + int destFreq=NOTE_SU(c.chan,c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch==2)?1:(1+(chan[c.chan].baseFreq>>9))); @@ -482,7 +487,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { chan[c.chan].keyOn=true; break; case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 484151595..51abfef28 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -257,6 +257,8 @@ int DivPlatformSwan::dispatch(DivCommand c) { pcm=true; } else if (furnaceDac) { pcm=false; + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; } if (pcm) { if (skipRegisterWrites) break; @@ -265,7 +267,12 @@ int DivPlatformSwan::dispatch(DivCommand c) { if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) { if (c.value!=DIV_NOTE_NULL) { dacSample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + dacSample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); } if (dacSample<0 || dacSample>=parent->song.sampleLen) { dacSample=-1; @@ -332,6 +339,8 @@ int DivPlatformSwan::dispatch(DivCommand c) { dacSample=-1; if (dumpWrites) postWrite(0xffff0002,0); pcm=false; + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; } chan[c.chan].active=false; chan[c.chan].keyOff=true; @@ -383,7 +392,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { } break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_PERIODIC(c.value2); + int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -412,7 +421,13 @@ int DivPlatformSwan::dispatch(DivCommand c) { } break; case DIV_CMD_SAMPLE_MODE: - if (c.chan==1) pcm=c.value; + if (c.chan==1) { + pcm=c.value; + if (!pcm) { + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; + } + } break; case DIV_CMD_SAMPLE_BANK: sampleBank=c.value; @@ -426,7 +441,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 7c48dc108..fcd13b8ed 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -299,10 +299,15 @@ int DivPlatformVERA::dispatch(DivCommand c) { if (c.chan<16) { rWriteLo(c.chan,2,chan[c.chan].vol); } else { + DivInstrument* ins=parent->getIns(chan[16].ins,DIV_INS_VERA); if (c.value!=DIV_NOTE_NULL) { - DivInstrument* ins=parent->getIns(chan[16].ins,DIV_INS_VERA); chan[16].pcm.sample=ins->amiga.getSample(c.value); + chan[16].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[16].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + chan[16].pcm.sample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); } if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) { chan[16].pcm.sample=-1; @@ -371,7 +376,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=calcNoteFreq(c.chan,c.value2); + int destFreq=calcNoteFreq(c.chan,c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -394,7 +399,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=calcNoteFreq(c.chan,c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].baseFreq=calcNoteFreq(c.chan,c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index f5f7371d6..c87a747db 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -236,13 +236,20 @@ int DivPlatformVRC6::dispatch(DivCommand c) { chan[c.chan].pcm=true; } else if (chan[c.chan].furnaceDac) { chan[c.chan].pcm=false; + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; } if (chan[c.chan].pcm) { if (skipRegisterWrites) break; if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) { if (c.value!=DIV_NOTE_NULL) { chan[c.chan].dacSample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + chan[c.chan].dacSample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); } if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { chan[c.chan].dacSample=-1; @@ -270,6 +277,8 @@ int DivPlatformVRC6::dispatch(DivCommand c) { //chan[c.chan].keyOn=true; chan[c.chan].furnaceDac=true; } else { + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; } @@ -314,6 +323,8 @@ int DivPlatformVRC6::dispatch(DivCommand c) { chan[c.chan].dacSample=-1; if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); chan[c.chan].pcm=false; + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; chan[c.chan].active=false; chan[c.chan].keyOff=true; chan[c.chan].macroInit(NULL); @@ -352,7 +363,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_PERIODIC(c.value2); + int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -382,6 +393,10 @@ int DivPlatformVRC6::dispatch(DivCommand c) { case DIV_CMD_SAMPLE_MODE: if (c.chan!=2) { // pulse chan[c.chan].pcm=c.value; + if (!chan[c.chan].pcm) { + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; + } } break; case DIV_CMD_SAMPLE_BANK: @@ -391,7 +406,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { } break; case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 9600cc09b..77d34b668 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -536,6 +536,8 @@ int DivPlatformX1_010::dispatch(DivCommand c) { chan[c.chan].furnacePCM=true; } else { chan[c.chan].furnacePCM=false; + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; } if (skipRegisterWrites) break; if (chan[c.chan].furnacePCM) { @@ -543,7 +545,9 @@ int DivPlatformX1_010::dispatch(DivCommand c) { chan[c.chan].macroInit(ins); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); @@ -608,6 +612,8 @@ int DivPlatformX1_010::dispatch(DivCommand c) { } } else if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; chan[c.chan].baseFreq=NoteX1_010(c.chan,chan[c.chan].note); chan[c.chan].fixedFreq=0; chan[c.chan].freqChanged=true; @@ -684,7 +690,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { } break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NoteX1_010(c.chan,c.value2); + int destFreq=NoteX1_010(c.chan,c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -732,7 +738,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { } case DIV_CMD_LEGATO: chan[c.chan].note=c.value; - chan[c.chan].baseFreq=NoteX1_010(c.chan,chan[c.chan].note+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].baseFreq=NoteX1_010(c.chan,chan[c.chan].note+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); chan[c.chan].freqChanged=true; break; case DIV_CMD_PRE_PORTA: diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 4d3986b2d..0a6168cd2 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -919,7 +919,9 @@ int DivPlatformYM2608::dispatch(DivCommand c) { } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); @@ -1109,7 +1111,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { } case DIV_CMD_NOTE_PORTA: { if (c.chan>5 || parent->song.linearPitch==2) { // PSG, ADPCM-B - int destFreq=NOTE_OPNB(c.chan,c.value2); + int destFreq=NOTE_OPNB(c.chan,c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -1150,7 +1152,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { chan[c.chan].insChanged=false; } } - chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); + chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value+chan[c.chan].sampleNoteDelta); chan[c.chan].freqChanged=true; break; } diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index f30ce9082..6753744e2 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -850,7 +850,9 @@ int DivPlatformYM2610::dispatch(DivCommand c) { } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); @@ -1081,7 +1083,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { } case DIV_CMD_NOTE_PORTA: { if (c.chan>=psgChanOffs || parent->song.linearPitch==2) { // PSG, ADPCM-B - int destFreq=NOTE_OPNB(c.chan,c.value2); + int destFreq=NOTE_OPNB(c.chan,c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -1122,7 +1124,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { chan[c.chan].insChanged=false; } } - chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); + chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value+chan[c.chan].sampleNoteDelta); chan[c.chan].freqChanged=true; break; } diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index e588bea94..e01077d56 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -917,7 +917,9 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); @@ -1148,7 +1150,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } case DIV_CMD_NOTE_PORTA: { if (c.chan>=psgChanOffs || parent->song.linearPitch==2) { // PSG, ADPCM-B - int destFreq=NOTE_OPNB(c.chan,c.value2); + int destFreq=NOTE_OPNB(c.chan,c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -1189,7 +1191,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { chan[c.chan].insChanged=false; } } - chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); + chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value+chan[c.chan].sampleNoteDelta); chan[c.chan].freqChanged=true; break; } diff --git a/src/engine/platform/ymz280b.cpp b/src/engine/platform/ymz280b.cpp index 395d1524e..878ed0f0d 100644 --- a/src/engine/platform/ymz280b.cpp +++ b/src/engine/platform/ymz280b.cpp @@ -213,7 +213,9 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) { chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255; if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); @@ -272,7 +274,7 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_FREQUENCY(c.value2); + int destFreq=NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; int multiplier=(parent->song.linearPitch==2)?1:256; if (destFreq>chan[c.chan].baseFreq) { @@ -296,7 +298,7 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; diff --git a/src/engine/platform/zxbeeperquadtone.cpp b/src/engine/platform/zxbeeperquadtone.cpp index bc3fc884d..66e1ee007 100644 --- a/src/engine/platform/zxbeeperquadtone.cpp +++ b/src/engine/platform/zxbeeperquadtone.cpp @@ -30,13 +30,14 @@ const char** DivPlatformZXBeeperQuadTone::getRegisterSheet() { } void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) { - bool o=false; for (size_t h=0; h=0 && curSamplesong.sampleLen && !isMuted[4]) { + bool sampleActive=false; + if (curSample>=0 && curSamplesong.sampleLen) { + sampleActive=!isMuted[4]; while (curSamplePeriod>=chan[4].freq) { DivSample* s=parent->getSample(curSample); if (s->samples>0) { - if (!isMuted[4]) o=(s->data8[curSamplePos++]>0); + chan[4].out=(s->data8[curSamplePos++]>0); if (curSamplePos>=s->samples) curSample=-1; // (theoretical) 32KiB limit if (curSamplePos>=32768*8) curSample=-1; @@ -46,46 +47,47 @@ void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) { curSamplePeriod-=chan[4].freq; } curSamplePeriod+=40; - if ((outputClock&3)==0) { + } + if (sampleActive) { + buf[0][h]=chan[4].out?32767:0; + if (outputClock==0) { oscBuf[0]->data[oscBuf[0]->needle++]=0; oscBuf[1]->data[oscBuf[1]->needle++]=0; oscBuf[2]->data[oscBuf[2]->needle++]=0; oscBuf[3]->data[oscBuf[3]->needle++]=0; - oscBuf[4]->data[oscBuf[4]->needle++]=o?32767:0; } + oscBuf[4]->data[oscBuf[4]->needle++]=buf[0][h]; } else { int ch=outputClock/2; int b=ch*4; + bool o=false; if ((outputClock&1)==0) { + short oscOut; chan[ch].sPosition+=(regPool[1+b]<<8)|regPool[0+b]; chan[ch].out=regPool[3+b]+((((chan[ch].sPosition>>8)&0xff)data[oscBuf[4]->needle++]=0; - } - o=chan[ch].out&0x10; - oscBuf[ch]->data[oscBuf[ch]->needle++]=o?32767:0; - chan[ch].out<<=1; - - // if muted, ztill run sample - if (curSample>=0 && curSamplesong.sampleLen && isMuted[4]) { - while (curSamplePeriod>=chan[4].freq) { - DivSample* s=parent->getSample(curSample); - if (s->samples>0) { - if (curSamplePos>=s->samples) curSample=-1; - // (theoretical) 32KiB limit - if (curSamplePos>=32768*8) curSample=-1; - } else { - curSample=-1; - } - curSamplePeriod-=chan[4].freq; + if (isMuted[ch] || ((chan[ch].out&0x18)==0)) { + oscOut=0; + } else if ((chan[ch].out&0x18)==0x18) { + oscOut=32767; + } else { + oscOut=16383; } - curSamplePeriod+=40; + oscBuf[ch]->data[oscBuf[ch]->needle++]=oscOut; } + if (!isMuted[ch]) o=chan[ch].out&0x10; + if (noHiss) { + deHisser[outputClock]=o; + buf[0][h]=-1; + for (int i=0; i<8; i++) { + buf[0][h]+=deHisser[i]?4096:0; + } + } else { + buf[0][h]=o?32767:0; + } + chan[ch].out<<=1; + oscBuf[4]->data[oscBuf[4]->needle++]=0; } outputClock=(outputClock+1)&7; - buf[0][h]=o?32767:0; } } @@ -157,6 +159,7 @@ void DivPlatformZXBeeperQuadTone::tick(bool sysTick) { chan[4].freq=parent->calcFreq(chan[4].baseFreq,chan[4].pitch,chan[4].fixedArp?chan[4].baseNoteOverride:chan[4].arpOff,chan[4].fixedArp,true,2,chan[4].pitch2,chipClock,off); if (chan[4].freq>258) chan[4].freq=258; if (chan[4].freq<3) chan[4].freq=3; + rWrite(16,(chan[4].freq-2)&255); chan[4].freq*=13; } if (chan[4].keyOn) chan[4].keyOn=false; @@ -187,13 +190,21 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); if (c.value!=DIV_NOTE_NULL) { curSample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; // TODO support offset commands curSamplePos=0; curSamplePeriod=0; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + curSample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); + // TODO support offset commands + curSamplePos=0; + curSamplePeriod=0; } chan[c.chan].active=true; chan[c.chan].keyOn=true; @@ -241,7 +252,7 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_FREQUENCY(c.value2); + int destFreq=NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -268,7 +279,7 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) { if (c.chan<4) rWrite(2+c.chan*4,chan[c.chan].duty^0xff); break; case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; @@ -332,11 +343,12 @@ unsigned char* DivPlatformZXBeeperQuadTone::getRegisterPool() { } int DivPlatformZXBeeperQuadTone::getRegisterPoolSize() { - return 16; + return 17; } void DivPlatformZXBeeperQuadTone::reset() { - memset(regPool,0,16); + memset(regPool,0,17); + memset(deHisser,0,8); for (int i=0; i<5; i++) { chan[i]=DivPlatformZXBeeperQuadTone::Channel(); chan[i].std.setEngine(parent); @@ -345,7 +357,6 @@ void DivPlatformZXBeeperQuadTone::reset() { cycles=0; curChan=0; sOffTimer=0; - ulaOut=0; curSample=-1; curSamplePos=0; curSamplePeriod=0; @@ -373,9 +384,11 @@ void DivPlatformZXBeeperQuadTone::setFlags(const DivConfig& flags) { } CHECK_CUSTOM_CLOCK; rate=chipClock/40; + noHiss=flags.getBool("noHiss",false); for (int i=0; i<4; i++) { - oscBuf[i]->rate=rate/4; + oscBuf[i]->rate=rate/8; } + oscBuf[4]->rate=rate; } void DivPlatformZXBeeperQuadTone::poke(unsigned int addr, unsigned short val) { diff --git a/src/engine/platform/zxbeeperquadtone.h b/src/engine/platform/zxbeeperquadtone.h index 4efd66c07..f4fdda058 100644 --- a/src/engine/platform/zxbeeperquadtone.h +++ b/src/engine/platform/zxbeeperquadtone.h @@ -36,12 +36,13 @@ class DivPlatformZXBeeperQuadTone: public DivDispatch { Channel chan[5]; DivDispatchOscBuffer* oscBuf[5]; bool isMuted[5]; - unsigned char ulaOut; + bool noHiss; + bool deHisser[8]; int cycles, curChan, sOffTimer, delay, curSample, curSamplePeriod; unsigned int curSamplePos; unsigned int outputClock; - unsigned char regPool[16]; + unsigned char regPool[17]; friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); public: diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 3ff0560bc..d7b8426e8 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -247,6 +247,9 @@ const char* cmdName[]={ "ESFM_MODIN", "ESFM_ENV_DELAY", + "POWERNOISE_COUNTER_LOAD", + "POWERNOISE_IO_WRITE", + "MACRO_RESTART", }; @@ -1838,13 +1841,20 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi // process MIDI events (TODO: everything) if (output) if (output->midiIn) while (!output->midiIn->queue.empty()) { TAMidiMessage& msg=output->midiIn->queue.front(); + if (midiDebug) { + if (msg.type==TA_MIDI_SYSEX) { + logD("MIDI debug: %.2X SysEx",msg.type); + } else { + logD("MIDI debug: %.2X %.2X %.2X",msg.type,msg.data[0],msg.data[1]); + } + } int ins=-1; if ((ins=midiCallback(msg))!=-2) { int chan=msg.type&15; switch (msg.type&0xf0) { case TA_MIDI_NOTE_OFF: { - if (chan<0 || chan>=chans) break; if (midiIsDirect) { + if (chan<0 || chan>=chans) break; pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true)); } else { autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); @@ -1857,15 +1867,16 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi break; } case TA_MIDI_NOTE_ON: { - if (chan<0 || chan>=chans) break; if (msg.data[1]==0) { if (midiIsDirect) { + if (chan<0 || chan>=chans) break; pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true)); } else { autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); } } else { if (midiIsDirect) { + if (chan<0 || chan>=chans) break; pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true,false,true)); } else { autoNoteOn(msg.type&15,ins,msg.data[0]-12,msg.data[1]); @@ -1880,6 +1891,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi break; } } + } else if (midiDebug) { + logD("callback wants ignore"); } //logD("%.2x",msg.type); output->midiIn->queue.pop(); diff --git a/src/engine/song.h b/src/engine/song.h index 7f29ddb69..a4b60238a 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -132,7 +132,8 @@ enum DivSystem { DIV_SYSTEM_TED, DIV_SYSTEM_C140, DIV_SYSTEM_C219, - DIV_SYSTEM_ESFM + DIV_SYSTEM_ESFM, + DIV_SYSTEM_POWERNOISE, }; enum DivEffectType: unsigned short { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 0da85e855..65b4cf1f0 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -536,7 +536,7 @@ void DivEngine::registerSystems() { EffectHandlerMap fmOPNAPostEffectHandlerMap(fmOPNPostEffectHandlerMap); fmOPNAPostEffectHandlerMap.insert({ - {0x31, {DIV_CMD_ADPCMA_GLOBAL_VOLUME, "1Fxx: Set ADPCM-A global volume (0 to 3F)"}}, + {0x1f, {DIV_CMD_ADPCMA_GLOBAL_VOLUME, "1Fxx: Set ADPCM-A global volume (0 to 3F)"}}, }); EffectHandlerMap fmOPLLPostEffectHandlerMap={ @@ -1984,6 +1984,23 @@ void DivEngine::registerSystems() { }, fmESFMPostEffectHandlerMap ); + + sysDefs[DIV_SYSTEM_POWERNOISE]=new DivSysDef( + "PowerNoise", NULL, 0xd4, 0, 4, false, false, 0, false, 0, 0, 0, + "a fantasy sound chip designed by jvsTSX and The Beesh-Spweesh!\nused in the Hexheld fantasy console.", + {"Noise 1", "Noise 2", "Noise 3", "Slope"}, + {"N1", "N2", "N3", "SL"}, + {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_WAVE}, + {DIV_INS_POWERNOISE, DIV_INS_POWERNOISE, DIV_INS_POWERNOISE, DIV_INS_POWERNOISE_SLOPE}, + {}, + { + {0x20, {DIV_CMD_POWERNOISE_COUNTER_LOAD, "20xx: Load low byte of noise channel LFSR (00 to FF) or slope channel accumulator (00 to 7F)", constVal<0>, effectVal}}, + {0x21, {DIV_CMD_POWERNOISE_COUNTER_LOAD, "21xx: Load high byte of noise channel LFSR (00 to FF)", constVal<1>, effectVal}}, + {0x22, {DIV_CMD_POWERNOISE_IO_WRITE, "22xx: Write to I/O port A", constVal<0>, effectVal}}, + {0x23, {DIV_CMD_POWERNOISE_IO_WRITE, "23xx: Write to I/O port B", constVal<1>, effectVal}}, + }, + {} + ); sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0, 0, 0, diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 8c014121f..4eb815c76 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -44,6 +44,7 @@ const char* aboutLine[]={ "LTVA1", "MooingLemur", "OPNA2608", + "scratchminer", "superctr", "System64", "", @@ -67,14 +68,18 @@ const char* aboutLine[]={ "-- demo songs --", "0x5066", "Abstract 64", + "Aburtos", "ActualNK358", "airconmanws", "akumanatt", "AmigaX", "AquaDoesStuff", "AURORA*FIELDS", + "Background2982", "battybeats", + "bbqzzd", "Bernie", + "BlastBrothers", "Blaze Weednix", "BlueElectric05", "breakthetargets", @@ -106,6 +111,7 @@ const char* aboutLine[]={ "JayBOB18", "Jimmy-DS", "Kagamiin~", + "Kaoru", "kleeder", "Korbo", "jaezu", @@ -122,24 +128,33 @@ const char* aboutLine[]={ "masicbemester", "MelonadeM", "Miker", + "Molkirill", "NeoWar", + "Nerreave", "niffuM", "", "Notakin", + "nwcr", "NyaongI", "Pale Moon", "PeyPey", "PichuMario", + "Poltvick", + "PotaJoe", "potatoTeto", "psxdominator", "Raijin", "railzen7", + "Rei8bit", "RepellantMold", "RetroCarrot", "RevvoBolt", + "Rockyfan75000", "scooblee", "sillygoose", + "smaybius", "SnugglyBun", + "Spinning Square Waves", "SuperJet Spade", "Supper_E1", "SwapXFO", @@ -155,6 +170,8 @@ const char* aboutLine[]={ "The Beesh-Spweesh!", "TheRealHedgehogSonic", "tildearrow", + "tom_atom", + "traumatized", "Uhrwerk Klockwerx", "Ultraprogramer", "UserSniper", @@ -162,6 +179,7 @@ const char* aboutLine[]={ "Xan", "YaIiya", "Yuzu4K", + "Zabir", "Zaxolotl", "ZoomTen (Zumi)", "", @@ -231,6 +249,7 @@ const char* aboutLine[]={ "SM8521 emulator (modified version) by cam900", "D65010G031 emulator (modified version) by cam900", "Namco C140/C219 emulator (modified version) by cam900", + "PowerNoise emulator by scratchminer", "", "greetings to:", "NEOART Costa Rica", diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 6ae3fb8b7..9fb9f4042 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -254,6 +254,9 @@ void FurnaceGUI::sampleListItem(int i, int dir, int asset) { if (ImGui::MenuItem("make instrument")) { doAction(GUI_ACTION_SAMPLE_MAKE_INS); } + if (ImGui::MenuItem("make me a drum kit")) { + doAction(GUI_ACTION_SAMPLE_LIST_MAKE_MAP); + } if (ImGui::MenuItem("duplicate")) { doAction(GUI_ACTION_SAMPLE_LIST_DUPLICATE); } diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index ce56f470b..5dca05edb 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -432,6 +432,13 @@ void FurnaceGUI::drawDebug() { ImGui::TreePop(); } + if (ImGui::TreeNode("MIDI Debug")) { + if (ImGui::Button("Enable Debug (go to log viewer)")) { + e->setMidiDebug(true); + nextWindow=GUI_WINDOW_LOG; + } + ImGui::TreePop(); + } if (ImGui::TreeNode("Visualizer Debug")) { if (ImGui::BeginTable("visX",3,ImGuiTableFlags_Borders)) { ImGui::TableNextRow(ImGuiTableRowFlags_Headers); diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 0d43a2eea..4f350c93d 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -725,6 +725,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_INS_LIST_DIR_VIEW: insListDir=!insListDir; break; + case GUI_ACTION_WAVE_LIST_ADD: { waveSizeList.clear(); @@ -945,6 +946,48 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_LIST_DIR_VIEW: sampleListDir=!sampleListDir; break; + case GUI_ACTION_SAMPLE_LIST_MAKE_MAP: { + // determine instrument type + std::vector tempTypeList=e->getPossibleInsTypes(); + makeInsTypeList.clear(); + + for (DivInstrumentType& i: tempTypeList) { + if (i==DIV_INS_PCE || + i==DIV_INS_MSM6258 || + i==DIV_INS_MSM6295 || + i==DIV_INS_ADPCMA || + i==DIV_INS_ADPCMB || + i==DIV_INS_SEGAPCM || + i==DIV_INS_QSOUND || + i==DIV_INS_YMZ280B || + i==DIV_INS_RF5C68 || + i==DIV_INS_MULTIPCM || + i==DIV_INS_MIKEY || + i==DIV_INS_X1_010 || + i==DIV_INS_SWAN || + i==DIV_INS_AY || + i==DIV_INS_AY8930 || + i==DIV_INS_VRC6 || + i==DIV_INS_SU || + i==DIV_INS_SNES || + i==DIV_INS_ES5506 || + i==DIV_INS_K007232 || + i==DIV_INS_GA20 || + i==DIV_INS_K053260 || + i==DIV_INS_C140 || + i==DIV_INS_C219) { + makeInsTypeList.push_back(i); + } + } + + if (makeInsTypeList.empty()) { + makeInsTypeList.push_back(DIV_INS_AMIGA); + } + + displayInsTypeList=true; + displayInsTypeListMakeInsSample=-2; + break; + } case GUI_ACTION_SAMPLE_SELECT: if (curSample<0 || curSample>=(int)e->song.sample.size()) break; @@ -1457,7 +1500,8 @@ void FurnaceGUI::doAction(int what) { i==DIV_INS_K007232 || i==DIV_INS_GA20 || i==DIV_INS_K053260 || - i==DIV_INS_C140) { + i==DIV_INS_C140 || + i==DIV_INS_C219) { makeInsTypeList.push_back(i); } } diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index fd2715bc8..549cffaf0 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -52,7 +52,29 @@ const char* FurnaceGUI::noteNameNormal(short note, short octave) { return noteNames[seek]; } -void FurnaceGUI::prepareUndo(ActionType action) { +void FurnaceGUI::prepareUndo(ActionType action, UndoRegion region) { + if (region.begin.ord==-1) { + region.begin.ord=curOrder; + region.end.ord=curOrder; + region.begin.x=0; + region.end.x=e->getTotalChannelCount()-1; + region.begin.y=0; + region.end.y=e->curSubSong->patLen-1; + } else { + if (region.begin.ord<0) region.begin.ord=0; + if (region.begin.ord>e->curSubSong->ordersLen) region.begin.ord=e->curSubSong->ordersLen; + if (region.end.ord<0) region.end.ord=0; + if (region.end.ord>e->curSubSong->ordersLen) region.end.ord=e->curSubSong->ordersLen; + if (region.begin.x<0) region.begin.x=0; + if (region.begin.x>=e->getTotalChannelCount()) region.begin.x=e->getTotalChannelCount()-1; + if (region.end.x<0) region.end.x=0; + if (region.end.x>=e->getTotalChannelCount()) region.end.x=e->getTotalChannelCount()-1; + if (region.begin.y<0) region.begin.y=0; + if (region.begin.y>=e->curSubSong->patLen) region.begin.y=e->curSubSong->patLen-1; + if (region.end.y<0) region.end.y=0; + if (region.end.y>=e->curSubSong->patLen) region.end.y=e->curSubSong->patLen-1; + } + switch (action) { case GUI_UNDO_CHANGE_ORDER: memcpy(&oldOrders,e->curOrders,sizeof(DivOrders)); @@ -74,8 +96,21 @@ void FurnaceGUI::prepareUndo(ActionType action) { case GUI_UNDO_PATTERN_COLLAPSE: case GUI_UNDO_PATTERN_EXPAND: case GUI_UNDO_PATTERN_DRAG: - for (int i=0; igetTotalChannelCount(); i++) { - e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false)->copyOn(oldPat[i]); + for (int h=region.begin.ord; h<=region.end.ord; h++) { + for (int i=region.begin.x; i<=region.end.x; i++) { + unsigned short id=h|(i<<8); + DivPattern* p=NULL; + + auto it=oldPatMap.find(id); + if (it==oldPatMap.end()) { + p=oldPatMap[id]=new DivPattern; + //logV("oldPatMap: allocating for %.4x",id); + } else { + p=it->second; + } + + e->curPat[i].getPattern(e->curOrders->ord[i][h],false)->copyOn(p); + } } break; case GUI_UNDO_PATTERN_COLLAPSE_SONG: @@ -86,7 +121,7 @@ void FurnaceGUI::prepareUndo(ActionType action) { } } -void FurnaceGUI::makeUndo(ActionType action) { +void FurnaceGUI::makeUndo(ActionType action, UndoRegion region) { bool doPush=false; bool shallWalk=false; UndoStep s; @@ -99,6 +134,29 @@ void FurnaceGUI::makeUndo(ActionType action) { s.newOrdersLen=e->curSubSong->ordersLen; s.nibble=curNibble; size_t subSong=e->getCurrentSubSong(); + + if (region.begin.ord==-1) { + region.begin.ord=curOrder; + region.end.ord=curOrder; + region.begin.x=0; + region.end.x=e->getTotalChannelCount()-1; + region.begin.y=0; + region.end.y=e->curSubSong->patLen-1; + } else { + if (region.begin.ord<0) region.begin.ord=0; + if (region.begin.ord>e->curSubSong->ordersLen) region.begin.ord=e->curSubSong->ordersLen; + if (region.end.ord<0) region.end.ord=0; + if (region.end.ord>e->curSubSong->ordersLen) region.end.ord=e->curSubSong->ordersLen; + if (region.begin.x<0) region.begin.x=0; + if (region.begin.x>=e->getTotalChannelCount()) region.begin.x=e->getTotalChannelCount()-1; + if (region.end.x<0) region.end.x=0; + if (region.end.x>=e->getTotalChannelCount()) region.end.x=e->getTotalChannelCount()-1; + if (region.begin.y<0) region.begin.y=0; + if (region.begin.y>=e->curSubSong->patLen) region.begin.y=e->curSubSong->patLen-1; + if (region.end.y<0) region.end.y=0; + if (region.end.y>=e->curSubSong->patLen) region.end.y=e->curSubSong->patLen-1; + } + switch (action) { case GUI_UNDO_CHANGE_ORDER: for (int i=0; igetTotalChannelCount(); i++) { - DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false); - for (int j=0; jcurSubSong->patLen; j++) { - for (int k=0; kdata[j][k]!=oldPat[i]->data[j][k]) { - s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][curOrder],j,k,oldPat[i]->data[j][k],p->data[j][k])); + for (int h=region.begin.ord; h<=region.end.ord; h++) { + for (int i=region.begin.x; i<=region.end.x; i++) { + DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][h],false); + DivPattern* op=NULL; + unsigned short id=h|(i<<8); - if (k>=4) { - if (oldPat[i]->data[j][k&(~1)]==0x0b || - p->data[j][k&(~1)]==0x0b || - oldPat[i]->data[j][k&(~1)]==0x0d || - p->data[j][k&(~1)]==0x0d || - oldPat[i]->data[j][k&(~1)]==0xff || - p->data[j][k&(~1)]==0xff) { - shallWalk=true; + auto it=oldPatMap.find(id); + if (it==oldPatMap.end()) { + logW("no data in oldPatMap for channel %d!",i); + continue; + } else { + op=it->second; + } + + int jBegin=0; + int jEnd=e->curSubSong->patLen-1; + + if (h==region.begin.ord) jBegin=region.begin.y; + if (h==region.end.ord) jEnd=region.end.y; + + for (int j=jBegin; j<=jEnd; j++) { + for (int k=0; kdata[j][k]!=op->data[j][k]) { + s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][h],j,k,op->data[j][k],p->data[j][k])); + + if (k>=4) { + if (op->data[j][k&(~1)]==0x0b || + p->data[j][k&(~1)]==0x0b || + op->data[j][k&(~1)]==0x0d || + p->data[j][k&(~1)]==0x0d || + op->data[j][k&(~1)]==0xff || + p->data[j][k&(~1)]==0xff) { + shallWalk=true; + } } - } + } } } } @@ -172,6 +249,12 @@ void FurnaceGUI::makeUndo(ActionType action) { if (shallWalk) { e->walkSong(loopOrder,loopRow,loopEnd); } + + // garbage collection + for (std::pair i: oldPatMap) { + delete i.second; + } + oldPatMap.clear(); } void FurnaceGUI::doSelectAll() { @@ -460,7 +543,7 @@ String FurnaceGUI::doCopy(bool cut, bool writeClipboard, const SelectionPoint& s return clipb; } -void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int startOff, bool invalidData) +void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int startOff, bool invalidData, UndoRegion ur) { if (sscanf(data[1].c_str(),"%d",&startOff)!=1) return; if (startOff<0) return; @@ -602,7 +685,7 @@ void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, Str updateScroll(cursor.y); } - makeUndo(GUI_UNDO_PATTERN_PASTE); + makeUndo(GUI_UNDO_PATTERN_PASTE,ur); } } @@ -819,7 +902,7 @@ unsigned int convertEffectMPT_MPTM(unsigned char symbol, unsigned int val) { } // TODO: fix code style -void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int mptFormat) +void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int mptFormat, UndoRegion ur) { DETERMINE_LAST; @@ -1144,14 +1227,13 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String updateScroll(cursor.y); } - makeUndo(GUI_UNDO_PATTERN_PASTE); + makeUndo(GUI_UNDO_PATTERN_PASTE,ur); } } void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String clipb) { if (readClipboard) { finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_PASTE); char* clipText=SDL_GetClipboardText(); if (clipText!=NULL) { if (clipText[0]) { @@ -1198,10 +1280,28 @@ void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String cli if (!foundString) return; + UndoRegion ur; + if (mode==GUI_PASTE_MODE_OVERFLOW) { + int rows=cursor.y; + int firstPattern=curOrder; + int lastPattern=curOrder; + rows+=data.size(); + while (rows>=e->curSubSong->patLen) { + lastPattern++; + rows-=e->curSubSong->patLen; + } + + ur=UndoRegion(firstPattern,0,0,lastPattern,e->getTotalChannelCount()-1,e->curSubSong->patLen-1); + } + + if (readClipboard) { + prepareUndo(GUI_UNDO_PATTERN_PASTE,ur); + } + if (isFurnace) { - doPasteFurnace(mode,arg,readClipboard,clipb,data,startOff,invalidData); + doPasteFurnace(mode,arg,readClipboard,clipb,data,startOff,invalidData,ur); } else if (isModPlug) { - doPasteMPT(mode,arg,readClipboard,clipb,data,mptFormat); + doPasteMPT(mode,arg,readClipboard,clipb,data,mptFormat,ur); } } @@ -1565,6 +1665,10 @@ void FurnaceGUI::doExpand(int multiplier, const SelectionPoint& sStart, const Se void FurnaceGUI::doCollapseSong(int divider) { if (divider<2) return; + if (e->curSubSong->patLennewFrame()) { fontsFailed=true; } @@ -5833,6 +5893,26 @@ bool FurnaceGUI::loop() { if (ImGui::BeginPopup("InsTypeList",ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) { char temp[1024]; + if (displayInsTypeListMakeInsSample==-2) { + ImGui::Text("Drum kit mode:"); + if (ImGui::RadioButton("Normal",!makeDrumkitMode)) { + makeDrumkitMode=false; + } + if (ImGui::RadioButton("12 samples per octave",makeDrumkitMode)) { + makeDrumkitMode=true; + } + + if (!makeDrumkitMode) { + ImGui::Text("Starting octave"); + ImGui::SameLine(); + if (ImGui::InputInt("##DKOctave",&makeDrumkitOctave,1,3)) { + if (makeDrumkitOctave<0) makeDrumkitOctave=0; + if (makeDrumkitOctave>9) makeDrumkitOctave=9; + } + } + + ImGui::Separator(); + } for (DivInstrumentType& i: makeInsTypeList) { strncpy(temp,insTypes[i][0],1023); if (ImGui::MenuItem(temp)) { @@ -5841,7 +5921,36 @@ bool FurnaceGUI::loop() { if (curIns==-1) { showError("too many instruments!"); } else { - if (displayInsTypeListMakeInsSample>=0 && displayInsTypeListMakeInsSample<(int)e->song.sample.size()) { + if (displayInsTypeListMakeInsSample==-2) { + e->song.ins[curIns]->type=i; + e->song.ins[curIns]->name="Drum Kit"; + e->song.ins[curIns]->amiga.useNoteMap=true; + if (i!=DIV_INS_AMIGA) e->song.ins[curIns]->amiga.useSample=true; + + if (makeDrumkitMode) { + for (int j=0; j<120; j++) { + e->song.ins[curIns]->amiga.noteMap[j].freq=48; + e->song.ins[curIns]->amiga.noteMap[j].dpcmFreq=15; + e->song.ins[curIns]->amiga.noteMap[j].map=j%12; + if ((j%12)>=e->song.sampleLen) continue; + } + } else { + int index=-makeDrumkitOctave*12; + for (int j=0; j<120; j++) { + e->song.ins[curIns]->amiga.noteMap[j].freq=48; + e->song.ins[curIns]->amiga.noteMap[j].dpcmFreq=15; + if (index<0 || index>=e->song.sampleLen) { + index++; + continue; + } + e->song.ins[curIns]->amiga.noteMap[j].map=index++; + } + } + + nextWindow=GUI_WINDOW_INS_EDIT; + wavePreviewInit=true; + updateFMPreview=true; + } else if (displayInsTypeListMakeInsSample>=0 && displayInsTypeListMakeInsSample<(int)e->song.sample.size()) { e->song.ins[curIns]->type=i; e->song.ins[curIns]->name=e->song.sample[displayInsTypeListMakeInsSample]->name; e->song.ins[curIns]->amiga.initSample=displayInsTypeListMakeInsSample; @@ -6880,10 +6989,6 @@ bool FurnaceGUI::init() { ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_DockingEnable; toggleMobileUI(mobileUI,true); - for (int i=0; i #include +#include #include #include #include @@ -291,6 +292,8 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_C140, GUI_COLOR_INSTR_C219, GUI_COLOR_INSTR_ESFM, + GUI_COLOR_INSTR_POWERNOISE, + GUI_COLOR_INSTR_POWERNOISE_SLOPE, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_BG, @@ -757,6 +760,7 @@ enum FurnaceGUIActions { GUI_ACTION_SAMPLE_LIST_PREVIEW, GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW, GUI_ACTION_SAMPLE_LIST_DIR_VIEW, + GUI_ACTION_SAMPLE_LIST_MAKE_MAP, GUI_ACTION_SAMPLE_LIST_MAX, GUI_ACTION_SAMPLE_MIN, @@ -889,9 +893,17 @@ struct SelectionPoint { struct UndoRegion { struct UndoRegionPoint { int ord, x, y; + UndoRegionPoint(int o, int xp, int yp): + ord(o), x(xp), y(yp) {} UndoRegionPoint(): - ord(0), x(0), y(0) {} + ord(-1), x(-1), y(-1) {} } begin, end; + UndoRegion(int o0, int x0, int y0, int o1, int x1, int y1): + begin(o0,x0,y0), + end(o1,x1,y1) {} + UndoRegion(): + begin(), + end() {} }; enum ActionType { @@ -1465,6 +1477,7 @@ class FurnaceGUI { bool killGraphics; bool safeMode; bool midiWakeUp; + bool makeDrumkitMode; bool audioEngineChanged, settingsChanged, debugFFT; bool willExport[DIV_MAX_CHIPS]; int vgmExportVersion; @@ -1474,6 +1487,7 @@ class FurnaceGUI { int macroPointSize; int waveEditStyle; int displayInsTypeListMakeInsSample; + int makeDrumkitOctave; int mobileEditPage; int wheelCalmDown; int shallDetectScale; @@ -1966,6 +1980,11 @@ class FurnaceGUI { DivInstrument* prevInsData; + unsigned char* pendingLayoutImport; + size_t pendingLayoutImportLen; + int pendingLayoutImportStep; + FixedQueue pendingLayoutImportReopen; + int curIns, curWave, curSample, curOctave, curOrder, playOrder, prevIns, oldRow, editStep, exportLoops, soloChan, orderEditMode, orderCursor; int loopOrder, loopRow, loopEnd, isClipping, newSongCategory, latchTarget; int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar; @@ -2149,10 +2168,10 @@ class FurnaceGUI { ImVec2 orderScrollRealOrigin; ImVec2 dragMobileMenuOrigin; - int layoutTimeBegin, layoutTimeEnd, layoutTimeDelta; - int renderTimeBegin, renderTimeEnd, renderTimeDelta; - int drawTimeBegin, drawTimeEnd, drawTimeDelta; - int eventTimeBegin, eventTimeEnd, eventTimeDelta; + uint64_t layoutTimeBegin, layoutTimeEnd, layoutTimeDelta; + uint64_t renderTimeBegin, renderTimeEnd, renderTimeDelta; + uint64_t drawTimeBegin, drawTimeEnd, drawTimeDelta; + uint64_t eventTimeBegin, eventTimeEnd, eventTimeDelta; FurnaceGUIPerfMetric perfMetrics[64]; int perfMetricsLen; @@ -2179,7 +2198,7 @@ class FurnaceGUI { int oldOrdersLen; DivOrders oldOrders; - DivPattern* oldPat[DIV_MAX_CHANS]; + std::map oldPatMap; FixedQueue undoHist; FixedQueue redoHist; @@ -2536,16 +2555,16 @@ class FurnaceGUI { void moveCursorTop(bool select); void moveCursorBottom(bool select); void editAdvance(); - void prepareUndo(ActionType action); - void makeUndo(ActionType action); + void prepareUndo(ActionType action, UndoRegion region=UndoRegion()); + void makeUndo(ActionType action, UndoRegion region=UndoRegion()); void doSelectAll(); void doDelete(); void doPullDelete(); void doInsert(); void doTranspose(int amount, OperationMask& mask); String doCopy(bool cut, bool writeClipboard, const SelectionPoint& sStart, const SelectionPoint& sEnd); - void doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int startOff, bool invalidData); - void doPasteMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int mptFormat); + void doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int startOff, bool invalidData, UndoRegion ur); + void doPasteMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int mptFormat, UndoRegion ur); void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL, int arg=0, bool readClipboard=true, String clipb=""); void doChangeIns(int ins); void doInterpolate(); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index f48b8b906..039bfd5bc 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -176,6 +176,8 @@ const char* insTypes[DIV_INS_MAX+1][3]={ {"C140",ICON_FA_VOLUME_UP,ICON_FUR_INS_C140}, {"C219",ICON_FA_VOLUME_UP,ICON_FUR_INS_C219}, {"FM (ESFM)",ICON_FA_AREA_CHART,ICON_FUR_INS_ESFM}, + {"PowerNoise (noise)",ICON_FUR_NOISE,ICON_FUR_INS_POWERNOISE}, + {"PowerNoise (slope)",ICON_FUR_SAW,ICON_FUR_INS_POWERNOISE_SAW}, {NULL,ICON_FA_QUESTION,ICON_FA_QUESTION} }; @@ -728,6 +730,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("SAMPLE_LIST_PREVIEW", "Preview", 0), D("SAMPLE_LIST_STOP_PREVIEW", "Stop preview", 0), D("SAMPLE_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v), + D("SAMPLE_LIST_MAKE_MAP", "Make me a drum kit", 0), D("SAMPLE_LIST_MAX", "", NOT_AN_ACTION), D("SAMPLE_MIN", "---Sample editor", NOT_AN_ACTION), @@ -986,6 +989,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_C140,"",ImVec4(1.0f,1.0f,0.0f,1.0f)), D(GUI_COLOR_INSTR_C219,"",ImVec4(1.0f,0.8f,0.0f,1.0f)), D(GUI_COLOR_INSTR_ESFM,"",ImVec4(0.1f,0.9f,1.0f,1.0f)), + D(GUI_COLOR_INSTR_POWERNOISE,"",ImVec4(1.0f,1.0f,0.8f,1.0f)), + D(GUI_COLOR_INSTR_POWERNOISE_SLOPE,"",ImVec4(1.0f,0.6f,0.3f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)), @@ -1203,6 +1208,7 @@ const int availableSystems[]={ DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_ESFM, DIV_SYSTEM_PONG, + DIV_SYSTEM_POWERNOISE, 0 // don't remove this last one! }; @@ -1292,6 +1298,7 @@ const int chipsSpecial[]={ DIV_SYSTEM_VRC6, DIV_SYSTEM_MMC5, DIV_SYSTEM_SM8521, + DIV_SYSTEM_POWERNOISE, 0 // don't remove this last one! }; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index e96d1b64e..f38dba6a1 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -328,6 +328,14 @@ const char* es5506FilterModes[4]={ "HP/K2, HP/K2", "HP/K2, LP/K1", "LP/K2, LP/K2", "LP/K2, LP/K1", }; +const char* powerNoiseControlBits[3]={ + "enable tap B", "AM with slope", NULL +}; + +const char* powerNoiseSlopeControlBits[7]={ + "invert B", "invert A", "reset B", "reset A", "clip B", "clip A", NULL +}; + const char* panBits[5]={ "right", "left", "rear right", "rear left", NULL }; @@ -1477,7 +1485,7 @@ void FurnaceGUI::kvsConfig(DivInstrument* ins, bool supportsKVS) { ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch); for (int i=0; i<4; i++) { - int o=(opCount==4)?orderedOps[i]:i; + int o=(opCount==4 && ins->type!=DIV_INS_ESFM)?orderedOps[i]:i; if (!(i&1)) ImGui::TableNextRow(); const char* label="AUTO##OPKVS"; if (ins->fm.op[o].kvs==0) { @@ -2916,7 +2924,11 @@ void FurnaceGUI::drawInsEdit() { ImGui::SetNextWindowSizeConstraints(ImVec2(440.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW,canvasH)); } if (ImGui::Begin("Instrument Editor",&insEditOpen,globalWinFlags|(settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking))) { - if (curIns<0 || curIns>=(int)e->song.ins.size()) { + if (curIns==-2) { + ImGui::SetCursorPosY(ImGui::GetCursorPosY()+(ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeightWithSpacing()+ImGui::GetStyle().ItemSpacing.y)*0.5f); + CENTER_TEXT("waiting..."); + ImGui::Text("waiting..."); + } else if (curIns<0 || curIns>=(int)e->song.ins.size()) { ImGui::SetCursorPosY(ImGui::GetCursorPosY()+(ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeightWithSpacing()*(e->song.ins.empty()?2.0f:3.0f)+ImGui::GetStyle().ItemSpacing.y)*0.5f); CENTER_TEXT("no instrument selected"); ImGui::Text("no instrument selected"); @@ -6898,6 +6910,12 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="OP4 Noise Mode"; dutyMax=3; } + if (ins->type==DIV_INS_POWERNOISE) { + dutyMax=0; + } + if (ins->type==DIV_INS_POWERNOISE_SLOPE) { + dutyMax=0; + } const char* waveLabel="Waveform"; int waveMax=(ins->type==DIV_INS_VERA)?3:(MAX(1,e->song.waveLen-1)); @@ -6932,6 +6950,8 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_TED) waveMax=0; if (ins->type==DIV_INS_C140) waveMax=0; if (ins->type==DIV_INS_C219) waveMax=0; + if (ins->type==DIV_INS_POWERNOISE) waveMax=0; + if (ins->type==DIV_INS_POWERNOISE_SLOPE) waveMax=0; if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7; if (ins->type==DIV_INS_PET) { waveMax=8; @@ -7070,6 +7090,12 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_ES5506) { panMax=4095; } + if (ins->type==DIV_INS_POWERNOISE) { + panMax=15; + } + if (ins->type==DIV_INS_POWERNOISE_SLOPE) { + panMax=15; + } if (volMax>0) { macroList.push_back(FurnaceGUIMacroDesc(volumeLabel,&ins->std.volMacro,volMin,volMax,160,uiColors[GUI_COLOR_MACRO_VOLUME])); @@ -7157,7 +7183,9 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_C140 || ins->type==DIV_INS_C219 || ins->type==DIV_INS_TED || - ins->type==DIV_INS_ESFM) { + ins->type==DIV_INS_ESFM || + ins->type==DIV_INS_POWERNOISE || + ins->type==DIV_INS_POWERNOISE_SLOPE) { macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ex1Max>0) { @@ -7250,10 +7278,35 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_MSM5232) { macroList.push_back(FurnaceGUIMacroDesc("Noise",&ins->std.ex3Macro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } + if (ins->type==DIV_INS_POWERNOISE) { + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,2,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powerNoiseControlBits)); + macroList.push_back(FurnaceGUIMacroDesc("Tap A Location",&ins->std.ex4Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Tap B Location",&ins->std.ex5Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Load LFSR",&ins->std.ex8Macro,0,16,256,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + } + if (ins->type==DIV_INS_POWERNOISE_SLOPE) { + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powerNoiseSlopeControlBits)); + macroList.push_back(FurnaceGUIMacroDesc("Portion A Length",&ins->std.ex2Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Portion B Length",&ins->std.ex3Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Portion A Offset",&ins->std.ex6Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Portion B Offset",&ins->std.ex7Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); + } drawMacros(macroList,macroEditStateMacros); ImGui::EndTabItem(); } + if (ins->type==DIV_INS_POWERNOISE || ins->type==DIV_INS_POWERNOISE_SLOPE) { + if (ImGui::BeginTabItem("PowerNoise")) { + int pnOctave=ins->powernoise.octave; + if (ImGui::InputInt("Octave offset",&pnOctave,1,4)) { PARAMETER + if (pnOctave<0) pnOctave=0; + if (pnOctave>15) pnOctave=15; + ins->powernoise.octave=pnOctave; + } + ImGui::Text("go to Macros for other parameters."); + ImGui::EndTabItem(); + } + } if (ins->type==DIV_INS_NES || ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index e102eae4e..c5d087de5 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -767,6 +767,34 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "clockSel=1") } ); + ENTRY( + "NEC PC-98 (with PC-9801-86) stereo", { // -73 also has OPNA + CH(DIV_SYSTEM_YM2608, 1.0f, 0, "clockSel=1"), + CH(DIV_SYSTEM_PCM_DAC, 1.0f, -1.0f, // 2x 16-bit Burr Brown DAC + "rate=44100\n" + "outDepth=15\n" + ), + CH(DIV_SYSTEM_PCM_DAC, 1.0f, 1.0f, + "rate=44100\n" + "outDepth=15\n" + ), + CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "clockSel=1") + } + ); + ENTRY( + "NEC PC-98 (with PC-9801-86; extended channel 3) stereo", { // -73 also has OPNA + CH(DIV_SYSTEM_YM2608_EXT, 1.0f, 0, "clockSel=1"), + CH(DIV_SYSTEM_PCM_DAC, 1.0f, -1.0f, + "rate=44100\n" + "outDepth=15\n" + ), + CH(DIV_SYSTEM_PCM_DAC, 1.0f, 1.0f, + "rate=44100\n" + "outDepth=15\n" + ), + CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "clockSel=1") + } + ); ENTRY( "NEC PC-98 (with PC-9801-73)", { CH(DIV_SYSTEM_YM2608, 1.0f, 0, "clockSel=1"), @@ -2828,6 +2856,11 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_SOUND_UNIT, 1.0f, 0, "") } ); + ENTRY( + "PowerNoise", { + CH(DIV_SYSTEM_POWERNOISE, 1.0f, 0, "") + } + ); CATEGORY_END; CATEGORY_BEGIN("DefleMask-compatible","these configurations are compatible with DefleMask.\nselect this if you need to save as .dmf or work with that program."); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 2ec4a0fd2..71f3cda80 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -44,7 +44,7 @@ #endif #ifdef HAVE_FREETYPE -#define FONT_BACKEND_DEFAULT 1 +#define FONT_BACKEND_DEFAULT 0 #else #define FONT_BACKEND_DEFAULT 0 #endif @@ -1520,10 +1520,10 @@ void FurnaceGUI::drawSettings() { ImGui::Text("YM2612"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::Combo("##YM2612Core",&settings.ym2612Core,ym2612Cores,3)) settingsChanged=true; + if (ImGui::Combo("##YM2612Core",&settings.ym2612Core,ym2612Cores,2)) settingsChanged=true; ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::Combo("##YM2612CoreRender",&settings.ym2612CoreRender,ym2612Cores,3)) settingsChanged=true; + if (ImGui::Combo("##YM2612CoreRender",&settings.ym2612CoreRender,ym2612Cores,2)) settingsChanged=true; ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -1966,6 +1966,7 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_PREVIEW); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DIR_VIEW); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_MAKE_MAP); KEYBIND_CONFIG_END; ImGui::TreePop(); @@ -3493,6 +3494,10 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_INSTR_PV1000,"PV-1000"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_K053260,"K053260"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_C140,"C140"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_C219,"C219"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_ESFM,"ESFM"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_POWERNOISE,"PowerNoise (noise)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_POWERNOISE_SLOPE,"PowerNoise (slope)"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); ImGui::TreePop(); } @@ -4756,18 +4761,17 @@ bool FurnaceGUI::importLayout(String path) { fclose(f); return false; } - unsigned char* file=new unsigned char[len]; - if (fread(file,1,(size_t)len,f)!=(size_t)len) { + pendingLayoutImport=new unsigned char[len]; + if (fread(pendingLayoutImport,1,(size_t)len,f)!=(size_t)len) { perror("read error"); lastError=fmt::sprintf("on read: %s",strerror(errno)); fclose(f); - delete[] file; + delete[] pendingLayoutImport; return false; } fclose(f); - ImGui::LoadIniSettingsFromMemory((const char*)file,len); - delete[] file; + pendingLayoutImportLen=len; return true; } diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 6c2f99868..be0609c98 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -2280,6 +2280,23 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl } break; } + case DIV_SYSTEM_SFX_BEEPER_QUADTONE: { + bool sysPal=flags.getInt("clockSel",0); + bool noHiss=flags.getBool("noHiss",false); + if (ImGui::Checkbox("PAL",&sysPal)) { + altered=true; + } + if (ImGui::Checkbox("Disable hissing",&noHiss)) { + altered=true; + } + if (altered) { + e->lockSave([&]() { + flags.set("clockSel",(int)sysPal); + flags.set("noHiss",noHiss); + }); + } + break; + } case DIV_SYSTEM_SWAN: case DIV_SYSTEM_BUBSYS_WSG: case DIV_SYSTEM_PET: diff --git a/src/icon/furIcons.h b/src/icon/furIcons.h index 5d5cec4cb..fb1c8a7b5 100644 --- a/src/icon/furIcons.h +++ b/src/icon/furIcons.h @@ -1,7 +1,7 @@ // not auto-generated. update every time you change icons.ttf! #define ICON_MIN_FUR 0xe0f0 -#define ICON_MAX_FUR 0xe159 +#define ICON_MAX_FUR 0xe15b // test #define ICON_FUR_TEST0 u8"\ue0f0" @@ -67,6 +67,8 @@ #define ICON_FUR_INS_C140 u8"\ue135" #define ICON_FUR_INS_C219 u8"\ue142" #define ICON_FUR_INS_ESFM u8"\ue143" +#define ICON_FUR_INS_POWERNOISE u8"\ue15a" +#define ICON_FUR_INS_POWERNOISE_SAW u8"\ue15b" // sample editor #define ICON_FUR_SAMPLE_APPLY_SILENCE u8"\ue136" diff --git a/src/main.cpp b/src/main.cpp index df9a9577f..3b23068e6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -253,6 +253,7 @@ TAParamResult pVersion(String) { printf("- SM8521 emulator (modified version) by cam900 (zlib license)\n"); printf("- D65010G031 emulator (modified version) by cam900 (zlib license)\n"); printf("- C140/C219 emulator (modified version) by cam900 (zlib license)\n"); + printf("- PowerNoise emulator by scratchminer (MIT)\n"); return TA_PARAM_QUIT; }