From 46e5e8db01acbb4d5765821ed77ba8868fa481fd Mon Sep 17 00:00:00 2001 From: Zheyu Shen Date: Fri, 18 May 2018 01:47:12 +0800 Subject: [PATCH] Add comprehensive Msys2 and CI support --- .appveyor.yml | 73 +++ .gitignore | 11 +- .travis.yml | 2 +- CMakeLists.txt | 114 +++- Makefile | 31 - README.md | 140 +++- appveyor_build.ps1 | 8 + cmake/Findsackit.cmake | 41 +- cmake/HunterGate.cmake | 543 ++++++++++++++++ cmake/hunter/config.cmake | 2 + hunter/dummy | 0 include/common.h | 2 +- listfile.txt | 14 + src/lua_udp.h | 14 +- src/main.c | 24 +- src/network.c | 395 ++++++------ xlibinc/sackit/.gitignore | 20 + xlibinc/sackit/CMakeLists.txt | 69 ++ xlibinc/sackit/NOTES.txt | 150 +++++ xlibinc/sackit/README.txt | 65 ++ xlibinc/sackit/app_compare.c | 200 ++++++ xlibinc/sackit/app_convert.c | 41 ++ xlibinc/sackit/app_ezjack.c | 41 ++ xlibinc/sackit/app_play.c | 231 +++++++ xlibinc/sackit/app_sfx.c | 281 +++++++++ xlibinc/sackit/buildwin.sh | 3 + xlibinc/sackit/cmake/FindJack.cmake | 49 ++ xlibinc/sackit/cmake/FindSDL2.cmake | 163 +++++ xlibinc/sackit/effects.c | 506 +++++++++++++++ xlibinc/sackit/ezjack.c | 457 ++++++++++++++ xlibinc/sackit/ezjack.h | 94 +++ xlibinc/sackit/fixedmath.c | 71 +++ xlibinc/sackit/litemixer.c | 30 + xlibinc/sackit/mixer.c | 177 ++++++ xlibinc/sackit/mixer_float.h | 372 +++++++++++ xlibinc/sackit/mixer_int.h | 354 +++++++++++ xlibinc/sackit/mixer_int_fast.h | 424 +++++++++++++ xlibinc/sackit/objects.c | 688 ++++++++++++++++++++ xlibinc/sackit/playroutine.c | 563 +++++++++++++++++ xlibinc/sackit/playroutine_effects.c | 911 +++++++++++++++++++++++++++ xlibinc/sackit/playroutine_nna.c | 292 +++++++++ xlibinc/sackit/sackit.h | 370 +++++++++++ xlibinc/sackit/sackit_internal.h | 104 +++ xlibinc/sackit/tables.c | 240 +++++++ 44 files changed, 8059 insertions(+), 321 deletions(-) create mode 100644 .appveyor.yml delete mode 100644 Makefile create mode 100644 appveyor_build.ps1 create mode 100644 cmake/HunterGate.cmake create mode 100644 cmake/hunter/config.cmake create mode 100644 hunter/dummy create mode 100644 listfile.txt create mode 100644 xlibinc/sackit/.gitignore create mode 100644 xlibinc/sackit/CMakeLists.txt create mode 100644 xlibinc/sackit/NOTES.txt create mode 100644 xlibinc/sackit/README.txt create mode 100644 xlibinc/sackit/app_compare.c create mode 100644 xlibinc/sackit/app_convert.c create mode 100644 xlibinc/sackit/app_ezjack.c create mode 100644 xlibinc/sackit/app_play.c create mode 100644 xlibinc/sackit/app_sfx.c create mode 100644 xlibinc/sackit/buildwin.sh create mode 100644 xlibinc/sackit/cmake/FindJack.cmake create mode 100644 xlibinc/sackit/cmake/FindSDL2.cmake create mode 100644 xlibinc/sackit/effects.c create mode 100644 xlibinc/sackit/ezjack.c create mode 100644 xlibinc/sackit/ezjack.h create mode 100644 xlibinc/sackit/fixedmath.c create mode 100644 xlibinc/sackit/litemixer.c create mode 100644 xlibinc/sackit/mixer.c create mode 100644 xlibinc/sackit/mixer_float.h create mode 100644 xlibinc/sackit/mixer_int.h create mode 100644 xlibinc/sackit/mixer_int_fast.h create mode 100644 xlibinc/sackit/objects.c create mode 100644 xlibinc/sackit/playroutine.c create mode 100644 xlibinc/sackit/playroutine_effects.c create mode 100644 xlibinc/sackit/playroutine_nna.c create mode 100644 xlibinc/sackit/sackit.h create mode 100644 xlibinc/sackit/sackit_internal.h create mode 100644 xlibinc/sackit/tables.c diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..32808d8 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,73 @@ +environment: + matrix: + - GENERATOR: "MinGW Makefiles" + HUNTER: "ON" + + - GENERATOR: "MSYS Makefiles" + HUNTER: "OFF" + MSYS2_ARCH: x86_64 + MSYSTEM: MINGW64 + + - GENERATOR: "MSYS Makefiles" + HUNTER: "OFF" + MSYS2_ARCH: i686 + MSYSTEM: MINGW32 + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + GENERATOR: "Visual Studio 15 2017 Win64" + HUNTER: "ON" + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + GENERATOR: "Visual Studio 14 2015 Win64" + HUNTER: "ON" + +before_build: + # Stupid cmd always fails me!!!!!!!!!! + - ps: $env:CMAKE_SCRIPT = "cmake -G \`"$env:GENERATOR\`" -DHUNTER_ENABLED=$env:HUNTER . && cmake --build . --target iceball && cmake --build . --target iceball-dedi"; + + # Workaround for CMake complaining about sh.exe being in PATH + - ps: $env:PATH = ($env:PATH.Split(";") | Where-Object { $_ -ne "C:\Program Files\Git\usr\bin" }) -join ";"; + - ps: $env:PATH = "C:\MinGW\bin;$env:PATH"; + # Set up MSYS2 + # Unused toolchains are removed to speed up update. + - ps: | + if ($env:GENERATOR -eq "MSYS Makefiles") + { + $env:PATH = "C:\msys64\$env:MSYSTEM\bin;C:\msys64\usr\bin;$env:PATH"; + $env:PACMAN = "pacman --noconfirm --noprogressbar"; + $env:MINGW_PACKAGE_PREFIX = "mingw-w64-$env:MSYS2_ARCH"; + if ($env:MSYS2_ARCH -eq "x86_64") + { + bash -lc "$env:PACMAN -Rs mingw-w64-i686-toolchain"; + } + if ($env:MSYS2_ARCH -eq "i686") + { + bash -lc "$env:PACMAN -Rs mingw-w64-x86_64-toolchain"; + } + bash -lc "$env:PACMAN -Syu"; + bash -lc "$env:PACMAN -Su"; + bash -lc "$env:PACMAN -S $env:MINGW_PACKAGE_PREFIX-cmake"; + bash -lc "$env:PACMAN -S $env:MINGW_PACKAGE_PREFIX-SDL2"; + bash -lc "$env:PACMAN -S $env:MINGW_PACKAGE_PREFIX-luajit-git"; + bash -lc "$env:PACMAN -S $env:MINGW_PACKAGE_PREFIX-enet"; + } + +build_script: + #Use a seperate script file so that warnings on stderr won't fail the build + - powershell .\appveyor_build.ps1 + - ps: | + if ($env:GENERATOR -eq "MinGW Makefiles") + { + $env:ICEBALL_ZIP_FILENAME = "iceball-$env:APPVEYOR_REPO_COMMIT.zip"; + 7z a $env:ICEBALL_ZIP_FILENAME `@listfile.txt; + Push-AppveyorArtifact $env:ICEBALL_ZIP_FILENAME; + } + +deploy: + description: $(APPVEYOR_REPO_COMMIT) + provider: GitHub + auth_token: + secure: tMJF/Nc5vz1iNTuX94v3fdoToy+RDykn8cfehVok1el8s2ZgD9GSi/fLfbVsSv5M + force_update: true + on: + appveyor_repo_tag: true diff --git a/.gitignore b/.gitignore index 67e1c04..172a9b8 100644 --- a/.gitignore +++ b/.gitignore @@ -65,4 +65,13 @@ ZERO_CHECK.vcproj # vim stuff .vimrc -# clsave/vol/fastload.tmp +# fastload cache +clsave/vol/fastload.tmp + +# Makefiles are now generated by cmake +Makefile* + +# Doxygen stuff +docs/html/* +docs/latex/* +docs/doxygen_sqlite3.db diff --git a/.travis.yml b/.travis.yml index 1fa1e85..9aa9622 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,4 +12,4 @@ addons: liblua5.1-0-dev libenet-dev libluajit-5.1-dev -script: cmake . && cmake --build . +script: cmake . && make iceball iceball-dedi diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e9d9d5..81860b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,22 @@ -cmake_minimum_required (VERSION 2.8.4) +cmake_minimum_required (VERSION 3.0) +# "MSYS2 and *nix are first class citizens." the holy arsdragonfly decided. +option(HUNTER_ENABLED "Enable Hunter to grab dependencies on Windows for mingw32-make and MSVC" OFF) +SET(HUNTER_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/hunter") +SET(HUNTER_CONFIGURATION_TYPES Debug) +include("cmake/HunterGate.cmake") +HunterGate( + URL "https://github.com/ruslo/hunter/archive/v0.20.70.tar.gz" + SHA1 "95fb7d11f0828746e2983b5f06ff7981a676da3f" + LOCAL +) project (iceball) set(CMAKE_SOURCE_DIR src) -set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - add_definitions(-fno-strict-aliasing -Wall -Wextra -g) # keep debugging symbols even in Release builds + add_definitions(-fno-strict-aliasing -Wall -Wextra -Og) # keep debugging symbols even in Release builds endif () include_directories(include) @@ -16,32 +26,56 @@ if (WIN32) endif (MSVC) endif (WIN32) -if (MINGW) - set(CMAKE_PREFIX_PATH "dist/mingw/enet;dist/mingw/sdl2;dist/mingw/lua51;dist/mingw/sackit;dist/mingw/zlib" CACHE PATH "" FORCE) -elseif (MSVC) - set(CMAKE_PREFIX_PATH "dist/msvc/enet;dist/msvc/sdl2;dist/msvc/lua51;dist/msvc/sackit;dist/msvc/zlib" CACHE PATH "" FORCE) + +if (MSYS) #MINGW is true on MSYS2, but we do not use hunter +elseif (MINGW OR MSVC) #Use hunter to grab and build dependencies + hunter_add_package(enet) + find_package(enet CONFIG REQUIRED) + hunter_add_package(Lua) + find_package(Lua CONFIG REQUIRED) + hunter_add_package(ZLIB) + find_package(ZLIB CONFIG REQUIRED) + hunter_add_package(SDL2) + find_package(SDL2 CONFIG REQUIRED) endif () -find_package(ENet REQUIRED) -find_package(SDL2 REQUIRED) -find_package(ZLIB REQUIRED) -find_package(LuaJIT) -if (LUAJIT_FOUND) - add_definitions(-DUSE_LUAJIT) - set(LUA_LIBRARIES ${LUA_LIBRARY} m) -else () - find_package(Lua REQUIRED) +if (MSYS OR (NOT (MINGW OR MSVC))) + #Default on MSYS, *nix and OSX + find_package(ENet REQUIRED) + find_package(SDL2 REQUIRED) + find_package(ZLIB REQUIRED) + find_package(LuaJIT) + if (LUAJIT_FOUND) + add_definitions(-DUSE_LUAJIT) + set(LUA_LIBRARIES ${LUA_LIBRARY} m) + else () + find_package(Lua REQUIRED) + endif () endif () + find_package(sackit REQUIRED) find_package(OpenGL REQUIRED) -include_directories( - ${ENet_INCLUDE_DIRS} - ${sackit_INCLUDE_DIRS} - ${ZLIB_INCLUDE_DIRS} - ${SDL2_INCLUDE_DIR} - ${LUA_INCLUDE_DIR} -) + +if (MSYS OR (NOT (MINGW OR MSVC))) + #Stupid naming inconsistency + include_directories( + ${ENet_INCLUDE_DIRS} + ${sackit_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIRS} + ${SDL2_INCLUDE_DIR} + ${LUA_INCLUDE_DIR} + ) +else () + include_directories( + "${ENET_ROOT}/include" + ${sackit_INCLUDE_DIRS} + "${ZLIB_ROOT}/include" + "${SDL2_ROOT}/include/SDL2" + "${LUA_ROOT}/include" + ) +endif () + file(GLOB LUA_FILES src/lua* src/external/bit.c) set(MAIN_FILES @@ -71,28 +105,38 @@ source_group(lua FILES ${LUA_FILES}) # iceball target add_executable(iceball ${MAIN_FILES} ${LUA_FILES} ${GL_FILES}) -target_link_libraries(iceball ${CMAKE_DL_LIBS} ${ENet_LIBRARIES} ${ZLIB_LIBRARIES} ${sackit_LIBRARY} ${LUA_LIBRARIES} ${SDL2_LIBRARIES} ${OPENGL_LIBRARIES}) +if (MSYS OR (NOT (MINGW OR MSVC))) + target_link_libraries(iceball ${CMAKE_DL_LIBS} ${ENet_LIBRARIES} ${ZLIB_LIBRARIES} ${sackit_LIBRARY} ${LUA_LIBRARIES} ${SDL2_LIBRARIES} ${OPENGL_LIBRARIES}) + if (MSYS) + set_target_properties(iceball PROPERTIES LINK_FLAGS "-mwindows") # Get rid of console + endif () +else () + if (MINGW) + set_target_properties(iceball PROPERTIES LINK_FLAGS "-lmingw32") + endif () + target_link_libraries(iceball ${CMAKE_DL_LIBS} ${sackit_LIBRARY} ${OPENGL_LIBRARIES} enet::enet Lua::lua_lib ZLIB::zlib SDL2::SDL2main SDL2::SDL2) +endif () set_target_properties(iceball PROPERTIES C_STANDARD 99) # iceball-dedi target add_executable(iceball-dedi EXCLUDE_FROM_ALL ${MAIN_FILES} ${LUA_FILES}) -target_link_libraries(iceball-dedi ${CMAKE_DL_LIBS} ${ENet_LIBRARIES} ${ZLIB_LIBRARIES} ${LUA_LIBRARIES} ${SDL_LIBRARY}) +if (MSYS OR (NOT (MINGW OR MSVC))) + target_link_libraries(iceball-dedi ${CMAKE_DL_LIBS} ${ENet_LIBRARIES} ${ZLIB_LIBRARIES} ${LUA_LIBRARIES}) +else () + target_link_libraries(iceball-dedi ${CMAKE_DL_LIBS} enet::enet Lua::lua_lib ZLIB::zlib) +endif () set_target_properties(iceball-dedi PROPERTIES C_STANDARD 99) set_target_properties(iceball-dedi PROPERTIES COMPILE_DEFINITIONS "DEDI") function(copy_run_dep arg1) add_custom_command(TARGET iceball POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${PROJECT_SOURCE_DIR}/${arg1}" + "${arg1}" $) endfunction() - -if (MINGW) - copy_run_dep(dist/mingw/sdl2/bin/libSDL2.dll) - copy_run_dep(dist/mingw/lua51/bin/liblua.dll) - copy_run_dep(dist/mingw/zlib/bin/libzlib.dll) -elseif (MSVC) - copy_run_dep(dist/msvc/sdl2/bin/SDL2.dll) - copy_run_dep(dist/msvc/lua51/bin/lua.dll) - copy_run_dep(dist/msvc/zlib/bin/zlib.dll) +if (MSYS) + copy_run_dep($ENV{MSYSTEM_PREFIX}/bin/SDL2.dll) + copy_run_dep($ENV{MSYSTEM_PREFIX}/bin/lua51.dll) + copy_run_dep($ENV{MSYSTEM_PREFIX}/bin/zlib1.dll) + copy_run_dep($ENV{MSYSTEM_PREFIX}/bin/libenet-7.dll) endif () diff --git a/Makefile b/Makefile deleted file mode 100644 index 0191c43..0000000 --- a/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -# I personally don't care if you steal this makefile. --GM - -#-Wno-unused-but-set-variable $(CFLAGS_EXTRA) -CFLAGS = -fno-strict-aliasing -g -Wall -Wextra \ - -Wno-unused-variable -Wno-unused-parameter \ - $(CFLAGS_EXTRA) \ - -Iinclude \ - -Ixlibinc \ - -I/usr/local/include \ - $(HEADERS_SDL) \ - $(HEADERS_ENet) \ - $(HEADERS_Lua) - -HEADERS_SDL = `sdl2-config --cflags` -HEADERS_ENet = `pkg-config libenet --cflags` -HEADERS_Lua = `./findlua.sh --cflags` - -LDFLAGS = -g -I/usr/local/include $(LDFLAGS_EXTRA) -LIBS_SDL = `sdl2-config --libs` -LIBS_ENet = `pkg-config libenet --libs` -LIBS_Lua = `./findlua.sh --libs` -# Lua is not an acronym. Get used to typing it with lower case u/a. -LIBS_zlib = -lz -LIBS_sackit = xlibinc/libsackit.a -LIBS = -Lxlibinc -lm $(LIBS_Lua) $(LIBS_SDL) $(LIBS_zlib) $(LIBS_sackit) -lGL -lGLEW $(LIBS_ENet) -ldl - -BINNAME = iceball - -OBJDIR = build/unix_gl - -include main.make diff --git a/README.md b/README.md index bd09b85..a82b286 100644 --- a/README.md +++ b/README.md @@ -4,48 +4,138 @@ Iceball is both a highly-extensible engine on top of which people can do anything they want, and a game that builds upon the classic version of [Ace of Spades](http://en.wikipedia.org/wiki/Ace_of_Spades_(video_game)) experience. # Getting started -Just want to play Iceball? You can grab the latest build from [iceball.build](http://iceball.build/). If you want to modify the source or just simply build Iceball yourself, follow the [instructions](https://github.com/iamgreaser/iceball/wiki/Building) on the wiki. + +Just want to play Iceball? You can grab the latest build from [here](https://github.com/iamgreaser/iceball/releases). If you want to modify the source or just simply build Iceball yourself, see the [How to Build](#how-to-build) section below, or alternatively check out the (now possibly outdated) [instructions](https://github.com/iamgreaser/iceball/wiki/Building) on the wiki. If you're planning on contributing to the Iceball project, please check out [contribution guidelines](https://github.com/iamgreaser/iceball/wiki/Helping-out) on the wiki as well. All help is appreciated! # Community -* **Official**: - * Reddit: [/r/iceball](http://reddit.com/r/iceball) - * IRC: `irc.fractiongamers.com/#iceball` ([webchat](http://webchat.fractiongamers.com/?channels=iceball)) -* **Unofficial**: - * BnS subreddit: [/r/buildandshoot](http://reddit.com/r/buildandshoot) - * BnS subforum: [buildandshoot.com](http://www.buildandshoot.com/viewforum.php?f=84) + +- **Official**: + - Reddit: [/r/iceball](http://reddit.com/r/iceball) + - IRC: `irc.fractiongamers.com/#iceball` ([webchat](http://webchat.fractiongamers.com/?channels=iceball)) +- **Unofficial**: + - BnS subreddit: [/r/buildandshoot](http://reddit.com/r/buildandshoot) + - BnS subforum: [buildandshoot.com](http://www.buildandshoot.com/viewforum.php?f=84) + +# How to Build + +## Linux + +Grab the dependencies: + +``` +# Ubuntu et. al. +$ sudo apt update +$ sudo apt install cmake libsdl2-dev libenet-dev libluajit-5.1-dev +``` + +Run CMake and compile: + +``` +cmake . && cmake --build . +``` + +## Windows + +### Msys2 + +The Msys2 MinGW toolchain is recommended since it provides hassle-free dependency management. + +Grab the dependencies (assuming running MSYS2 MinGW 64-bit shell; otherwise change x86_64 to i686) + +``` +$ pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-SDL2 mingw-w64-x86_64-luajit-git mingw-w64-x86_64-enet +``` + +Run CMake and compile: + +``` +$ cmake -G "MSYS Makefiles" . && make iceball +``` + +If you want to build the dedicated server executable, run + +``` +$ make iceball-dedi +``` + +### MinGW + +Dependency management is done using Hunter; + +``` +$ cmake -G "MinGW Makefiles" -DHUNTER_ENABLED=ON -DCMAKE_SH="CMAKE_SH-NOTFOUND" . && mingw32-make iceball +``` + +If you want to build the dedicated server executable, run + +``` +$ mingw32-make iceball-dedi +``` + +### MSVC + +Dependency management is done using Hunter as well. Open up the VS command prompt and enter + +``` +$ cmake -G "Visual Studio 15 2017 Win64" -DHUNTER_ENABLED=ON . && cmake --build . --target iceball +``` + +If you want to build the dedicated server executable, run + +``` +$ cmake --build . --target iceball-dedi +``` + +## OS X + +Grab the dependencies: + +``` +$ brew install lua, enet, SDL2 +``` + +Run CMake and compile: + +``` +$ cmake . && cmake --build . +``` # Feedback If you have some thoughts on how to improve this project, please use the subreddit to start a discussion about it. If the feature you want added is both clearly defined and plausible, you can open a issue in the [issue tracker](https://github.com/iamgreaser/iceball/issues) instead. -You should also use the [issue tracker](https://github.com/iamgreaser/iceball/issues) for reporting any bugs you find. Make sure they aren't already reported though! +You should also use the [issue tracker](https://github.com/arsdragonfly/iceball/issues) for reporting any bugs you find. Make sure they aren't already reported though! # More info -Check out the `docs/` directory for some in-depth information regarding formats, engine intrinsics etc. Be aware that not all documentation may be up to date however. A lot of this information is being ported to the [wiki](https://github.com/iamgreaser/iceball/wiki) to siphon out deprecated documentation. + +Check out the `docs/` directory for some in-depth information regarding formats, engine intrinsics etc. Be aware that not all documentation may be up to date however. A lot of information is also available at the original [wiki](https://github.com/iamgreaser/iceball/wiki). +The project is now switching to [Doxygen](http://www.doxygen.org/). # License + ``` -Iceball is licensed under the regular GNU GPL version 3. -Ice Lua Components is licensed under the LGPL version 3. -Any of the "Team Sparkle" stuff is available under MIT, including the launcher. -All assets are released under Creative Commons 3.0 BY-SA: - http://creativecommons.org/licenses/by-sa/3.0/ + Iceball is licensed under the regular GNU GPL version 3. + Ice Lua Components is licensed under the LGPL version 3. + Any of the "Team Sparkle" stuff is available under MIT, including the launcher. + Sackit is under public domain. + All assets are released under Creative Commons 3.0 BY-SA: + http://creativecommons.org/licenses/by-sa/3.0/ -These are, unless otherwise marked: - Copyright (C) 2012-2015, Iceball contributors. + These are, unless otherwise marked: + Copyright (C) 2012-2015, Iceball contributors. -The credits list is almost always out of date, -so check the git log for a list of contributors. + The credits list is almost always out of date, + so check the git log for a list of contributors. -Ice Lua Components contains some content from libSDL, - which is licensed under the LGPL version 2.1. -It is marked accordingly. + Ice Lua Components contains some content from libSDL, + which is licensed under the LGPL version 2.1. + It is marked accordingly. -Code in src/external is licensed under their respective licenses, - which are listed in LICENCE-others.txt. + Code in src/external is licensed under their respective licenses, + which are listed in LICENCE-others.txt. -The manual is in the public domain, except where otherwise specified. + The manual is in the public domain, except where otherwise specified. -Copyright (C) 2012-2015, Iceball contributors + Copyright (C) 2012-2015, Iceball contributors ``` diff --git a/appveyor_build.ps1 b/appveyor_build.ps1 new file mode 100644 index 0000000..c0a0a6f --- /dev/null +++ b/appveyor_build.ps1 @@ -0,0 +1,8 @@ +if ($env:GENERATOR -eq "MSYS Makefiles") +{ + $env:BUILD_FOLDER = $env:APPVEYOR_BUILD_FOLDER -replace '\\', '\\\\'; + bash -lc "cd $env:BUILD_FOLDER; $env:CMAKE_SCRIPT 2>&1"; +} else { + $env:CMAKE_SCRIPT = $env:CMAKE_SCRIPT -replace '\\', ''; + cmd.exe /c "$env:CMAKE_SCRIPT 2>&1"; +} diff --git a/cmake/Findsackit.cmake b/cmake/Findsackit.cmake index f41c579..f93ce5b 100644 --- a/cmake/Findsackit.cmake +++ b/cmake/Findsackit.cmake @@ -5,25 +5,34 @@ # sackit_INCLUDE_DIRS - the sackit include directory # sackit_LIBRARIES - the libraries needed to use sackit # -# $SACKITDIR is an environment variable used for finding sackit. +# The build process used involve building and installing sackit in a seperate source tree; +# since sackit is now shipped with iceball and installed in-place, we no longer use system-wide paths. +# Enable them if you otherwise want to link against system-wide sackit. +# $SACKITDIR is an environment variable used for finding system-wide sackit. # -FIND_PATH(sackit_INCLUDE_DIRS sackit.h - PATHS - $ENV{SACKITDIR} - /usr/local - /usr - PATH_SUFFIXES include - ) +# FIND_PATH(sackit_INCLUDE_DIRS sackit.h +# PATHS +# $ENV{SACKITDIR} +# /usr/local +# /usr +# PATH_SUFFIXES include +# ) +# +# FIND_LIBRARY(sackit_LIBRARY +# NAMES sackit +# PATHS ${CMAKE_SOURCE_DIR}/xlibinc/ +# $ENV{SACKITDIR} +# /usr/local +# /usr +# PATH_SUFFIXES lib +# ) +# + +add_subdirectory(xlibinc/sackit) -FIND_LIBRARY(sackit_LIBRARY - NAMES sackit - PATHS - $ENV{SACKITDIR} - /usr/local - /usr - PATH_SUFFIXES lib - ) +FIND_PATH(sackit_INCLUDE_DIRS sackit.h PATHS ${CMAKE_CURRENT_SOURCE_DIR}/xlibinc/sackit/) +set(sackit_LIBRARY sackit) # handle the QUIETLY and REQUIRED arguments and set SACKIT_FOUND to TRUE if # all listed variables are TRUE diff --git a/cmake/HunterGate.cmake b/cmake/HunterGate.cmake new file mode 100644 index 0000000..4e2bb71 --- /dev/null +++ b/cmake/HunterGate.cmake @@ -0,0 +1,543 @@ +# Copyright (c) 2013-2017, Ruslan Baratov +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This is a gate file to Hunter package manager. +# Include this file using `include` command and add package you need, example: +# +# cmake_minimum_required(VERSION 3.0) +# +# include("cmake/HunterGate.cmake") +# HunterGate( +# URL "https://github.com/path/to/hunter/archive.tar.gz" +# SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d" +# ) +# +# project(MyProject) +# +# hunter_add_package(Foo) +# hunter_add_package(Boo COMPONENTS Bar Baz) +# +# Projects: +# * https://github.com/hunter-packages/gate/ +# * https://github.com/ruslo/hunter + +option(HUNTER_ENABLED "Enable Hunter package manager support" ON) +if(HUNTER_ENABLED) + if(CMAKE_VERSION VERSION_LESS "3.0") + message(FATAL_ERROR "At least CMake version 3.0 required for hunter dependency management." + " Update CMake or set HUNTER_ENABLED to OFF.") + endif() +endif() + +include(CMakeParseArguments) # cmake_parse_arguments + +option(HUNTER_STATUS_PRINT "Print working status" ON) +option(HUNTER_STATUS_DEBUG "Print a lot info" OFF) +option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON) + +set(HUNTER_WIKI "https://github.com/ruslo/hunter/wiki") + +function(hunter_gate_status_print) + if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG) + foreach(print_message ${ARGV}) + message(STATUS "[hunter] ${print_message}") + endforeach() + endif() +endfunction() + +function(hunter_gate_status_debug) + if(HUNTER_STATUS_DEBUG) + foreach(print_message ${ARGV}) + string(TIMESTAMP timestamp) + message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}") + endforeach() + endif() +endfunction() + +function(hunter_gate_wiki wiki_page) + message("------------------------------ WIKI -------------------------------") + message(" ${HUNTER_WIKI}/${wiki_page}") + message("-------------------------------------------------------------------") + message("") + message(FATAL_ERROR "") +endfunction() + +function(hunter_gate_internal_error) + message("") + foreach(print_message ${ARGV}) + message("[hunter ** INTERNAL **] ${print_message}") + endforeach() + message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") + message("") + hunter_gate_wiki("error.internal") +endfunction() + +function(hunter_gate_fatal_error) + cmake_parse_arguments(hunter "" "WIKI" "" "${ARGV}") + string(COMPARE EQUAL "${hunter_WIKI}" "" have_no_wiki) + if(have_no_wiki) + hunter_gate_internal_error("Expected wiki") + endif() + message("") + foreach(x ${hunter_UNPARSED_ARGUMENTS}) + message("[hunter ** FATAL ERROR **] ${x}") + endforeach() + message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") + message("") + hunter_gate_wiki("${hunter_WIKI}") +endfunction() + +function(hunter_gate_user_error) + hunter_gate_fatal_error(${ARGV} WIKI "error.incorrect.input.data") +endfunction() + +function(hunter_gate_self root version sha1 result) + string(COMPARE EQUAL "${root}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("root is empty") + endif() + + string(COMPARE EQUAL "${version}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("version is empty") + endif() + + string(COMPARE EQUAL "${sha1}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("sha1 is empty") + endif() + + string(SUBSTRING "${sha1}" 0 7 archive_id) + + if(EXISTS "${root}/cmake/Hunter") + set(hunter_self "${root}") + else() + set( + hunter_self + "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked" + ) + endif() + + set("${result}" "${hunter_self}" PARENT_SCOPE) +endfunction() + +# Set HUNTER_GATE_ROOT cmake variable to suitable value. +function(hunter_gate_detect_root) + # Check CMake variable + string(COMPARE NOTEQUAL "${HUNTER_ROOT}" "" not_empty) + if(not_empty) + set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE) + hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable") + return() + endif() + + # Check environment variable + string(COMPARE NOTEQUAL "$ENV{HUNTER_ROOT}" "" not_empty) + if(not_empty) + set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE) + hunter_gate_status_debug("HUNTER_ROOT detected by environment variable") + return() + endif() + + # Check HOME environment variable + string(COMPARE NOTEQUAL "$ENV{HOME}" "" result) + if(result) + set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE) + hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable") + return() + endif() + + # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only) + if(WIN32) + string(COMPARE NOTEQUAL "$ENV{SYSTEMDRIVE}" "" result) + if(result) + set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE) + hunter_gate_status_debug( + "HUNTER_ROOT set using SYSTEMDRIVE environment variable" + ) + return() + endif() + + string(COMPARE NOTEQUAL "$ENV{USERPROFILE}" "" result) + if(result) + set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE) + hunter_gate_status_debug( + "HUNTER_ROOT set using USERPROFILE environment variable" + ) + return() + endif() + endif() + + hunter_gate_fatal_error( + "Can't detect HUNTER_ROOT" + WIKI "error.detect.hunter.root" + ) +endfunction() + +macro(hunter_gate_lock dir) + if(NOT HUNTER_SKIP_LOCK) + if("${CMAKE_VERSION}" VERSION_LESS "3.2") + hunter_gate_fatal_error( + "Can't lock, upgrade to CMake 3.2 or use HUNTER_SKIP_LOCK" + WIKI "error.can.not.lock" + ) + endif() + hunter_gate_status_debug("Locking directory: ${dir}") + file(LOCK "${dir}" DIRECTORY GUARD FUNCTION) + hunter_gate_status_debug("Lock done") + endif() +endmacro() + +function(hunter_gate_download dir) + string( + COMPARE + NOTEQUAL + "$ENV{HUNTER_DISABLE_AUTOINSTALL}" + "" + disable_autoinstall + ) + if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL) + hunter_gate_fatal_error( + "Hunter not found in '${dir}'" + "Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'" + "Settings:" + " HUNTER_ROOT: ${HUNTER_GATE_ROOT}" + " HUNTER_SHA1: ${HUNTER_GATE_SHA1}" + WIKI "error.run.install" + ) + endif() + string(COMPARE EQUAL "${dir}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("Empty 'dir' argument") + endif() + + string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("HUNTER_GATE_SHA1 empty") + endif() + + string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("HUNTER_GATE_URL empty") + endif() + + set(done_location "${dir}/DONE") + set(sha1_location "${dir}/SHA1") + + set(build_dir "${dir}/Build") + set(cmakelists "${dir}/CMakeLists.txt") + + hunter_gate_lock("${dir}") + if(EXISTS "${done_location}") + # while waiting for lock other instance can do all the job + hunter_gate_status_debug("File '${done_location}' found, skip install") + return() + endif() + + file(REMOVE_RECURSE "${build_dir}") + file(REMOVE_RECURSE "${cmakelists}") + + file(MAKE_DIRECTORY "${build_dir}") # check directory permissions + + # Disabling languages speeds up a little bit, reduces noise in the output + # and avoids path too long windows error + file( + WRITE + "${cmakelists}" + "cmake_minimum_required(VERSION 3.0)\n" + "project(HunterDownload LANGUAGES NONE)\n" + "include(ExternalProject)\n" + "ExternalProject_Add(\n" + " Hunter\n" + " URL\n" + " \"${HUNTER_GATE_URL}\"\n" + " URL_HASH\n" + " SHA1=${HUNTER_GATE_SHA1}\n" + " DOWNLOAD_DIR\n" + " \"${dir}\"\n" + " TLS_VERIFY\n" + " ${HUNTER_TLS_VERIFY}\n" + " SOURCE_DIR\n" + " \"${dir}/Unpacked\"\n" + " CONFIGURE_COMMAND\n" + " \"\"\n" + " BUILD_COMMAND\n" + " \"\"\n" + " INSTALL_COMMAND\n" + " \"\"\n" + ")\n" + ) + + if(HUNTER_STATUS_DEBUG) + set(logging_params "") + else() + set(logging_params OUTPUT_QUIET) + endif() + + hunter_gate_status_debug("Run generate") + + # Need to add toolchain file too. + # Otherwise on Visual Studio + MDD this will fail with error: + # "Could not find an appropriate version of the Windows 10 SDK installed on this machine" + if(EXISTS "${CMAKE_TOOLCHAIN_FILE}") + get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE) + set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}") + else() + # 'toolchain_arg' can't be empty + set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=") + endif() + + string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make) + if(no_make) + set(make_arg "") + else() + # Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM + set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}") + endif() + + execute_process( + COMMAND + "${CMAKE_COMMAND}" + "-H${dir}" + "-B${build_dir}" + "-G${CMAKE_GENERATOR}" + "${toolchain_arg}" + ${make_arg} + WORKING_DIRECTORY "${dir}" + RESULT_VARIABLE download_result + ${logging_params} + ) + + if(NOT download_result EQUAL 0) + hunter_gate_internal_error("Configure project failed") + endif() + + hunter_gate_status_print( + "Initializing Hunter workspace (${HUNTER_GATE_SHA1})" + " ${HUNTER_GATE_URL}" + " -> ${dir}" + ) + execute_process( + COMMAND "${CMAKE_COMMAND}" --build "${build_dir}" + WORKING_DIRECTORY "${dir}" + RESULT_VARIABLE download_result + ${logging_params} + ) + + if(NOT download_result EQUAL 0) + hunter_gate_internal_error("Build project failed") + endif() + + file(REMOVE_RECURSE "${build_dir}") + file(REMOVE_RECURSE "${cmakelists}") + + file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}") + file(WRITE "${done_location}" "DONE") + + hunter_gate_status_debug("Finished") +endfunction() + +# Must be a macro so master file 'cmake/Hunter' can +# apply all variables easily just by 'include' command +# (otherwise PARENT_SCOPE magic needed) +macro(HunterGate) + if(HUNTER_GATE_DONE) + # variable HUNTER_GATE_DONE set explicitly for external project + # (see `hunter_download`) + set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) + endif() + + # First HunterGate command will init Hunter, others will be ignored + get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET) + + if(NOT HUNTER_ENABLED) + # Empty function to avoid error "unknown function" + function(hunter_add_package) + endfunction() + + set( + _hunter_gate_disabled_mode_dir + "${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode" + ) + if(EXISTS "${_hunter_gate_disabled_mode_dir}") + hunter_gate_status_debug( + "Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}" + ) + list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}") + endif() + elseif(_hunter_gate_done) + hunter_gate_status_debug("Secondary HunterGate (use old settings)") + hunter_gate_self( + "${HUNTER_CACHED_ROOT}" + "${HUNTER_VERSION}" + "${HUNTER_SHA1}" + _hunter_self + ) + include("${_hunter_self}/cmake/Hunter") + else() + set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_LIST_DIR}") + + string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name) + if(_have_project_name) + hunter_gate_fatal_error( + "Please set HunterGate *before* 'project' command. " + "Detected project: ${PROJECT_NAME}" + WIKI "error.huntergate.before.project" + ) + endif() + + cmake_parse_arguments( + HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV} + ) + + string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1) + string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url) + string( + COMPARE + NOTEQUAL + "${HUNTER_GATE_UNPARSED_ARGUMENTS}" + "" + _have_unparsed + ) + string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global) + string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath) + + if(_have_unparsed) + hunter_gate_user_error( + "HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}" + ) + endif() + if(_empty_sha1) + hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory") + endif() + if(_empty_url) + hunter_gate_user_error("URL suboption of HunterGate is mandatory") + endif() + if(_have_global) + if(HUNTER_GATE_LOCAL) + hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)") + endif() + if(_have_filepath) + hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)") + endif() + endif() + if(HUNTER_GATE_LOCAL) + if(_have_global) + hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)") + endif() + if(_have_filepath) + hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)") + endif() + endif() + if(_have_filepath) + if(_have_global) + hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)") + endif() + if(HUNTER_GATE_LOCAL) + hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)") + endif() + endif() + + hunter_gate_detect_root() # set HUNTER_GATE_ROOT + + # Beautify path, fix probable problems with windows path slashes + get_filename_component( + HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE + ) + hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}") + if(NOT HUNTER_ALLOW_SPACES_IN_PATH) + string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces) + if(NOT _contain_spaces EQUAL -1) + hunter_gate_fatal_error( + "HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces." + "Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error" + "(Use at your own risk!)" + WIKI "error.spaces.in.hunter.root" + ) + endif() + endif() + + string( + REGEX + MATCH + "[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*" + HUNTER_GATE_VERSION + "${HUNTER_GATE_URL}" + ) + string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty) + if(_is_empty) + set(HUNTER_GATE_VERSION "unknown") + endif() + + hunter_gate_self( + "${HUNTER_GATE_ROOT}" + "${HUNTER_GATE_VERSION}" + "${HUNTER_GATE_SHA1}" + _hunter_self + ) + + set(_master_location "${_hunter_self}/cmake/Hunter") + if(EXISTS "${HUNTER_GATE_ROOT}/cmake/Hunter") + # Hunter downloaded manually (e.g. by 'git clone') + set(_unused "xxxxxxxxxx") + set(HUNTER_GATE_SHA1 "${_unused}") + set(HUNTER_GATE_VERSION "${_unused}") + else() + get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE) + set(_done_location "${_archive_id_location}/DONE") + set(_sha1_location "${_archive_id_location}/SHA1") + + # Check Hunter already downloaded by HunterGate + if(NOT EXISTS "${_done_location}") + hunter_gate_download("${_archive_id_location}") + endif() + + if(NOT EXISTS "${_done_location}") + hunter_gate_internal_error("hunter_gate_download failed") + endif() + + if(NOT EXISTS "${_sha1_location}") + hunter_gate_internal_error("${_sha1_location} not found") + endif() + file(READ "${_sha1_location}" _sha1_value) + string(COMPARE EQUAL "${_sha1_value}" "${HUNTER_GATE_SHA1}" _is_equal) + if(NOT _is_equal) + hunter_gate_internal_error( + "Short SHA1 collision:" + " ${_sha1_value} (from ${_sha1_location})" + " ${HUNTER_GATE_SHA1} (HunterGate)" + ) + endif() + if(NOT EXISTS "${_master_location}") + hunter_gate_user_error( + "Master file not found:" + " ${_master_location}" + "try to update Hunter/HunterGate" + ) + endif() + endif() + include("${_master_location}") + set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) + endif() +endmacro() diff --git a/cmake/hunter/config.cmake b/cmake/hunter/config.cmake new file mode 100644 index 0000000..7f93eec --- /dev/null +++ b/cmake/hunter/config.cmake @@ -0,0 +1,2 @@ +hunter_config(Lua VERSION 5.1.5-p3) +hunter_config(SDL2 VERSION ${HUNTER_SDL2_VERSION} CMAKE_ARGS RENDER_D3D=OFF VIDEO_OPENGLES=OFF) diff --git a/hunter/dummy b/hunter/dummy new file mode 100644 index 0000000..e69de29 diff --git a/include/common.h b/include/common.h index 17d057d..31b2732 100644 --- a/include/common.h +++ b/include/common.h @@ -80,7 +80,7 @@ typedef signed __int32 int32_t; typedef unsigned __int32 uint32_t; typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; -#if (_MSC_VER != 1900) +#if (_MSC_VER < 1900) #define snprintf sprintf_s #endif #define _USE_MATH_DEFINES //M_PI and whatnot from math.h diff --git a/listfile.txt b/listfile.txt new file mode 100644 index 0000000..a754bba --- /dev/null +++ b/listfile.txt @@ -0,0 +1,14 @@ +clsave/* +deploy/* +dlcache/* +docs/* +heart/* +pkg/* +svsave/* +tools/* +CREDITS.txt +LICENCE-*.txt +README.md +opencmd.bat +version.md +*.exe diff --git a/src/lua_udp.h b/src/lua_udp.h index 3449bfc..0fefb2d 100644 --- a/src/lua_udp.h +++ b/src/lua_udp.h @@ -15,13 +15,15 @@ along with Iceball. If not, see . */ +#if !defined(_MSC_VER) || (_MSC_VER <= 1900) const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt); +#endif int whitelist_validate(const char *name, int port); int icelua_fn_common_udp_open(lua_State *L) { int top = icelua_assert_stack(L, 0, 0); - + int ret, sockfd; const char *host; char port_ch[18]; @@ -46,7 +48,7 @@ int icelua_fn_common_udp_open(lua_State *L) { int icelua_fn_common_udp_recvfrom(lua_State *L) { int top = icelua_assert_stack(L, 1, 1); - + int sockfd; int n = 0; @@ -92,9 +94,9 @@ int icelua_fn_common_udp_recvfrom(lua_State *L) { lua_pushnil(L); else lua_pushstring(L, astr); - + lua_pushinteger(L, ntohs(saddr.sin_port)); - + return 3; } @@ -138,7 +140,7 @@ int icelua_fn_common_udp_sendto(lua_State *L) { if(L == lstate_client && !whitelist_validate(host, port)) return luaL_error(L, "address/port not on whitelist!"); - + // FIXME: the host lookup result should ideally be cached // FIXME: make note of the address family used / socktype // TODO: support IPv6 @@ -199,5 +201,3 @@ int icelua_fn_common_udp_close(lua_State *L) { close(sockfd); return 0; } - - diff --git a/src/main.c b/src/main.c index 1b133b1..446f37e 100644 --- a/src/main.c +++ b/src/main.c @@ -143,12 +143,12 @@ static const char* get_gl_debug_severity_name(GLenum severity) void APIENTRY opengl_cb_fun(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, void* userParam) { - printf("---------------------opengl-callback-start------------\n"); - printf("message: %s\n", message); - printf("type: %s\n", get_gl_debug_type_name(type)); - printf("id: %d\n", id); - printf("severity: %s\n", get_gl_debug_severity_name(severity)); - printf("---------------------opengl-callback-end--------------\n"); + //printf("---------------------opengl-callback-start------------\n"); + //printf("message: %s\n", message); + //printf("type: %s\n", get_gl_debug_type_name(type)); + //printf("id: %d\n", id); + //printf("severity: %s\n", get_gl_debug_severity_name(severity)); + //printf("---------------------opengl-callback-end--------------\n"); } int video_init(void) @@ -1185,6 +1185,11 @@ int start_game() int main(int argc, char *argv[]) { +#ifdef WIN32 + //Windows users need special care since they probably don't know >log.txt 2>&1 + FILE * fd_stdout = freopen("stdout.txt", "wt", stdout); + FILE * fd_stderr = freopen("stderr.txt", "wt", stderr); +#endif struct cli_args args = {0}; int parse_status = parse_args(argc, argv, &args); @@ -1210,7 +1215,7 @@ int main(int argc, char *argv[]) free(args.basedir); args.basedir = NULL; } - + mod_basedir = args.basedir; #ifndef DEDI @@ -1230,5 +1235,10 @@ int main(int argc, char *argv[]) fflush(stdout); fflush(stderr); +#ifdef WIN32 + fclose(fd_stdout); + fclose(fd_stderr); +#endif + return 0; } diff --git a/src/network.c b/src/network.c index 1f3b5e7..9d61278 100644 --- a/src/network.c +++ b/src/network.c @@ -70,15 +70,16 @@ client_t to_clients[CLIENT_MAX]; #endif #if WIN32 +#if !defined(_MSC_VER) || (_MSC_VER <= 1900) const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) { const uint8_t *p; - + if (af == AF_INET) { if (cnt < INET_ADDRSTRLEN) return NULL; - + p = (const uint8_t*)src; snprintf(dst, cnt, "%u.%u.%u.%u", (unsigned int) (p[0] & 0xff), (unsigned int) (p[1] & 0xff), @@ -86,7 +87,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) } else if (af == AF_INET6) { if (cnt < 5*8) return NULL; - + p = (const uint8_t*)src; snprintf(dst, cnt, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", (unsigned int) (p[0] & 0xff), (unsigned int) (p[1] & 0xff), @@ -103,6 +104,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) return dst; } #endif +#endif // END SNIP int net_client_get_neth(client_t *cli) @@ -111,11 +113,11 @@ int net_client_get_neth(client_t *cli) if(cli == &to_client_local) return SOCKFD_LOCAL; - + for(i = 0; i < CLIENT_MAX; i++) if(cli == &to_clients[i]) return i*2+1; - + return SOCKFD_NONE; } @@ -127,10 +129,10 @@ client_t *net_neth_get_client(int neth) cli = &to_client_local; else if((neth & 1) && neth >= 0 && neth < CLIENT_MAX*2) cli = &to_clients[neth>>1]; - + if(cli == NULL || cli->sockfd == SOCKFD_NONE) return NULL; - + return cli; } @@ -142,7 +144,7 @@ int net_packet_push(int len, const char *data, int neth, packet_t **head, packet , len, PACKET_LEN_MAX); return 1; } - + client_t *cli = net_neth_get_client(neth); if(cli == NULL) @@ -164,14 +166,14 @@ int net_packet_push(int len, const char *data, int neth, packet_t **head, packet , len); return 1; } - + packet_t *pkt = (packet_t*)malloc(sizeof(packet_t)+len); if(pkt == NULL) { error_perror("net_packet_new"); return 1; } - + memcpy(pkt->data, data, len); pkt->len = len; pkt->neth = neth; @@ -186,7 +188,7 @@ int net_packet_push(int len, const char *data, int neth, packet_t **head, packet *tail = pkt; } } - + return 0; } @@ -198,14 +200,14 @@ int net_packet_push_lua_enet(int len, const char *data, int neth, packet_t **hea , len, PACKET_LEN_MAX-4); return 1; } - + if(len < 1) { fprintf(stderr, "net_packet_new: packet too small (%i < 1)\n" , len); return 1; } - + client_t *cli = net_neth_get_client(neth); if(cli == NULL) @@ -217,21 +219,21 @@ int net_packet_push_lua_enet(int len, const char *data, int neth, packet_t **hea } int poffs = (len >= 64 ? 3 : 1); - + packet_t *pkt = (packet_t*)malloc(sizeof(packet_t)+len+poffs); if(pkt == NULL) { error_perror("net_packet_new"); return 1; } - + pkt->data[0] = (poffs == 1 ? len+0x3F : 0x7F); if(poffs != 1) { pkt->data[1] = len&255; pkt->data[2] = len>>8; } - + memcpy(poffs + pkt->data, data, len); pkt->len = len + poffs; pkt->neth = neth; @@ -245,7 +247,7 @@ int net_packet_push_lua_enet(int len, const char *data, int neth, packet_t **hea pkt->n = NULL; *tail = pkt; } - + return 0; } @@ -257,14 +259,14 @@ int net_packet_push_lua(int len, const char *data, int neth, int unreliable, pac , len, PACKET_LEN_MAX-4); return 1; } - + if(len < 1) { fprintf(stderr, "net_packet_new: packet too small (%i < 1)\n" , len); return 1; } - + client_t *cli = net_neth_get_client(neth); if(cli == NULL) @@ -280,21 +282,21 @@ int net_packet_push_lua(int len, const char *data, int neth, int unreliable, pac enet_peer_send(cli->peer, 0, ep); } else { int poffs = (len >= 64 ? 3 : 1); - + packet_t *pkt = (packet_t*)malloc(sizeof(packet_t)+len+poffs); if(pkt == NULL) { error_perror("net_packet_new"); return 1; } - + pkt->data[0] = (poffs == 1 ? len+0x3F : 0x7F); if(poffs != 1) { pkt->data[1] = len&255; pkt->data[2] = len>>8; } - + memcpy(poffs + pkt->data, data, len); pkt->len = len + poffs; pkt->neth = neth; @@ -309,7 +311,7 @@ int net_packet_push_lua(int len, const char *data, int neth, int unreliable, pac *tail = pkt; } } - + return 0; } @@ -317,16 +319,16 @@ packet_t *net_packet_pop(packet_t **head, packet_t **tail) { if(*head == NULL) return NULL; - + packet_t *pkt = *head; *head = pkt->n; if(*head == NULL) *tail = NULL; else (*head)->p = NULL; - + pkt->p = pkt->n = NULL; - + return pkt; } @@ -336,12 +338,12 @@ void net_packet_free(packet_t *pkt, packet_t **head, packet_t **tail) pkt->p->n = pkt->n; if(pkt->n != NULL) pkt->n->p = pkt->p; - + if(head != NULL && pkt == *head) *head = pkt->n; if(tail != NULL && pkt == *tail) *tail = pkt->p; - + free(pkt); } @@ -360,7 +362,7 @@ void net_deinit_client(client_t *cli) while(cli->send_head != NULL) net_packet_free(cli->send_head, &(cli->send_head), &(cli->send_tail)); } - + cli->sockfd = SOCKFD_NONE; } @@ -368,7 +370,7 @@ void net_kick_sockfd_immediate(int sockfd, ENetPeer *peer, const char *msg) { if(sockfd == SOCKFD_NONE || sockfd == SOCKFD_LOCAL) return; - + client_t *cli = net_find_sockfd(sockfd, peer); char buf[260]; @@ -376,7 +378,7 @@ void net_kick_sockfd_immediate(int sockfd, ENetPeer *peer, const char *msg) buf[1] = strlen(msg); memcpy(buf+2, msg, (int)(uint8_t)buf[1]); buf[2+(int)(uint8_t)buf[1]] = 0x00; - + fprintf(stderr, "KICK: %i \"%s\"\n", sockfd, msg); // only send what's necessary @@ -388,7 +390,7 @@ void net_kick_sockfd_immediate(int sockfd, ENetPeer *peer, const char *msg) if(sockfd != SOCKFD_LOCAL) send(sockfd, buf, ((int)(uint8_t)buf[1])+1, 0); */ - + // call hook_disconnect if there's a valid client if(cli != NULL) { @@ -401,10 +403,10 @@ void net_kick_sockfd_immediate(int sockfd, ENetPeer *peer, const char *msg) lua_pushinteger(lstate_server, net_client_get_neth(cli)); else lua_pushboolean(lstate_server, 1); - + lua_pushboolean(lstate_server, 1); lua_pushstring(lstate_server, msg); - + if(lua_pcall(lstate_server, 3, 0, 0) != 0) { printf("ERROR running server Lua (hook_disconnect): %s\n", lua_tostring(lstate_server, -1)); @@ -442,7 +444,7 @@ void net_kick_client_immediate(client_t *cli, const char *msg) lua_pop(lstate_client, 1); } else { lua_pushstring(lstate_client, msg); - + if(lua_pcall(lstate_client, 1, 0, 0) != 0) { fprintf(stderr, "ERROR running client Lua (hook_kick): %s\n" @@ -451,10 +453,10 @@ void net_kick_client_immediate(client_t *cli, const char *msg) } } } - + if(cli == NULL) return; - + int oldsockfd = cli->sockfd; if(cli != &to_client_local) { @@ -492,7 +494,7 @@ const char *net_aux_gettype_str(int ftype) case UD_IMG_PNG: return "png"; } - + return NULL; } @@ -504,13 +506,13 @@ char *net_fetch_file(const char *fname, int *flen) perror("net_fetch_file"); return NULL; } - + int buf_len = 512; int buf_pos = 0; char *buf = (char*)malloc(buf_len+1); // TODO: check if NULL int buf_cpy; - + while(!feof(fp)) { int fetch_len = buf_len-buf_pos; @@ -521,18 +523,18 @@ char *net_fetch_file(const char *fname, int *flen) free(buf); return NULL; } - + buf_pos += buf_cpy; - + if(feof(fp)) break; - + buf_len += (buf_len>>1)+1; buf = (char*)realloc(buf, buf_len+1); } - + fclose(fp); - + *flen = buf_pos; buf[buf_pos] = '\0'; return buf; @@ -550,13 +552,13 @@ int net_eat_c2s_packet(client_t *cli, client_t *other, int neth, int len, const const char *fname = data + 3; int udtype = data[1] & 15; const char *ftype = net_aux_gettype_str(udtype); - + printf("file request: %02X %s \"%s\"\n", udtype, (ftype == NULL ? "*ERROR*" : ftype), fname); - + // call hook_file if it exists int use_serialised = 0; - + lua_getglobal(lstate_server, "server"); lua_getfield(lstate_server, -1, "hook_file"); lua_remove(lstate_server, -2); @@ -568,20 +570,20 @@ int net_eat_c2s_packet(client_t *cli, client_t *other, int neth, int len, const lua_pushinteger(lstate_server, neth); else lua_pushboolean(lstate_server, 1); - + lua_pushstring(lstate_server, ftype); lua_pushstring(lstate_server, fname); - + if(lua_pcall(lstate_server, 3, 1, 0) != 0) { fprintf(stderr, "ERROR running server Lua (hook_file): %s\n" , lua_tostring(lstate_server, -1)); lua_pop(lstate_server, 1); - + net_kick_client_immediate(net_neth_get_client(neth), "hook_file failed on server"); return 1; } - + // check result if(lua_isnil(lstate_server, -1)) { @@ -621,7 +623,7 @@ int net_eat_c2s_packet(client_t *cli, client_t *other, int neth, int len, const use_serialised = -1; break; } - + if(use_serialised == -1) { lua_pop(lstate_server, 1); @@ -629,7 +631,7 @@ int net_eat_c2s_packet(client_t *cli, client_t *other, int neth, int len, const } } } - + if(use_serialised) { // do nothing - we have the thing @@ -641,7 +643,7 @@ int net_eat_c2s_packet(client_t *cli, client_t *other, int neth, int len, const fprintf(stderr, "S->C transfer error: access denied\n"); return 1; } - + // check if we have a file in the queue if(other->sfetch_udtype != UD_INVALID) { @@ -649,15 +651,15 @@ int net_eat_c2s_packet(client_t *cli, client_t *other, int neth, int len, const fprintf(stderr, "S->C transfer error: still sending file\n"); return 1; } - + // k let's give this a whirl other->sfetch_ubuf = net_fetch_file(fname, &(other->sfetch_ulen)); } - + if(other->sfetch_ubuf != NULL) { other->sfetch_udtype = udtype; - + uLongf cbound = compressBound(other->sfetch_ulen); other->sfetch_cbuf = (char*)malloc(cbound); // TODO: check if NULL @@ -666,13 +668,13 @@ int net_eat_c2s_packet(client_t *cli, client_t *other, int neth, int len, const { // abort fprintf(stderr, "S->C transfer error: could not compress!\n"); - + if(other->sfetch_cbuf != NULL) free(other->sfetch_cbuf); free(other->sfetch_ubuf); other->sfetch_cbuf = NULL; other->sfetch_ubuf = NULL; - + char buf[] = "\x35"; net_packet_push(1, buf, neth, &(cli->send_head), &(cli->send_tail)); @@ -681,20 +683,20 @@ int net_eat_c2s_packet(client_t *cli, client_t *other, int neth, int len, const other->sfetch_clen = (int)cbound; free(other->sfetch_ubuf); other->sfetch_ubuf = NULL; - + // assemble packets... - + // initial packet { char buf[9]; buf[0] = 0x31; *(uint32_t *)&buf[1] = other->sfetch_clen; *(uint32_t *)&buf[5] = other->sfetch_ulen; - + net_packet_push(9, buf, neth, &(cli->send_head), &(cli->send_tail)); } - + // data packets { char buf[1+4+2+1024]; @@ -706,23 +708,23 @@ int net_eat_c2s_packet(client_t *cli, client_t *other, int neth, int len, const plen = 1024; *(uint32_t *)&buf[1] = (uint32_t)i; *(uint16_t *)&buf[5] = (uint16_t)plen; - + memcpy(&buf[7], &(other->sfetch_cbuf[i]), plen); - + net_packet_push(plen+7, buf, neth, &(cli->send_head), &(cli->send_tail)); } } - + // success packet { char buf[1]; buf[0] = 0x32; - + net_packet_push(1, buf, neth, &(cli->send_head), &(cli->send_tail)); } - + // all good! free(other->sfetch_cbuf); other->sfetch_cbuf = NULL; @@ -732,7 +734,7 @@ int net_eat_c2s_packet(client_t *cli, client_t *other, int neth, int len, const char buf[] = "\x35"; net_packet_push(1, buf, neth, &(cli->send_head), &(cli->send_tail)); - + } return 1; } break; @@ -753,13 +755,13 @@ int net_eat_c2s_packet(client_t *cli, client_t *other, int neth, int len, const void net_eat_c2s(client_t *cli) { int i; - + // TODO: sanity checks / handle fatal errors correctly packet_t *pkt, *npkt; for(pkt = cli->head; pkt != NULL; pkt = npkt) { npkt = pkt->n; - + // TODO: actually discern the source client_t *other = &to_client_local; @@ -788,7 +790,7 @@ int net_eat_s2c_packet(client_t *cli, client_t *other, int neth, int len, const fprintf(stderr, "ERROR: base dir already defined!\n"); // TODO: make this fatal } - + return 1; } break; case 0x17: { @@ -798,7 +800,7 @@ int net_eat_s2c_packet(client_t *cli, client_t *other, int neth, int len, const } else { net_kick_client_immediate(&to_client_local, &(data[2])); } - + return 1; } break; case 0x31: { @@ -813,7 +815,7 @@ int net_eat_s2c_packet(client_t *cli, client_t *other, int neth, int len, const cli->cfetch_ubuf = NULL; cli->cfetch_cpos = 0; // TODO: check if NULL - + return 1; } break; case 0x32: { @@ -822,24 +824,24 @@ int net_eat_s2c_packet(client_t *cli, client_t *other, int neth, int len, const //printf("transfer END\n"); cli->cfetch_ubuf = (char*)malloc(cli->cfetch_ulen+1); // TODO: check if NULL - + uLongf dlen = cli->cfetch_ulen; if(uncompress((Bytef *)(cli->cfetch_ubuf), &dlen, (Bytef *)(cli->cfetch_cbuf), cli->cfetch_clen) != Z_OK) { fprintf(stderr, "FETCH ERROR: could not decompress!\n"); // TODO: make this fatal - + free(cli->cfetch_cbuf); free(cli->cfetch_ubuf); cli->cfetch_cbuf = NULL; cli->cfetch_ubuf = NULL; return 1; } - + free(cli->cfetch_cbuf); cli->cfetch_cbuf = NULL; - + return 1; } break; case 0x33: { @@ -858,7 +860,7 @@ int net_eat_s2c_packet(client_t *cli, client_t *other, int neth, int len, const memcpy(offs + cli->cfetch_cbuf, &(data[7]), plen); cli->cfetch_cpos = offs + plen; } - + return 1; } break; case 0x35: { @@ -876,7 +878,7 @@ int net_eat_s2c_packet(client_t *cli, client_t *other, int neth, int len, const default: if(data[0] >= 0x40 && ((uint8_t)data[0]) <= 0x7F) return 0; - + return 1; } @@ -891,7 +893,7 @@ void net_eat_s2c(client_t *cli) for(pkt = cli->head; pkt != NULL; pkt = npkt) { npkt = pkt->n; - + if(net_eat_s2c_packet(cli, other, pkt->neth, pkt->len, pkt->data)) net_packet_free(pkt, &(cli->head), &(cli->tail)); } @@ -901,17 +903,17 @@ int net_flush_parse_onepkt(const char *data, int len) { if(len <= 0) return 0; - + int cmd = data[0]; int ilen = 0; - + if(cmd >= 0x40 && cmd <= 0x7E) { ilen = cmd-0x3E; } else if(cmd == 0x7F) { if(len < 4) return 0; - + ilen = (int)*(uint16_t *)&data[1]; ilen += 3; } else switch(cmd) { @@ -920,7 +922,7 @@ int net_flush_parse_onepkt(const char *data, int len) { if(len < 2) return 0; - + ilen = (int)(uint8_t)data[1]; ilen += 3; } break; @@ -928,7 +930,7 @@ int net_flush_parse_onepkt(const char *data, int len) { if(len < 4) return 0; - + ilen = (int)(uint8_t)data[2]; ilen += 4; } break; @@ -939,7 +941,7 @@ int net_flush_parse_onepkt(const char *data, int len) { if(len < 7) return 0; - + ilen = (int)*(uint16_t *)&data[5]; ilen += 7; } break; @@ -952,15 +954,15 @@ int net_flush_parse_onepkt(const char *data, int len) // TODO: terminate cleanly instead of locking return 0; } - + //printf("cmd=%02X ilen=%i blen=%i\n", cmd, ilen, len); - + if(ilen > PACKET_LEN_MAX) { // TODO: terminate cleanly instead of locking return 0; } - + return (ilen <= len ? ilen : 0); } @@ -970,30 +972,30 @@ void net_flush_parse_c2s(client_t *cli) int offs = 0; int len; char *data; - + len = cli->spkt_len; data = cli->spkt_buf; - + while(offs < len) { int nlen = net_flush_parse_onepkt(data+offs, len-offs); - + //printf("nlen=%i\n",nlen); if(nlen <= 0) break; - + net_packet_push(nlen, data+offs, net_client_get_neth(cli), &(to_server.head), &(to_server.tail)); - + offs += nlen; } - + if(offs != 0) { //printf("offs=%i len=%i\n", offs, len); if(offs < len) memmove(data, data+offs, len-offs); - + cli->spkt_len -= offs; } } @@ -1003,25 +1005,25 @@ void net_flush_parse_s2c(client_t *cli) int offs = 0; int len; char *data; - + len = cli->rpkt_len; data = cli->rpkt_buf; - + while(offs < len) { int nlen = net_flush_parse_onepkt(data+offs, len-offs); - + if(nlen <= 0) break; - + //printf("nlen=%i\n",nlen); - + net_packet_push(nlen, data+offs, net_client_get_neth(cli), &(cli->head), &(cli->tail)); - + offs += nlen; } - + if(offs != 0) { //printf("offs=%i len=%i\n", offs, len); @@ -1038,7 +1040,7 @@ void net_flush_parse_s2c(client_t *cli) client_t *net_find_sockfd(int sockfd, ENetPeer *peer) { int i; - + if(sockfd == SOCKFD_LOCAL) { return &to_client_local; @@ -1053,7 +1055,7 @@ client_t *net_find_sockfd(int sockfd, ENetPeer *peer) } else { return NULL; } - + return NULL; } @@ -1062,10 +1064,10 @@ client_t *net_alloc_sockfd(int sockfd, ENetPeer *peer) int i; client_t *cli = net_find_sockfd(sockfd, peer); - + if(cli != NULL) return cli; - + for(i = 0; i < CLIENT_MAX; i++) { if(to_clients[i].sockfd == SOCKFD_NONE) @@ -1075,7 +1077,7 @@ client_t *net_alloc_sockfd(int sockfd, ENetPeer *peer) return &to_clients[i]; } } - + return NULL; } @@ -1086,9 +1088,9 @@ int net_flush_transfer(client_t *cfrom, client_t *cto, packet_t *pfrom) //printf("still full!\n"); return 1; } - + int len = (cto == &to_server ? cfrom->spkt_len : cto->rpkt_len); - + if(pfrom->len + len > PACKET_LEN_MAX*2) { // TODO: send this somehow @@ -1100,17 +1102,17 @@ int net_flush_transfer(client_t *cfrom, client_t *cto, packet_t *pfrom) pfrom->p->n = pfrom->n; else cfrom->send_head = pfrom->n; - + if(pfrom->n != NULL) pfrom->n->p = pfrom->p; else cfrom->send_tail = pfrom->p; - + // here's the linkcopy version /* pfrom->n = NULL; pfrom->p = NULL; - + if(cto->tail == NULL) { cto->tail = cto->head = pfrom; @@ -1120,10 +1122,10 @@ int net_flush_transfer(client_t *cfrom, client_t *cto, packet_t *pfrom) p2->n = pfrom; pfrom->p = p2; }; - + return 0; */ - + // and of course the serialised version: if(cto == &to_server) { @@ -1163,9 +1165,9 @@ void net_flush_accept_one(int sockfd, ENetPeer *peer, struct sockaddr_storage *s cport = ((struct sockaddr_in6 *)(ss))->sin6_port; break; } - + printf("connection from %s, port %i, family %i\n", xstr, cport, ss->ss_family); - + // disable Nagle's algo int yes = 1; if(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&yes, sizeof(yes)) == -1) @@ -1174,7 +1176,7 @@ void net_flush_accept_one(int sockfd, ENetPeer *peer, struct sockaddr_storage *s " Kicked because gameplay will be complete shit otherwise."); return; } - + // set connection nonblocking yes = 1; #ifdef WIN32 @@ -1187,16 +1189,16 @@ void net_flush_accept_one(int sockfd, ENetPeer *peer, struct sockaddr_storage *s return; } } - + // get a slot client_t *cli = net_alloc_sockfd(sockfd, peer); - + if(cli == NULL) { net_kick_sockfd_immediate(sockfd, peer, "Server ran out of free slots!"); return; } - + // call hook_connect lua_getglobal(lstate_server, "server"); lua_getfield(lstate_server, -1, "hook_connect"); @@ -1205,13 +1207,13 @@ void net_flush_accept_one(int sockfd, ENetPeer *peer, struct sockaddr_storage *s { lua_pushinteger(lstate_server, net_client_get_neth(cli)); lua_newtable(lstate_server); - + if(sockfd == SOCKFD_ENET) { lua_pushstring(lstate_server, "enet/ip"); - + lua_setfield(lstate_server, -2, "proto"); - + lua_newtable(lstate_server); lua_pushstring(lstate_server, xstr); lua_setfield(lstate_server, -2, "ip"); @@ -1221,7 +1223,7 @@ void net_flush_accept_one(int sockfd, ENetPeer *peer, struct sockaddr_storage *s lua_setfield(lstate_server, -2, "cport"); lua_pushinteger(lstate_server, net_port); lua_setfield(lstate_server, -2, "sport"); - + lua_setfield(lstate_server, -2, "addr"); } else { switch(ss->ss_family) @@ -1232,9 +1234,9 @@ void net_flush_accept_one(int sockfd, ENetPeer *peer, struct sockaddr_storage *s lua_pushstring(lstate_server, "tcp/ip6"); else lua_pushstring(lstate_server, "tcp/ip"); - + lua_setfield(lstate_server, -2, "proto"); - + lua_newtable(lstate_server); lua_pushstring(lstate_server, xstr); lua_setfield(lstate_server, -2, "ip"); @@ -1244,12 +1246,12 @@ void net_flush_accept_one(int sockfd, ENetPeer *peer, struct sockaddr_storage *s lua_setfield(lstate_server, -2, "cport"); lua_pushinteger(lstate_server, net_port); lua_setfield(lstate_server, -2, "sport"); - + lua_setfield(lstate_server, -2, "addr"); break; } } - + if(lua_pcall(lstate_server, 2, 0, 0) != 0) { printf("ERROR running server Lua (hook_connect): %s\n", lua_tostring(lstate_server, -1)); @@ -1260,7 +1262,7 @@ void net_flush_accept_one(int sockfd, ENetPeer *peer, struct sockaddr_storage *s } else { lua_pop(lstate_server, 1); } - + // send pkg basedir packet { char buf[260]; @@ -1268,7 +1270,7 @@ void net_flush_accept_one(int sockfd, ENetPeer *peer, struct sockaddr_storage *s buf[1] = strlen(mod_basedir); memcpy(buf+2, mod_basedir, (int)(uint8_t)buf[1]); buf[2+(int)(uint8_t)buf[1]] = 0x00; - + net_packet_push(2+((int)(uint8_t)buf[1])+1, buf, net_client_get_neth(cli), &(to_server.send_head), &(to_server.send_tail)); } @@ -1281,7 +1283,7 @@ void net_flush_accept(void) struct sockaddr_storage ss; socklen_t slen = sizeof(ss); int sockfd = accept(server_sockfd_ipv6, (struct sockaddr *)&ss, &slen); - + if(sockfd == -1) { #ifdef WIN32 @@ -1298,13 +1300,13 @@ void net_flush_accept(void) net_flush_accept_one(sockfd, NULL, &ss, slen); } } - + if(server_sockfd_ipv4 != -1) { struct sockaddr_storage ss; socklen_t slen = sizeof(ss); int sockfd = accept(server_sockfd_ipv4, (struct sockaddr *)&ss, &slen); - + if(sockfd == -1) { #ifdef WIN32 @@ -1329,7 +1331,7 @@ void net_flush_snr(client_t *cli) { if(cli->sockfd == SOCKFD_NONE) return; - + if(cli == &to_client_local) { if(boot_mode & IB_SERVER) @@ -1340,7 +1342,7 @@ void net_flush_snr(client_t *cli) { int bs = send(cli->sockfd, cli->spkt_buf, cli->spkt_len, 0); - + if(bs == -1) { #ifdef WIN32 @@ -1361,11 +1363,11 @@ void net_flush_snr(client_t *cli) memmove(cli->spkt_buf, cli->spkt_buf+bs, cli->spkt_len); } } - + { int bs = recv(cli->sockfd, cli->rpkt_buf+cli->rpkt_len, PACKET_LEN_MAX*2-cli->rpkt_len, 0); - + if(bs == -1) { #ifdef WIN32 @@ -1397,7 +1399,7 @@ void net_flush_snr(client_t *cli) { int bs = send(cli->sockfd, cli->rpkt_buf, cli->rpkt_len, 0); - + if(bs == -1) { #ifdef WIN32 @@ -1418,11 +1420,11 @@ void net_flush_snr(client_t *cli) memmove(cli->rpkt_buf, cli->rpkt_buf+bs, cli->rpkt_len); } } - + { int bs = recv(cli->sockfd, cli->spkt_buf+cli->spkt_len, PACKET_LEN_MAX*2-cli->spkt_len, 0); - + if(bs == -1) { #ifdef WIN32 @@ -1455,7 +1457,7 @@ void net_flush(void) int i; int err; ENetEvent ev; - + if(boot_mode & IB_SERVER) { net_flush_accept(); @@ -1501,20 +1503,20 @@ void net_flush(void) break; } } - + // clear full flags for(i = 0; i < CLIENT_MAX; i++) to_clients[i].isfull = 0; to_client_local.isfull = 0; to_server.isfull = 0; - + // serialise the packets for(pkt = to_server.send_head; pkt != NULL; pkt = npkt) { npkt = pkt->n; - + client_t *cli = net_neth_get_client(pkt->neth); - + if(cli == NULL) { // this happens when a packet is dropped @@ -1527,19 +1529,19 @@ void net_flush(void) net_flush_transfer(&to_server, cli, pkt); } } - + for(i = 0; i < CLIENT_MAX; i++) if(to_clients[i].sockfd != SOCKFD_NONE) net_flush_snr(&to_clients[i]); - + // parse the incoming stuff for(i = 0; i < CLIENT_MAX; i++) if(to_clients[i].sockfd != SOCKFD_NONE) net_flush_parse_c2s(&to_clients[i]); - + net_flush_parse_c2s(&to_client_local); } - + if(boot_mode & IB_CLIENT) { if(boot_mode & IB_ENET) @@ -1582,20 +1584,20 @@ void net_flush(void) } else { // the usual stuff to_server.isfull = 0; - + for(pkt = to_client_local.send_head; pkt != NULL; pkt = npkt) { npkt = pkt->n; - + net_flush_transfer(&to_client_local, &to_server, pkt); } - + net_flush_snr(&to_client_local); - + net_flush_parse_s2c(&to_client_local); } } - + if(boot_mode & IB_SERVER) net_eat_c2s(&to_server); if(boot_mode & IB_CLIENT) @@ -1606,15 +1608,15 @@ int net_gethost(char *name, int port, struct sockaddr *sa, size_t alen) { char port_str[32]; struct addrinfo ainf; - + memset(&ainf, 0, sizeof(ainf)); ainf.ai_flags = 0; ainf.ai_family = AF_UNSPEC; ainf.ai_socktype = SOCK_STREAM; ainf.ai_protocol = 0; - + snprintf(port_str, 31, "%i", net_port); - + struct addrinfo *res; int err = getaddrinfo(name, port_str, &ainf, &res); if(err != 0) @@ -1622,7 +1624,7 @@ int net_gethost(char *name, int port, struct sockaddr *sa, size_t alen) fprintf(stderr, "net_gethost: %s\n", gai_strerror(err)); return 1; } - + struct addrinfo *best,*fol; best = NULL; for(fol = res; fol != NULL; fol = fol->ai_next) @@ -1642,7 +1644,7 @@ int net_gethost(char *name, int port, struct sockaddr *sa, size_t alen) } break; } printf("lookup: %s\n", xstr); - + // NOTE: prioritising IPv4 over IPv6. if(best == NULL) { @@ -1652,9 +1654,9 @@ int net_gethost(char *name, int port, struct sockaddr *sa, size_t alen) best = fol; } } - + memcpy(sa, best->ai_addr, best->ai_addrlen); - + //freeaddrinfo(res); return 0; } @@ -1712,15 +1714,15 @@ int net_connect(void) struct sockaddr_storage sa; if(net_gethost(net_address, net_port, (struct sockaddr *)&sa, sizeof(sa))) return 1; - + int sockfd = socket(sa.ss_family, SOCK_STREAM, 0); - + if(sockfd == -1) return error_perror("net_connect(socket)"); - + if(connect(sockfd, (struct sockaddr *)&sa, sizeof(sa)) == -1) return error_perror("net_connect(connect)"); - + int yes = 1; #ifdef WIN32 if(ioctlsocket(sockfd, FIONBIO, (u_long *)&yes)) @@ -1729,27 +1731,27 @@ int net_connect(void) fcntl(sockfd, F_GETFL) | O_NONBLOCK)) #endif return error_perror("net_connect(nonblock)"); - + if(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&yes, sizeof(yes)) == -1) return error_perror("net_connect(nodelay)"); - + to_client_local.sockfd = sockfd; } } break; - + case 3: { // client + server to_client_local.sockfd = SOCKFD_LOCAL; } break; } - + return 0; } void net_disconnect(void) { int i; - + if(to_client_local.sockfd >= 0) { #ifdef WIN32 @@ -1772,7 +1774,7 @@ void net_disconnect(void) client_host = NULL; } } - + for(i = 0; i < CLIENT_MAX; i++) { if(to_clients[i].sockfd >= 0) @@ -1804,23 +1806,23 @@ int net_bind(void) { if(net_port == 0) return 0; - + struct sockaddr_in6 sa6; struct sockaddr_in sa4; - + server_sockfd_ipv6 = socket(AF_INET6, SOCK_STREAM, 0); server_sockfd_ipv4 = socket(AF_INET, SOCK_STREAM, 0); - + sa6.sin6_family = AF_INET6; sa6.sin6_port = htons(net_port); sa6.sin6_addr = in6addr_any; sa6.sin6_flowinfo = 0; sa6.sin6_scope_id = 0; - + sa4.sin_family = AF_INET; sa4.sin_port = htons(net_port); sa4.sin_addr.s_addr = INADDR_ANY; - + int yes = 1; // who the hell uses solaris anyway if(setsockopt(server_sockfd_ipv6, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)) == -1) { @@ -1833,7 +1835,7 @@ int net_bind(void) perror("net_bind(reuseaddr.4)"); server_sockfd_ipv4 = SOCKFD_NONE; } - + yes = 1; if(server_sockfd_ipv6 != SOCKFD_NONE) { @@ -1854,7 +1856,7 @@ int net_bind(void) server_sockfd_ipv6 = SOCKFD_NONE; } } - + yes = 1; if(server_sockfd_ipv4 != SOCKFD_NONE) { @@ -1875,14 +1877,14 @@ int net_bind(void) server_sockfd_ipv4 = SOCKFD_NONE; } } - + if(server_sockfd_ipv4 == SOCKFD_NONE && server_sockfd_ipv6 == SOCKFD_NONE) return 1; - + printf("sockfds: IPv4 = %i, IPv6 = %i\n" , server_sockfd_ipv4 , server_sockfd_ipv6); - + // ENet server setup ENetAddress saddr; saddr.host = ENET_HOST_ANY; @@ -1901,7 +1903,7 @@ int net_bind(void) } printf("ENet host bound\n"); - + return 0; } @@ -1912,7 +1914,7 @@ void net_unbind(void) close(server_sockfd_ipv4); server_sockfd_ipv4 = SOCKFD_NONE; } - + if(server_sockfd_ipv6 != SOCKFD_NONE) { close(server_sockfd_ipv6); @@ -1929,7 +1931,7 @@ void net_unbind(void) int net_init(void) { int i; - + #ifdef WIN32 // complete hackjob if(!((boot_mode & IB_ENET) || (boot_mode & IB_SERVER))) @@ -1939,7 +1941,7 @@ int net_init(void) return 1; } #endif - + // start ENet unless running a TCP-only client if((boot_mode & IB_ENET) || (boot_mode & IB_SERVER)) { @@ -1950,7 +1952,7 @@ int net_init(void) } } atexit(enet_deinitialize); - + for(i = 0; i < CLIENT_MAX; i++) { to_clients[i].sockfd = SOCKFD_NONE; @@ -1958,37 +1960,36 @@ int net_init(void) to_clients[i].head = to_clients[i].tail = NULL; to_clients[i].send_head = to_clients[i].send_tail = NULL; } - + to_server.sockfd = SOCKFD_NONE; to_server.peer = NULL; to_server.head = to_server.tail = NULL; to_server.send_head = to_server.send_tail = NULL; - + to_client_local.sockfd = SOCKFD_NONE; to_client_local.peer = NULL; to_client_local.head = to_client_local.tail = NULL; to_client_local.send_head = to_client_local.send_tail = NULL; - + return 0; } void net_deinit(void) { int i; - + net_deinit_client(&to_server); net_deinit_client(&to_client_local); - + for(i = 0; i < CLIENT_MAX; i++) net_deinit_client(&(to_clients[i])); - + if(server_host != NULL) enet_host_destroy(server_host); if(client_host != NULL) enet_host_destroy(client_host); - + #ifdef WIN32 WSACleanup(); #endif } - diff --git a/xlibinc/sackit/.gitignore b/xlibinc/sackit/.gitignore new file mode 100644 index 0000000..aa02e52 --- /dev/null +++ b/xlibinc/sackit/.gitignore @@ -0,0 +1,20 @@ +# object files +*.o +# sackit_* +libsackit.so +libsackit.a + +# backups +*~ + +# build directory +build/ + +# Makefile is cmake-generated +Makefile + +cmake_install.cmake +CMakeCache.txt +CMakeFiles + +install_manifest.txt diff --git a/xlibinc/sackit/CMakeLists.txt b/xlibinc/sackit/CMakeLists.txt new file mode 100644 index 0000000..3a9d397 --- /dev/null +++ b/xlibinc/sackit/CMakeLists.txt @@ -0,0 +1,69 @@ +# cmake_minimum_required(VERSION 2.8.4) +# project(sackit) + +set(SACKIT_SOURCES + effects.c + fixedmath.c + mixer.c + objects.c + playroutine.c + playroutine_effects.c + playroutine_nna.c + tables.c +) + +set(SACKIT_HEADERS + sackit_internal.h + sackit.h +) + +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +add_library(sackit ${SACKIT_SOURCES} ${SACKIT_HEADERS}) +target_link_libraries(sackit) + +# install(TARGETS sackit DESTINATION "lib") +# install(FILES ${SACKIT_HEADERS} DESTINATION "include") + +# option(BUILD_SACKIT_COMPARE "Build sackit compare" OFF) +# option(BUILD_SACKIT_PLAY "Build sackit play" OFF) +# option(BUILD_SACKIT_EZJACK "Build sackit ezjack" OFF) +# option(BUILD_SACKIT_SFX "Build sackit sfx" OFF) +# option(BUILD_SACKIT_CONVERT "Build sackit convert" OFF) +# +# if (BUILD_SACKIT_COMPARE OR BUILD_SACKIT_PLAY OR BUILD_SACKIT_EZJACK OR BUILD_SACKIT_SFX) +# find_package(SDL2 REQUIRED) +# include_directories(${SDL2_INCLUDE_DIR}) +# +# if (BUILD_SACKIT_EZJACK) +# find_package(Jack REQUIRED) +# include_directories(${JACK_INCLUDE_DIRS}) +# endif () +# endif () +# +# if (BUILD_SACKIT_COMPARE) +# add_executable(sackit_compare app_compare.c) +# target_link_libraries(sackit_compare ${SDL2_LIBRARY} sackit) +# endif () +# +# if (BUILD_SACKIT_PLAY) +# add_executable(sackit_play app_play.c) +# target_link_libraries(sackit_play ${SDL2_LIBRARY} sackit) +# endif () +# +# if (BUILD_SACKIT_EZJACK) +# add_executable(sackit_ezjack app_ezjack.c ezjack.c) +# target_link_libraries(sackit_ezjack ${SDL2_LIBRARY} ${JACK_LIBRARIES} sackit) +# endif () +# +# if (BUILD_SACKIT_SFX) +# add_executable(sackit_sfx app_sfx.c) +# target_link_libraries(sackit_sfx ${SDL2_LIBRARY} sackit) +# endif () +# +# if (BUILD_SACKIT_CONVERT) +# add_executable(sackit_convert app_convert.c) +# target_link_libraries(sackit_convert sackit) +# endif () +# diff --git a/xlibinc/sackit/NOTES.txt b/xlibinc/sackit/NOTES.txt new file mode 100644 index 0000000..be0ea10 --- /dev/null +++ b/xlibinc/sackit/NOTES.txt @@ -0,0 +1,150 @@ +# +# STUFF THAT CAN WRECK THE MIXER +# + +IT203 changelog states: + Interpolated mixing options. + +IT203-IT210 driver info has lots of good stuff: + Disk Writer + + This WAV file writer is NOT available for public distribution. + Contact me if you wish to obtain this - it will NOT be made + available without some sort of (monetary) agreement. + + Details + * 16 bit Stereo/Mono output + * 22kHz to 64kHz output frequency + * 16 bit interpolation (65536x 'oversampling') + * 32 bit mixing + * Software volume ramping (set at 500th of a second or less) + + The disk writer also uses volumes ranging between 0 and 32768 + instead of 0 to 128. + +IT204 changelog states: + To all mixed devices which supported interpolation, some samples + could have caused errors. + + +IT206 is the first version with a PUBLIC ITWAV.DRV. +It spits out a RAW file. +(It's available in the PAID version of IT203.) + +IT211 changelog states: + The ITWAV.DRV file now writes proper .WAV files instead of .RAW + +^ This is probably the easiest to get working accurately that actually spits out a .wav. + +IT212 changelog states: + Inserted a new algorithm into the direct-to-disk writer to remove + clicks at the end of samples in cases of Note Cut commands, Note Cut NNA + and instantaneous sample changes in sample mode. For those who have + sent me money and would like to receive the upgrade, EMail me. + +IT213 changelog states: + Direct to Disk writer now uses logarithmic volume ramping + and quadratic spline interpolation + +IT214 changelog states: + Diskwriter interpolation changed from quadratic spline + to cubic spline. (Requires a FPU) + +IT214 patch 3 changelog states: + Driver news: Updated driver format (incompatible with previous ITs) + Resonant filters - check FILTERS.TXT for information. + This stuff has been released basically so that contributors + can distribute their songs that use filters.. + MMX drivers implemented. + WAV driver - time accuracy improved + - can specify destination directory (on shift-F5) + - handles resonant filters +IT214 patch 4 changelog states: + 4-band EQ in diskwriter + +List of things that'll piss me off, by version: +IT206: + Volume ramping. At least we have a number here :) + Oversampling, perhaps? + +# +# DIFFS FROM IT211 +# + +Currently going with IT211. +Stuff that might screw us up: +IT212: + - Bug fix: Default sample pan will override instrument pan whether "Old + Effects" is on or off. [IRRELEVANT, SEE IT213 BELOW] + + - Soundcard Driver news + - Inserted a new algorithm into the direct-to-disk writer to remove + clicks at the end of samples in cases of Note Cut commands, Note Cut NNA + and instantaneous sample changes in sample mode. For those who have + sent me money and would like to receive the upgrade, EMail me. + +IT213: + - Modification: Sample panning reset to override instrument panning due to + demand. + + - Update: Several miscellaneous changes + + - Update: If old effects is *ON*, then a looped volume envelope will NOT + include the last node (for XM compat) + + - Automatic MIDI Pitch wheel handling. Vibrato, pitch slides, portamentos + all handled. + + + - MIDI Configuration can be embedded into .IT files. + (Option is on Shift-F1) + +IT214: + - Driver news: Diskwriter interpolation changed from quadratic spline + to cubic spline. (Requires a FPU) + +IT214 Patch 1: + - Bug fix: Volume envelopes were skipping some ticks (sounded too fast) + - Bug fix: Slight problems with the wav writer fixed + - Other miscellaneous fixups + +IT214 Patch 2: + - Bug fix: MIDI Macros (unparameterised) were somehow disabled somewhere + after IT212.. now reenabled + +IT214 Patch 3: + - Driver news: Updated driver format (incompatible with previous ITs) + Resonant filters - check FILTERS.TXT for information. + This stuff has been released basically so that contributors + can distribute their songs that use filters.. + - MMX drivers implemented. + - WAV driver - time accuracy improved + - handles resonant filters + +IT214 Patch 4: + - Bug fix: SCx and Qxx commands and will work with MIDI Out instruments + + - Driver news: 4-band EQ in diskwriter + Minor miscellaneous upgrades + Fixes to MMX drivers that clicked on 0 volume (oops) + +IT214 Patch 5: + - Rewrite of all MMX routines for stability when networked. Non MMX drivers + have not been changed. + +# +# +# +From ITTECH.TXT: + Bit 5: On = Link Effect G's memory with Effect E/F. Also + Gxx with an instrument present will cause the + envelopes to be retriggered. If you change a + sample on a row with Gxx, it'll adjust the + frequency of the current note according to: + + NewFrequency = OldFrequency * NewC5 / OldC5; + +This is complete bullshit - the sample just doesn't change. +Suprising how such an accurate document has such a big mistake in it. +(No really, ITTECH.TXT is suprisingly accurate.) diff --git a/xlibinc/sackit/README.txt b/xlibinc/sackit/README.txt new file mode 100644 index 0000000..2d7ad85 --- /dev/null +++ b/xlibinc/sackit/README.txt @@ -0,0 +1,65 @@ +sackit is an .it format player that strives for sample accuracy. +Currently it's not quite there for any tests, + but does come pretty close ignoring volume ramping. + +IT 2.14p5 is what we're targetting right now. +- IT 2.11 is the first version with a .wav writer. + - It hasn't really changed since the first .raw writer in IT206. +- IT v2.12 introduces anticlick into the .wav writer, which adds complexity. + - As I have found out, it also adjusts the volume ramping timing. + For 44kHz, 2.11 uses 89 samples, while 2.12 uses 111 samples. + This appears to be 1/500 of a second for 2.11 and 1/400 for 2.12. +- IT v2.13 uses logarithmic vol ramping and quadratic interp. + - I didn't really notice much difference with gm-pants.it. +- IT v2.14 COMPLETELY SCREWS EVERYTHING OVER, + as it switches to a cubic-spline *FLOATING-POINT* mixer. + - It also appears to double the volume in mono mode to match stereo mode volumes. +- IT v2.14 patch 3 does LOTS OF THINGS: + - Hey guys, I'm a resonant filter! + - Not much of an issue as I've seen the actual ASM source code Jeff released, + although it's for the A,B,C coefficient stuff, not the actual mixing. + - No really, this isn't much of an issue. The cubic interp needs work, though. + - Although it also buggers up the anticlick a bit. + - "Time accuracy improved"... + - I think this means I can't just do (int)((mixfreq*4)/(tempo*10)). + - ...it might actually refer to the volume ramping being SUBTLY different. +- IT v2.14 patch 4 introduces a 4 band EQ. + - This means more bloody reverse engineering. + - Yes, patch 4 actually existed. I don't have it though, but p5 should be the same. + - Actually, after having done testing, there's VERY little difference. + It might even be a source of variance: + I noticed differences of what I think was exactly one sample, every now and then, + with a very small difference. + +So, the status of things, which is probably out of date: +- Tiny bit of +/- 1 per channel noise (for 2.11 / 2.12). +- Volume ramping isn't quite right (the length is correct for all versions, though. I think.). +- Anticlick is implemented. Not perfect though. +- Haven't quite got the right Amiga base clock, so slides tend to be off slightly. + - This has been improved, but still gets it wrong every now and then. +- Vibrato works perfectly where it doesn't retrigger, at least wrt linear slides. +- IT214/215 decompression IS NOW IMPLEMENTED, YAY +- Sanity checks are lacking - it's pretty easy to crash it. +- Not many effects are implemented. +- Instruments are finally supported, though envelopes need work wrt sample-accuracy. + - NNAs are in place. DCAs are, too, althought they need work. + - Will abort() if you exhaust all 256 virtual channels at the moment. +- RESONANT FILTERS YAAAAAAAAY +- Envelope carry flag is somewhat implemented, with some Compat Gxx mode retrig happening. + +If you'd like to test this, +- see if you can get ImpulseTracker 2.14p5 from somewhere (it214v5.zip) +- open DOSBox, core dynamic, cycles max, it /s20 /m44100 +- open the IT module and play it with that wavewriter +- compare the module and the wav file with ./sackit_compare + +If your test uses IT214-compressed samples (this pertains to testing pre-2.14 versions), +- open it up in IT >=2.14 (it214v5.zip should be fine) +- resave it as "IT2xx" (as opposed to "IT214") + +AUTHORS: + 2012-2013 Ben "GreaseMonkey" Russell. + +LICENCE: + Public domain. I'm sick of people using libmikmod/libmodplug. THEY BOTH SUCK. + diff --git a/xlibinc/sackit/app_compare.c b/xlibinc/sackit/app_compare.c new file mode 100644 index 0000000..0ee2ab4 --- /dev/null +++ b/xlibinc/sackit/app_compare.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include + +#include + +#include "sackit.h" + +#define BUFLEN 1024 + +SDL_Surface *screen = NULL; + +uint32_t palette[4] = { + 0x00FFFFFF, + 0x00FF0000, + 0x0000FF00, + 0x000000FF, +}; + +volatile int sound_ready = 1; +int16_t *sound_buf = NULL; +int16_t *sound_queue = NULL; +int sound_queue_pos = (int)(((unsigned int)-1)>>1); + +void test_sdl_callback(void *userdata, Uint8 *stream, int len) +{ + int offs = 0; + sackit_playback_t *sackit = (sackit_playback_t *)userdata; + int16_t *outbuf = (int16_t *)stream; + int16_t *nvbuf = (int16_t *)sound_buf; + + len /= 2; + + while(offs < len) + { + if(sound_queue_pos < BUFLEN) + { + int xlen = BUFLEN-sound_queue_pos; + if(xlen > len-offs) + xlen = len; + + memcpy(&stream[offs*2], &sound_queue[sound_queue_pos], xlen*2); + sound_queue_pos += xlen; + offs += xlen; + } else { + memcpy(sound_queue, nvbuf, BUFLEN*2); + sound_queue_pos = 0; + sound_ready = 1; + } + } +} + +int main(int argc, char *argv[]) +{ + int x,y,i; + + int argoffs = 1; + int pausemode = 0; + + if(!strcmp(argv[argoffs], "-p")) + { + argoffs++; + pausemode = 1; + } + + it_module_t *module = sackit_module_load(argv[argoffs]); + + if(module == NULL) + return 1; + + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE); + + SDL_WM_SetCaption("sackit IT player", NULL); + screen = SDL_SetVideoMode(800, 600, 32, 0); + + // draw something + uint32_t *pbuf = screen->pixels; + int divpitch = screen->pitch/sizeof(uint32_t); + for(y = 0; y < screen->h; y++) + for(x = 0; x < screen->w; x++) + pbuf[divpitch*y+x] = 0x00000000; + + int16_t *refbuf = (argc > argoffs ? malloc(44100*60*10*2) : NULL); + + for(i = argoffs; i < argc; i++) + { + FILE *fp = fopen(argv[i], "rb"); + if(fgetc(fp) == 'R') + { + fseek(fp, 44, SEEK_SET); // cheat a bit, skip the parsing and go straight to the data + } else { + fseek(fp, 0, SEEK_SET); // not a RIFF. assume raw. + } + + x = 0; + for(;;) + { + y = fgetc(fp); + if(y == -1) + break; + y += fgetc(fp)<<8; + //y ^= 0x8000; + refbuf[x++] = y; + } + fclose(fp); + } + + sackit_playback_t *sackit = sackit_playback_new(module, BUFLEN, 256, MIXER_IT214FC); + + SDL_AudioSpec aspec; + aspec.freq = 44100; + aspec.format = AUDIO_S16SYS; + aspec.channels = 1; + aspec.samples = BUFLEN; + aspec.callback = test_sdl_callback; + sound_buf = calloc(1,BUFLEN*2); + sound_queue = calloc(1,BUFLEN*2); + SDL_OpenAudio(&aspec, NULL); + SDL_PauseAudio(0); + + int refoffs = 0; + + int play_a_sound = 1; + + int quitflag = 0; + while(!quitflag) + { + SDL_Event ev; + while(SDL_PollEvent(&ev)) + switch(ev.type) + { + case SDL_KEYDOWN: + play_a_sound = 1; + break; + case SDL_QUIT: + quitflag = 1; + break; + } + + if(play_a_sound && sound_ready) + { + if(pausemode) + play_a_sound = 0; + sackit_playback_update(sackit); + + // VISUALISE + memset(screen->pixels, 0, screen->pitch*screen->h); + for(x = 0; x < screen->w; x++) + { + int yb = sackit->buf[x]; + int yr = (refbuf == NULL ? 0 : refbuf[x+refoffs]); + + y = 0; + y = (y+0x8000)*screen->h/0x10000; + y = screen->h-1-y; + pbuf[divpitch*y+x] = 0xFFFFFF; + + y = yb; + y = (y+0x8000)*screen->h/0x10000; + y = screen->h-1-y; + pbuf[divpitch*y+x] = 0xFF0000; + //fgetc(fp);fgetc(fp); + + y = yr; + y = (y+0x8000)*screen->h/0x10000; + y = screen->h-1-y; + pbuf[divpitch*y+x] = 0x0000FF; + + y = yr-yb; + if(y < -0x8000) y = -0x8000; + if(y > 0x7FFF) y = 0x7FFF; + //y = (y+0x8000)*screen->h/0x10000; + y += screen->h/2; + y = screen->h-1-y; + if(y < 0) + y = 0; + if(y >= screen->h) + y = screen->h-1; + pbuf[divpitch*y+x] = 0x00FF00; + } + if(refbuf != NULL) + refoffs += sackit->buf_len; + + SDL_Flip(screen); + + int16_t *nvbuf = (int16_t *)sound_buf; + memcpy(nvbuf, sackit->buf, BUFLEN*2); + sound_ready = 0; + } + + SDL_Delay(10); + } + + sackit_playback_free(sackit); + sackit_module_free(module); + + return 0; +} diff --git a/xlibinc/sackit/app_convert.c b/xlibinc/sackit/app_convert.c new file mode 100644 index 0000000..e6fde6a --- /dev/null +++ b/xlibinc/sackit/app_convert.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include + +#include "sackit.h" + +int main(int argc, char *argv[]) +{ + int x,y,i; + + int maxlen = atoi(argv[3]); + + FILE *fp = fopen(argv[2], "wb"); + + if(fp == NULL) + return 1; + + it_module_t *module = sackit_module_load(argv[1]); + + if(module == NULL) + return 1; + + sackit_playback_t *sackit = sackit_playback_new(module, 44100, 256, MIXER_IT214FS); + + for(i = 0; i < maxlen; i++) + { + sackit_playback_update(sackit); + fwrite(sackit->buf, 44100*4, 1, fp); + } + + fclose(fp); + + // + sackit_playback_free(sackit); + sackit_module_free(module); + + return 0; +} + diff --git a/xlibinc/sackit/app_ezjack.c b/xlibinc/sackit/app_ezjack.c new file mode 100644 index 0000000..3c5e2e7 --- /dev/null +++ b/xlibinc/sackit/app_ezjack.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include + +#include "sackit.h" + +#include "ezjack.h" + +int main(int argc, char *argv[]) +{ +#ifdef STATIC_FNAME + it_module_t *module = sackit_module_load(STATIC_FNAME); +#else + it_module_t *module = sackit_module_load(argv[1]); +#endif + + if(module == NULL) + return 1; + + sackit_playback_t *sackit = sackit_playback_new(module, 1024, 256, MIXER_IT214FS); + + ezjack_bundle_t *bun = ezjack_open("sackit", 0, 2, 2048, 44100.0f, 0); + ezjack_activate(bun); + ezjack_autoconnect(bun); + + for(;;) + { + sackit_playback_update(sackit); + ezjack_write(bun, sackit->buf, sackit->buf_len*4, EZJackFormatS16Native); + } + + ezjack_close(bun); + + sackit_playback_free(sackit); + sackit_module_free(module); + + return 0; +} + diff --git a/xlibinc/sackit/app_play.c b/xlibinc/sackit/app_play.c new file mode 100644 index 0000000..ca81d35 --- /dev/null +++ b/xlibinc/sackit/app_play.c @@ -0,0 +1,231 @@ +#include +#include +#include +#include +#include + +#include + +#ifdef __EMSCRIPTEN__ +#include +#endif + +#include "sackit.h" + +#define BUFSIZE 4096 +#define FREQ 48000 + +SDL_Surface *screen = NULL; + +uint32_t palette[4] = { + 0x00FFFFFF, + 0x00FF0000, + 0x0000FF00, + 0x000000FF, +}; + +volatile int sound_ready = 1; +int16_t *sound_buf = NULL; +int16_t *sound_queue = NULL; +int sound_queue_pos = (int)(((unsigned int)-1)>>1); + +#ifdef __EMSCRIPTEN__ +float mozsux_expticks = -99999; +float mozsux_curticks = -1; +#endif + +void test_sdl_callback(void *userdata, Uint8 *stream, int len) +{ + int offs = 0; + sackit_playback_t *sackit = (sackit_playback_t *)userdata; + int16_t *outbuf = (int16_t *)stream; + int16_t *nvbuf = (int16_t *)sound_buf; + + len /= 4; + + while(offs < len) + { + if(sound_queue_pos < BUFSIZE) + { + int xlen = BUFSIZE-sound_queue_pos; + if(xlen > len-offs) + xlen = len; + + memcpy(&stream[offs*4], &sound_queue[sound_queue_pos*2], xlen*4); + sound_queue_pos += xlen; + offs += xlen; + } else { + memcpy(sound_queue, nvbuf, BUFSIZE*4); + sound_queue_pos = 0; + sound_ready = 1; + } + } +} + +int play_a_sound = 1; + +#ifdef __EMSCRIPTEN__ +sackit_playback_t *sackit_glb; +#endif + +int mainloop(sackit_playback_t *sackit) +{ + int x, y; +#ifndef __EMSCRIPTEN__ + uint32_t *pbuf = screen->pixels; + int divpitch = screen->pitch/sizeof(uint32_t); +#endif + + int quitflag = 0; + + SDL_Event ev; +#ifndef __EMSCRIPTEN__ + while(SDL_PollEvent(&ev)) + switch(ev.type) + { + case SDL_KEYDOWN: + play_a_sound = 1; + break; + case SDL_QUIT: + quitflag = 1; + break; + } +#endif + + if(play_a_sound && sound_ready) + { + //play_a_sound = 0; +#ifdef __EMSCRIPTEN__ + mozsux_curticks = SDL_GetTicks(); + if(mozsux_expticks == -99999) + mozsux_expticks = mozsux_curticks - 1000; + if(mozsux_curticks >= mozsux_expticks) + { + //printf("%i\n", mozsux_curticks); + sackit_playback_update(sackit); + + int16_t *nvbuf = (int16_t *)sound_buf; + memcpy(nvbuf, sackit->buf, BUFSIZE*4); + sound_ready = 0; + + mozsux_expticks += 1000.0f*BUFSIZE/(float)(FREQ); + if(mozsux_expticks + 1500.0f < mozsux_curticks) + mozsux_expticks = mozsux_curticks - 1500.0f; + SDL_PauseAudio(0); + } else { + SDL_PauseAudio(1); + } +#else + sackit_playback_update(sackit); + + int16_t *nvbuf = (int16_t *)sound_buf; + memcpy(nvbuf, sackit->buf, BUFSIZE*4); + sound_ready = 0; +#endif + + // VISUALISE +#ifndef __EMSCRIPTEN__ + memset(screen->pixels, 0, screen->pitch*screen->h); + for(x = 0; x < screen->w*2; x++) + { + int yb = sackit->buf[x]; + + if((x&1) == 0) + { + y = 0; + y = (y+0x8000)*screen->h/0x10000; + y = screen->h-1-y; + pbuf[divpitch*y+(x>>1)] = 0xFFFFFF; + } + + y = yb; + y = (y+0x8000)*screen->h/0x10000; + y = screen->h-1-y; + pbuf[divpitch*y+(x>>1)] |= ((x&1) ? 0x0000FF : 0xFF0000); + } + + SDL_Flip(screen); +#endif + } + + return quitflag; +} + +#ifdef __EMSCRIPTEN__ +void mainloop_em() +{ + mainloop(sackit_glb); +} +#endif + +int main(int argc, char *argv[]) +{ + int x,y,i; + +#ifdef STATIC_FNAME + it_module_t *module = sackit_module_load(STATIC_FNAME); +#else + it_module_t *module = sackit_module_load(argv[1]); +#endif + + if(module == NULL) + return 1; + + //SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE); + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_NOPARACHUTE); + + SDL_WM_SetCaption("sackit IT player", NULL); +#ifndef __EMSCRIPTEN__ + screen = SDL_SetVideoMode(800, 600, 32, 0); + + // draw something + uint32_t *pbuf = screen->pixels; + int divpitch = screen->pitch/sizeof(uint32_t); + for(y = 0; y < screen->h; y++) + for(x = 0; x < screen->w; x++) + pbuf[divpitch*y+x] = 0x00000000; +#endif + + sackit_playback_t *sackit = sackit_playback_new2(module, BUFSIZE, 256, fnlist_itmixer[MIXER_IT214FS], 4, FREQ); + //sackit_playback_t *sackit = sackit_playback_new2(module, BUFSIZE, 256, fnlist_itmixer[MIXER_INTFAST_AS], 4, FREQ); + //sackit_playback_t *sackit = sackit_playback_new2(module, BUFSIZE, 256, fnlist_itmixer[MIXER_IT212S], 4, FREQ); + + SDL_AudioSpec aspec; + aspec.freq = FREQ; + aspec.format = AUDIO_S16SYS; + aspec.channels = 2; +#ifdef __EMSCRIPTEN__ + aspec.samples = 512; +#else + aspec.samples = BUFSIZE; +#endif + aspec.callback = test_sdl_callback; + sound_buf = calloc(1,BUFSIZE*4); + sound_queue = calloc(1,BUFSIZE*4); + SDL_OpenAudio(&aspec, NULL); + SDL_PauseAudio(0); + + int refoffs = 0; + +#ifdef __EMSCRIPTEN__ + sackit_glb = sackit; + emscripten_set_main_loop(mainloop_em, 100, 1); +#else + while(!mainloop(sackit)) + { + SDL_Delay(10); + } + + sackit_playback_free(sackit); + sackit_module_free(module); + + free(sound_buf); + free(sound_queue); + + // to help shut valgrind up + SDL_Quit(); + + return 0; +#endif +} + diff --git a/xlibinc/sackit/app_sfx.c b/xlibinc/sackit/app_sfx.c new file mode 100644 index 0000000..3fdd428 --- /dev/null +++ b/xlibinc/sackit/app_sfx.c @@ -0,0 +1,281 @@ +#include +#include +#include +#include +#include + +#include + +#include "sackit.h" + +SDL_Surface *screen = NULL; + +uint32_t palette[4] = { + 0x00FFFFFF, + 0x00FF0000, + 0x0000FF00, + 0x000000FF, +}; + +volatile int sound_ready = 1; +int16_t *sound_buf = NULL; +int16_t *sound_queue = NULL; +int sound_queue_pos = (int)(((unsigned int)-1)>>1); + +void test_sdl_callback(void *userdata, Uint8 *stream, int len) +{ + int offs = 0; + sackit_playback_t *sackit = (sackit_playback_t *)userdata; + int16_t *outbuf = (int16_t *)stream; + int16_t *nvbuf = (int16_t *)sound_buf; + + len /= 4; + + while(offs < len) + { + if(sound_queue_pos < 4096) + { + int xlen = 4096-sound_queue_pos; + if(xlen > len-offs) + xlen = len; + + memcpy(&stream[offs*4], &sound_queue[sound_queue_pos*2], xlen*4); + sound_queue_pos += xlen; + offs += xlen; + } else { + memcpy(sound_queue, nvbuf, 4096*4); + sound_queue_pos = 0; + sound_ready = 1; + } + } +} + +int play_a_sound = 1; + +int mainloop(sackit_playback_t *sackit) +{ + int x, y; + uint32_t *pbuf = screen->pixels; + int divpitch = screen->pitch/sizeof(uint32_t); + + int quitflag = 0; + + SDL_Event ev; + while(SDL_PollEvent(&ev)) + switch(ev.type) + { + case SDL_KEYDOWN: + play_a_sound = 1; + break; + case SDL_QUIT: + quitflag = 1; + break; + } + + if(play_a_sound && sound_ready) + { + //play_a_sound = 0; + sackit_playback_update(sackit); + + int16_t *nvbuf = (int16_t *)sound_buf; + memcpy(nvbuf, sackit->buf, 4096*4); + sound_ready = 0; + + // VISUALISE + memset(screen->pixels, 0, screen->pitch*screen->h); + for(x = 0; x < screen->w*2; x++) + { + int yb = sackit->buf[x]; + + if((x&1) == 0) + { + y = 0; + y = (y+0x8000)*screen->h/0x10000; + y = screen->h-1-y; + pbuf[divpitch*y+(x>>1)] = 0xFFFFFF; + } + + y = yb; + y = (y+0x8000)*screen->h/0x10000; + y = screen->h-1-y; + pbuf[divpitch*y+(x>>1)] |= ((x&1) ? 0x0000FF : 0xFF0000); + } + + SDL_Flip(screen); + } + + return quitflag; +} + +int main(int argc, char *argv[]) +{ + int x,y,i; + + // find IMPM magic + int64_t fboffs = 0; +#ifdef WIN32 + { + char buf[5]; + int toffs; + int img_base; + int16_t numsects; + // TODO: get proper running name! (MSDN will help!) + FILE *fp = fopen(argv[0], "rb"); + fread(buf, 2, 1, fp); + if(buf[0] != 'M' || buf[1] != 'Z') + abort(); + fseek(fp, 0x3C, SEEK_SET); + fread(&toffs, 4, 1, fp); + fseek(fp, toffs, SEEK_SET); + fread(buf, 4, 1, fp); + if(memcmp(buf, "PE\x00\x00", 4)) + abort(); + + // get image base + section count + fseek(fp, 0x06 + toffs, SEEK_SET); + fread(&numsects, 2, 1, fp); + fseek(fp, 0x34 + toffs, SEEK_SET); + fread(&img_base, 2, 1, fp); + + // nosey through the sections + int i; + for(i = 0; i < numsects; i++) + { + int hoffs, hlen; + fseek(fp, 0xF8 + toffs + 0x28*i + 0x08, SEEK_SET); + fread(&hlen, 4, 1, fp); + fseek(fp, 0xF8 + toffs + 0x28*i + 0x14, SEEK_SET); + fread(&hoffs, 4, 1, fp); + printf("sect: %08X, %08X\n", hoffs, hlen); + hoffs += hlen; + if(hlen != 0 && hoffs > fboffs) + fboffs = hoffs; + } + if(fboffs & 0x1FF) + fboffs = (fboffs+0x200)&~0x1FF; + fclose(fp); + } + printf("offset: %016llX\n", fboffs); + it_module_t *module = sackit_module_load_offs(argv[0], fboffs); +#else + { + char buf[5]; + FILE *fp = fopen("/proc/self/exe", "rb"); + fread(buf, 4, 1, fp); + if(!memcmp(buf, "ELF\x7F", 4)) + abort(); + + // we don't care about endianness + // BUT 32/64-bit is important here + int fclass = fgetc(fp); + int is64 = (fclass == 2); + printf("64-bit: %i\n", is64); + // finally, as we're already running the damn thing, the version doesn't matter too much as it's obviously right. + + fseek(fp, is64 ? 0x20 : 0x1C, SEEK_SET); + int64_t phoff = 0; // just in case we have to for some WEIRD reason look up a 64-bit ELF in 32-bit code + int64_t shoff = 0; + // TODO: get 32-bit big endian to work here + // bigger TODO: get sackit to actually WORK on big endian + fread(&phoff, is64 ? 8 : 4, 1, fp); + fread(&shoff, is64 ? 8 : 4, 1, fp); + + fseek(fp, is64 ? 0x36 : 0x2A, SEEK_SET); + int16_t phentsize, phnum; + int16_t shentsize, shnum; + fread(&phentsize, 2, 1, fp); + fread(&phnum, 2, 1, fp); + fread(&shentsize, 2, 1, fp); + fread(&shnum, 2, 1, fp); + + // look through program headers + printf("%016llX\n", phoff); + int i; + for(i = 0; i < phnum; i++) + { + int64_t hoffs, hlen; + hoffs = hlen = 0; + fseek(fp, phoff + i*phentsize + (is64 ? 0x08 : 0x04), SEEK_SET); + fread(&hoffs, is64 ? 8 : 4, 1, fp); + fseek(fp, phoff + i*phentsize + (is64 ? 0x20 : 0x10), SEEK_SET); + fread(&hlen, is64 ? 8 : 4, 1, fp); + printf("prog: %016llX, %016llX\n", hoffs, hlen); + hoffs += hlen; + if(hlen != 0 && hoffs > fboffs) + fboffs = hoffs; + } + + // look through section headers + for(i = 0; i < shnum; i++) + { + int64_t hoffs, hlen; + int flags; + hoffs = hlen = 0; + fseek(fp, shoff + i*shentsize + (is64 ? 0x04 : 0x04), SEEK_SET); + fread(&flags, 4, 1, fp); + fseek(fp, shoff + i*shentsize + (is64 ? 0x18 : 0x10), SEEK_SET); + fread(&hoffs, is64 ? 8 : 4, 1, fp); + fread(&hlen, is64 ? 8 : 4, 1, fp); + printf("sect: %016llX, %016llX%s\n", hoffs, hlen, (flags == 8 ? " (NOBITS)" : "")); + hoffs += hlen; + if((flags != 8) && hlen != 0 && hoffs > fboffs) + fboffs = hoffs; + } + + if(fboffs < shoff + shnum*shentsize) + fboffs = shoff + shnum*shentsize; + + fclose(fp); + } + printf("offset: %016llX\n", fboffs); + it_module_t *module = sackit_module_load_offs("/proc/self/exe", fboffs); +#endif + + + if(module == NULL) + return 1; + + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE); + + SDL_WM_SetCaption("sackit IT player (self-extracting)", NULL); + screen = SDL_SetVideoMode(800, 600, 32, 0); + + // draw something + uint32_t *pbuf = screen->pixels; + int divpitch = screen->pitch/sizeof(uint32_t); + for(y = 0; y < screen->h; y++) + for(x = 0; x < screen->w; x++) + pbuf[divpitch*y+x] = 0x00000000; + + sackit_playback_t *sackit = sackit_playback_new(module, 4096, 256, MIXER_IT214FS); + + SDL_AudioSpec aspec; + aspec.freq = 44100; + aspec.format = AUDIO_S16SYS; + aspec.channels = 2; + aspec.samples = 4096; + aspec.callback = test_sdl_callback; + sound_buf = calloc(1,4096*4); + sound_queue = calloc(1,4096*4); + SDL_OpenAudio(&aspec, NULL); + SDL_PauseAudio(0); + + int refoffs = 0; + + while(!mainloop(sackit)) + { + SDL_Delay(10); + } + + sackit_playback_free(sackit); + sackit_module_free(module); + + free(sound_buf); + free(sound_queue); + + // to help shut valgrind up + SDL_Quit(); + + return 0; +} + diff --git a/xlibinc/sackit/buildwin.sh b/xlibinc/sackit/buildwin.sh new file mode 100644 index 0000000..c1fd10d --- /dev/null +++ b/xlibinc/sackit/buildwin.sh @@ -0,0 +1,3 @@ +#!/bin/sh +make -f Makefile APPSUFFIX=.exe SDL_CFLAGS="-Iwinlibs/ -Iwinlibs/SDL/" SDL_LDFLAGS="-Lwinlibs/ -lmingw32 -lSDLmain -lSDL" CC=mingw32-gcc RANLIB=mingw32-ranlib LIBSACKIT_SO=sackit.dll LIBSACKIT_A=libsackit-w32.a + diff --git a/xlibinc/sackit/cmake/FindJack.cmake b/xlibinc/sackit/cmake/FindJack.cmake new file mode 100644 index 0000000..ddc88dc --- /dev/null +++ b/xlibinc/sackit/cmake/FindJack.cmake @@ -0,0 +1,49 @@ +# Try to find JACK +# This will define the following variables: +# +# JACK_FOUND - Whether Jack was found. +# JACK_INCLUDE_DIRS - Jack include directories. +# JACK_LIBRARIES - Jack libraries. + +include(FindPackageHandleStandardArgs) + +if(JACK_LIBRARIES AND JACK_INCLUDE_DIRS) + + # in cache already + set(JACK_FOUND TRUE) + +else() + + find_package(PkgConfig) + if(PKG_CONFIG_FOUND) + pkg_check_modules(_JACK jack) + endif(PKG_CONFIG_FOUND) + + find_path(JACK_INCLUDE_DIR + NAMES + jack/jack.h + PATHS + ${_JACK_INCLUDEDIR} + ) + + find_library(JACK_LIBRARY + NAMES + jack + PATHS + ${_JACK_LIBDIR} + ) + + set(JACK_INCLUDE_DIRS + ${JACK_INCLUDE_DIR} + ) + + set(JACK_LIBRARIES + ${JACK_LIBRARY} + ) + + find_package_handle_standard_args(Jack DEFAULT_MSG JACK_LIBRARIES JACK_INCLUDE_DIRS) + + # show the JACK_INCLUDE_DIRS and JACK_LIBRARIES variables only in the advanced view + mark_as_advanced(JACK_INCLUDE_DIR JACK_LIBRARY JACK_INCLUDE_DIRS JACK_LIBRARIES) + +endif() diff --git a/xlibinc/sackit/cmake/FindSDL2.cmake b/xlibinc/sackit/cmake/FindSDL2.cmake new file mode 100644 index 0000000..236d6b4 --- /dev/null +++ b/xlibinc/sackit/cmake/FindSDL2.cmake @@ -0,0 +1,163 @@ +# Locate SDL2 library +# This module defines +# SDL2_LIBRARY, the name of the library to link against +# SDL2_FOUND, if false, do not try to link to SDL2 +# SDL2_INCLUDE_DIR, where to find SDL.h +# +# This module responds to the the flag: +# SDL2_BUILDING_LIBRARY +# If this is defined, then no SDL2main will be linked in because +# only applications need main(). +# Otherwise, it is assumed you are building an application and this +# module will attempt to locate and set the the proper link flags +# as part of the returned SDL2_LIBRARY variable. +# +# Don't forget to include SDLmain.h and SDLmain.m your project for the +# OS X framework based version. (Other versions link to -lSDL2main which +# this module will try to find on your behalf.) Also for OS X, this +# module will automatically add the -framework Cocoa on your behalf. +# +# +# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration +# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library +# (SDL2.dll, libsdl2.so, SDL2.framework, etc). +# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. +# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value +# as appropriate. These values are used to generate the final SDL2_LIBRARY +# variable, but when these values are unset, SDL2_LIBRARY does not get created. +# +# +# $SDL2DIR is an environment variable that would +# correspond to the ./configure --prefix=$SDL2DIR +# used in building SDL2. +# l.e.galup 9-20-02 +# +# Modified by Eric Wing. +# Added code to assist with automated building by using environmental variables +# and providing a more controlled/consistent search behavior. +# Added new modifications to recognize OS X frameworks and +# additional Unix paths (FreeBSD, etc). +# Also corrected the header search path to follow "proper" SDL guidelines. +# Added a search for SDL2main which is needed by some platforms. +# Added a search for threads which is needed by some platforms. +# Added needed compile switches for MinGW. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of +# SDL2_LIBRARY to override this selection or set the CMake environment +# CMAKE_INCLUDE_PATH to modify the search paths. +# +# Note that the header path has changed from SDL2/SDL.h to just SDL.h +# This needed to change because "proper" SDL convention +# is #include "SDL.h", not . This is done for portability +# reasons because not all systems place things in SDL2/ (see FreeBSD). + +#============================================================================= +# Copyright 2003-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +SET(SDL2_SEARCH_PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) + +FIND_PATH(SDL2_INCLUDE_DIR SDL.h + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES include/SDL2 include + PATHS ${SDL2_SEARCH_PATHS} +) + +FIND_LIBRARY(SDL2_LIBRARY_TEMP + NAMES SDL2 + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS ${SDL2_SEARCH_PATHS} +) + +IF(NOT SDL2_BUILDING_LIBRARY) + IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDL2main for compatibility even though they don't + # necessarily need it. + FIND_LIBRARY(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS ${SDL2_SEARCH_PATHS} + ) + ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") +ENDIF(NOT SDL2_BUILDING_LIBRARY) + +# SDL2 may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + +# MinGW needs an additional library, mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows +# (Actually on second look, I think it only needs one of the m* libraries.) +IF(MINGW) + SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") +ENDIF(MINGW) + +IF(SDL2_LIBRARY_TEMP) + # For SDL2main + IF(NOT SDL2_BUILDING_LIBRARY) + IF(SDL2MAIN_LIBRARY) + SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(SDL2MAIN_LIBRARY) + ENDIF(NOT SDL2_BUILDING_LIBRARY) + + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + IF(APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") + ENDIF(APPLE) + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + IF(NOT APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(NOT APPLE) + + # For MinGW library + IF(MINGW) + SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(MINGW) + + # Set the final string here so the GUI reflects the final state. + SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") + # Set the temp variable to INTERNAL so it is not seen in the CMake GUI + SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") +ENDIF(SDL2_LIBRARY_TEMP) + +INCLUDE(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) diff --git a/xlibinc/sackit/effects.c b/xlibinc/sackit/effects.c new file mode 100644 index 0000000..e7477a4 --- /dev/null +++ b/xlibinc/sackit/effects.c @@ -0,0 +1,506 @@ +#include "sackit_internal.h" + +uint32_t sackit_pitchslide_linear(uint32_t freq, int16_t amt) +{ + uint32_t slidemul; + + if(amt == 0) + return freq; + + if(amt < 0) + { + slidemul = (uint32_t)linear_slide_down_table[-amt]; + } else { + slidemul = (uint32_t)linear_slide_up_table[amt*2+1]; + slidemul <<= 16; + slidemul += (uint32_t)linear_slide_up_table[amt*2]; + } + + uint32_t r = sackit_mul_fixed_16_int_32(slidemul, freq); + + //printf("slide %i\n", r); + + return r; +} + +uint32_t sackit_pitchslide_linear_fine(uint32_t freq, int16_t amt) +{ + uint32_t slidemul; + + if(amt == 0) + return freq; + + if(amt < 0) + { + slidemul = (uint32_t)fine_linear_slide_down_table[-amt]; + } else { + slidemul = (uint32_t)fine_linear_slide_up_table[amt*2+1]; + slidemul <<= 16; + slidemul += (uint32_t)fine_linear_slide_up_table[amt*2]; + } + + uint32_t r = sackit_mul_fixed_16_int_32(slidemul, freq); + + //printf("slFde %i\n", r); + + return r; +} + +uint32_t sackit_pitchslide_amiga_fine(uint32_t freq, int16_t amt) +{ + if(amt == 0) + return freq; + + uint32_t r = AMICLK/(AMICLK/((int64_t)freq) - ((int64_t)amt)*AMIMUL); + + //printf("ami %i\n", r); + + return r; +} + +void sackit_effect_volslide(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int8_t amt) +{ + if(amt < 0) + { + pchn->vol = (pchn->vol < -amt + ? 0 + : pchn->vol+amt); + } else if(amt > 0) { + pchn->vol = (pchn->vol+amt > 64 + ? 64 + : pchn->vol+amt); + } + + if(pchn->achn != NULL) + pchn->achn->vol = pchn->vol; +} + +void sackit_effect_volslide_cv(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int8_t amt) +{ + if(amt < 0) + { + pchn->cv = (pchn->cv < -amt + ? 0 + : pchn->cv+amt); + } else if(amt > 0) { + pchn->cv = (pchn->cv+amt > 64 + ? 64 + : pchn->cv+amt); + } + + if(pchn->achn != NULL) + pchn->achn->cv = pchn->cv; +} + +void sackit_effect_volslide_gv(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int8_t amt) +{ + if(amt < 0) + { + sackit->gv = (sackit->gv < -amt + ? 0 + : sackit->gv+amt); + } else if(amt > 0) { + sackit->gv = (sackit->gv+amt > 128 + ? 128 + : sackit->gv+amt); + } +} + +void sackit_effect_pitchslide(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int16_t amt) +{ + if(amt == 0 || pchn->freq == 0) + return; + + // TODO confirm this behaviour + + if(sackit->module->header.flags & IT_MOD_LINEAR) + { + pchn->freq = sackit_pitchslide_linear(pchn->freq, amt); + } else { + pchn->freq = sackit_pitchslide_amiga_fine(pchn->freq, amt*4); + } + + if(pchn->achn != NULL) + pchn->achn->freq = pchn->freq; + + //printf("%i %i\n", pchn->achn->freq, pchn->achn->flags); +} + +void sackit_effect_pitchslide_fine(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int16_t amt) +{ + if(amt == 0 || pchn->freq == 0) + return; + + // TODO confirm this behaviour + + if(sackit->module->header.flags & IT_MOD_LINEAR) + { + pchn->freq = sackit_pitchslide_linear_fine(pchn->freq, amt); + } else { + pchn->freq = sackit_pitchslide_amiga_fine(pchn->freq, amt); + } + + if(pchn->achn != NULL) + pchn->achn->freq = pchn->freq; +} + +void sackit_effect_portaslide(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int16_t amt) +{ + if(amt == 0 || (uint32_t)pchn->freq == pchn->tfreq || pchn->freq == 0) + return; + + if((uint32_t)pchn->achn->freq < pchn->tfreq) + { + sackit_effect_pitchslide(sackit, pchn, amt); + // TODO: confirm if > or >= + if((uint32_t)pchn->achn->freq >= pchn->tfreq) + pchn->nfreq = pchn->freq = pchn->tfreq; + } else { + sackit_effect_pitchslide(sackit, pchn, -amt); + // TODO: confirm if < or <= + if((uint32_t)pchn->achn->freq <= pchn->tfreq) + pchn->nfreq = pchn->freq = pchn->tfreq; + } + + if(pchn->achn != NULL) + pchn->achn->freq = pchn->freq; + + //printf("%i\n", pchn->achn->freq); +} + +void sackit_effect_vibrato_nooffs(sackit_playback_t *sackit, sackit_pchannel_t *pchn) +{ + int32_t v; + + if(pchn->achn == NULL || pchn->achn->ofreq == 0 || pchn->vib_speed == 0) + return; + + //if(pchn->achn->vol == 0 || !(pchn->achn->flags & SACKIT_ACHN_PLAYING)) + if(!(pchn->achn->flags & SACKIT_ACHN_PLAYING)) + return; + + uint8_t offs = (uint8_t)pchn->vib_offs; + + switch(pchn->vib_type&3) + { + case 0: // sine + v = fine_sine_data[offs]; + break; + case 1: // ramp down + v = fine_ramp_down_data[offs]; + break; + case 2: // square + v = fine_square_wave[offs]; + break; + case 3: // random - NOT EASILY TESTABLE + // TODO! + v = 0; + break; + } + + /* + chan_sj.it: + 12464 8 + 12532 14 + 12554 16 + 12600 20 + 12554 16 + 12532 14 + 12464 8 + 12375 0 + 12341 -7 + 12219 -14 + 12197 -16 + 12153 -20 + 12197 -16 + 12219 -14 + 12341 -7 + 12375 0 + */ + + v = v*pchn->vib_depth; + if(sackit->module->header.flags & IT_MOD_OLDFX) + v = ~(v<<1); + int negdepth = (v < 0); + if(negdepth) + v = ~v; + v = (v+32)>>6; + if(negdepth) v = -v; + + if(sackit->module->header.flags & IT_MOD_LINEAR) + { + if(v >= -15 && v <= 15) + { + pchn->achn->ofreq = sackit_pitchslide_linear_fine(pchn->achn->ofreq, v); + } else { + // compensating that i have no separate slide up/down function + pchn->achn->ofreq = sackit_pitchslide_linear(pchn->achn->ofreq + , (negdepth ? -((-v)>>2): v>>2)); + } + } else { + pchn->achn->ofreq = sackit_pitchslide_amiga_fine(pchn->achn->ofreq, v); + } + + //printf("v %i %i\n", pchn->achn->ofreq, v); +} + +void sackit_effect_vibrato(sackit_playback_t *sackit, sackit_pchannel_t *pchn) +{ + int32_t v; + + if(pchn->achn == NULL || pchn->achn->ofreq == 0 || pchn->vib_speed == 0) + return; + + //if(pchn->achn->vol == 0 || !(pchn->achn->flags & SACKIT_ACHN_PLAYING)) + if(!(pchn->achn->flags & SACKIT_ACHN_PLAYING)) + return; + + // vibrato starts IMMEDIATELY. + pchn->vib_offs += pchn->vib_speed; + + // apply. + sackit_effect_vibrato_nooffs(sackit, pchn); +} + +void sackit_effect_tremolo_nooffs(sackit_playback_t *sackit, sackit_pchannel_t *pchn) +{ + int32_t v; + + if(pchn->achn == NULL || pchn->achn->ofreq == 0 || pchn->tre_speed == 0) + return; + + if(!(pchn->achn->flags & SACKIT_ACHN_PLAYING)) + return; + + uint8_t offs = (uint8_t)pchn->tre_offs; + + switch(pchn->vib_type&3) + { + case 0: // sine + v = fine_sine_data[offs]; + break; + case 1: // ramp down + v = fine_ramp_down_data[offs]; + break; + case 2: // square + v = fine_square_wave[offs]; + break; + case 3: // random - NOT EASILY TESTABLE + // TODO! + v = 0; + break; + } + + v = v*pchn->tre_depth; + //if(sackit->module->header.flags & IT_MOD_OLDFX) + // v = ~(v<<1); + int negdepth = (v < 0); + if(negdepth) + v = ~v; + v = (v+64)>>7; + if(negdepth) v = -v; + + v += pchn->achn->vol; + if(v < 0) v = 0; + if(v > 64) v = 64; + pchn->achn->vol = v; +} + +void sackit_effect_tremolo(sackit_playback_t *sackit, sackit_pchannel_t *pchn) +{ + int32_t v; + + if(pchn->achn == NULL || pchn->achn->ofreq == 0 || pchn->tre_speed == 0) + return; + + if(!(pchn->achn->flags & SACKIT_ACHN_PLAYING)) + return; + + pchn->tre_offs += pchn->tre_speed; + + // apply. + sackit_effect_tremolo_nooffs(sackit, pchn); +} + +void sackit_effect_tremor(sackit_playback_t *sackit, sackit_pchannel_t *pchn) +{ + if(!(pchn->trm_flags & 1)) + return; + + if(pchn->trm_flags & 2) + { + if(pchn->trm_cur_off == 0) + { + pchn->trm_cur_off = pchn->trm_val&15; + if(pchn->trm_cur_off == 0 || (sackit->module->header.flags & IT_MOD_OLDFX)) + pchn->trm_cur_off++; + } + + if(pchn->achn != NULL) + pchn->achn->vol = 0; + + pchn->trm_cur_off--; + if(pchn->trm_cur_off == 0) + pchn->trm_flags &= ~2; + } else { + if(pchn->trm_cur_on == 0) + { + pchn->trm_cur_on = pchn->trm_val>>4; + if(pchn->trm_cur_on == 0 || (sackit->module->header.flags & IT_MOD_OLDFX)) + pchn->trm_cur_on++; + } + pchn->trm_cur_on--; + if(pchn->trm_cur_on == 0) + pchn->trm_flags |= 2; + } +} + +void sackit_effect_retrig(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int first_note) +{ + // TODO: confirm th + if(!(pchn->rtg_flags&1)) + return; + //printf("%02X %i\n", pchn->rtg_val, pchn->rtg_counter); + + if(pchn->rtg_counter == 0) + { + pchn->rtg_counter = pchn->rtg_val&15; + if(pchn->rtg_counter == 0) + pchn->rtg_counter++; + + if(!first_note) + { + sackit_note_retrig(sackit, pchn, 0); + + // TODO: work out rounding from * and / volslides + switch(pchn->rtg_val>>4) + { + case 0x1: + pchn->vol -= 1; + break; + case 0x2: + pchn->vol -= 2; + break; + case 0x3: + pchn->vol -= 4; + break; + case 0x4: + pchn->vol -= 8; + break; + case 0x5: + pchn->vol -= 16; + break; + + case 0x6: + pchn->vol = (pchn->vol*2+1)/3; + break; + case 0x7: + pchn->vol = (pchn->vol+1)/2; + break; + + case 0x9: + pchn->vol += 1; + break; + case 0xA: + pchn->vol += 2; + break; + case 0xB: + pchn->vol += 4; + break; + case 0xC: + pchn->vol += 8; + break; + case 0xD: + pchn->vol += 16; + break; + + case 0xE: + pchn->vol = (pchn->vol*3+1)/2; + break; + case 0xF: + pchn->vol *= 2; + break; + } + + if(((int8_t)pchn->vol) < 0) + pchn->vol = 0; + if(pchn->vol > 64) + pchn->vol = 64; + + if(pchn->achn != NULL) + pchn->achn->vol = pchn->vol; + } + } + pchn->rtg_counter--; +} + +void sackit_effect_samplevibrato(sackit_playback_t *sackit, sackit_achannel_t *achn) +{ + if(achn == NULL || achn->ofreq == 0) + return; + + // from ITTECH.TXT: + // Every processing cycle, the following occurs: + // 1) Mov AX, [SomeVariableNameRelatingToVibrato] + // 2) Add AL, Rate + // 3) AdC AH, 0 + // 4) AH contains the depth of the vibrato as a fine-linear slide. + // 5) Mov [SomeVariableNameRelatingToVibrato], AX ; For the next cycle. + if(achn->svib_power < 0xFF00) + achn->svib_power += achn->svib_rate; + if(achn->svib_power > (achn->svib_depth<<8)) + achn->svib_power = (achn->svib_depth<<8); + + achn->svib_offs += achn->svib_speed; + + // TODO: determine exact slide! + int32_t v; + + //if(pchn->achn->vol == 0 || !(pchn->achn->flags & SACKIT_ACHN_PLAYING)) + if(!(achn->flags & SACKIT_ACHN_PLAYING)) + return; + + uint8_t offs = (uint8_t)achn->svib_offs; + + switch(achn->svib_type&3) + { + case 0: // sine + v = fine_sine_data[offs]; + break; + case 1: // ramp down + v = fine_ramp_down_data[offs]; + break; + case 2: // square + v = fine_square_wave[offs]; + break; + case 3: // random - NOT EASILY TESTABLE + // TODO! + v = 0; + break; + } + + //v = (v*((achn->svib_depth*achn->svib_power)>>11)); + + // closest: + //v = (v*((achn->svib_power>>8)*(((achn->svib_depth)>>3)))); + v = (v*(achn->svib_power>>8)); + v -= 32; + + // TODO: check if old effects affects sample vibrato + int negdepth = (v < 0); + if(negdepth) + v = ~v; + v = (v+(1<<5))>>6; + if(negdepth) v = -v; + + if(v >= -15 && v <= 15) + { + achn->ofreq = sackit_pitchslide_linear_fine(achn->ofreq, v); + } else { + // compensating that i have no separate slide up/down function + achn->ofreq = sackit_pitchslide_linear(achn->ofreq + , (negdepth ? -((-v)>>2): v>>2)); + } + //if(achn == (sackit->pchn[0].achn)) + // printf("%i %04X %i\n",achn->ofreq, achn->svib_power, v); +} diff --git a/xlibinc/sackit/ezjack.c b/xlibinc/sackit/ezjack.c new file mode 100644 index 0000000..5ea9e5a --- /dev/null +++ b/xlibinc/sackit/ezjack.c @@ -0,0 +1,457 @@ +/* +EZJACK: a simple wrapper for JACK to make stuff a bit easier +Copyright (c) Ben "GreaseMonkey" Russell, 2014. + +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. +*/ + +#include +#include +#include +#include +#include + +#include /* usleep(3) */ + +#include +#include + +#include "ezjack.h" + +static volatile jack_status_t lasterr = 0; +static volatile EZJackCallback maincb = NULL; + +static int _helper_get_fmt_size(ezjack_format_t fmt) +{ + switch(fmt) + { + case EZJackFormatFloat32Native: + case EZJackFormatFloat32LE: + case EZJackFormatFloat32BE: + return 4; + + case EZJackFormatU8: + case EZJackFormatS8: + return 1; + + case EZJackFormatS16Native: + case EZJackFormatS16LE: + case EZJackFormatS16BE: + case EZJackFormatU16Native: + case EZJackFormatU16LE: + case EZJackFormatU16BE: + return 2; + } + + return 0; // invalid +} + +jack_status_t ezjack_get_error(void) +{ + // FIXME: possibly not thread-safe! + jack_status_t ret = lasterr; + lasterr = 0; + return ret; +} + +int ezjack_set_callback(EZJackCallback cb) +{ + maincb = cb; + return 0; +} + +int ezjack_default_callback(jack_nframes_t nframes, void *arg) +{ + int i, j; + int ret = 0; + ezjack_bundle_t *bun = (ezjack_bundle_t *)arg; + + // Sample rate + float sfreq = jack_get_sample_rate(bun->client); + + float convgrad = (bun->freq/sfreq) - 0.00001f; + int convsize = nframes * convgrad; + + if(bun->fbuflen != convsize) + { + bun->fbuflen = convsize; + bun->fbuf = realloc(bun->fbuf, convsize*sizeof(float)); + } + +#define HELPER_CALLBACK_GETSPACE(varname, vardef, foorb, foocount, jack_ringbuffer_foo_space) \ + int varname = sizeof(float) * (vardef); \ + for(i = 0; i < bun->portstack.foocount; i++) \ + { \ + int cspace = (int)jack_ringbuffer_foo_space(bun->portstack.foorb[i]); \ + \ + if(cspace < varname) \ + varname = cspace; \ + } \ + + // Get space for writing to input ringbuffer + //HELPER_CALLBACK_GETSPACE(minreadspace, bun->bufsize, inrb, incount, jack_ringbuffer_write_space); + + // Inputs + for(i = 0; i < bun->portstack.incount; i++) + { + jack_port_t *p = bun->portstack.in[i]; + jack_ringbuffer_t *rb = bun->portstack.inrb[i]; + float *buf = jack_port_get_buffer(p, nframes); + + // TODO: support other interpolations + int k = 0; + for(j = 0; j < nframes; j++) + for(; k < (j+1)*convgrad; k++) + bun->fbuf[k] = buf[j]; + + // An overrun can happen - in this case, it's the app's fault + jack_ringbuffer_write(rb, (char *)(bun->fbuf), convsize*sizeof(float)); + } + + // Get space for writing to output ringbuffer + HELPER_CALLBACK_GETSPACE(minwriteoutspace, bun->bufsize, outrb, outcount, jack_ringbuffer_write_space); + + // Get space for reading from input ringbuffer + HELPER_CALLBACK_GETSPACE(minreadinspace, bun->bufsize, inrb, incount, jack_ringbuffer_read_space); + + // Call our callback + EZJackCallback cb = maincb; + if(cb != NULL) + // TODO: input + ret = cb(minreadinspace/sizeof(float), minwriteoutspace/sizeof(float), bun); + + // Get space for reading from output ringbuffer + HELPER_CALLBACK_GETSPACE(minoutspace, convsize, outrb, outcount, jack_ringbuffer_read_space); + + // Outputs + for(i = 0; i < bun->portstack.outcount; i++) + { + jack_port_t *p = bun->portstack.out[i]; + jack_ringbuffer_t *rb = bun->portstack.outrb[i]; + float *buf = jack_port_get_buffer(p, nframes); + + jack_ringbuffer_read(rb, (char *)(bun->fbuf), minoutspace); + + // TODO: support other interpolations + for(j = 0; j < nframes; j++) + buf[j] = bun->fbuf[(int)(j*convgrad)]; + } + + return ret; +} + +ezjack_bundle_t *ezjack_open(const char *client_name, int inputs, int outputs, int bufsize, float freq, ezjack_portflags_t flags) +{ + int i; + ezjack_bundle_t bun; + char namebuf[16]; + + // Open client + jack_status_t temperr = lasterr; + bun.client = jack_client_open(client_name, JackNoStartServer, &temperr); + lasterr = temperr; + + if(bun.client == NULL) + return NULL; + + bun.freq = freq; + bun.bufsize = bufsize; + bun.fbuflen = 0; + bun.fbuf = NULL; + + // Create some ports + bun.portstack.incount = 0; + bun.portstack.outcount = 0; + +#define HELPER_OPEN_PORTS(foo, fooputs, foocount, foorb, foobuf, foofmt, flags) \ + for(i = 0; i < fooputs; i++) \ + { \ + snprintf(namebuf, 16, foofmt, i+1); \ + bun.portstack.foo[i] = jack_port_register(bun.client, namebuf, JACK_DEFAULT_AUDIO_TYPE, flags, bufsize); \ + if(bun.portstack.foo[i] == NULL) \ + { \ + lasterr = JackFailure; \ + jack_client_close(bun.client); \ + return NULL; \ + } \ + \ + bun.portstack.foorb[i] = jack_ringbuffer_create(bufsize*sizeof(float)); \ + bun.portstack.foobuf[i] = malloc(bufsize*sizeof(float)); \ + \ + bun.portstack.foocount++; \ + } + + HELPER_OPEN_PORTS(in, inputs, incount, inrb, inbuf, "in_%i", JackPortIsInput); + HELPER_OPEN_PORTS(out, outputs, outcount, outrb, outbuf, "out_%i", JackPortIsOutput); + +#undef HELPER_OPEN_PORTS + + // Prepare our bundle + ezjack_bundle_t *ret = malloc(sizeof(ezjack_bundle_t)); + memcpy(ret, &bun, sizeof(ezjack_bundle_t)); + + // Set callback + // FIXME: error needs to be acted upon + jack_set_process_callback(bun.client, ezjack_default_callback, ret); + + return ret; +} + +int ezjack_autoconnect(ezjack_bundle_t *bun) +{ + int i; + + // Find ports + // If we can't find any physical ports, don't worry. + // If a connection fails, don't worry either. +#define HELPER_FIND_PORTS(foo, foocount, foonames, foopattern, fooflags, footo, foofrom) \ + if(bun->portstack.foocount > 0) \ + { \ + const char **foonames = jack_get_ports(bun->client, foopattern, JACK_DEFAULT_AUDIO_TYPE, fooflags|JackPortIsPhysical); \ + if(foonames != NULL) \ + { \ + i = 0; \ + while(foonames[i] != NULL) \ + { \ + jack_connect(bun->client, foofrom, footo); \ + i++; \ + } \ + } \ + } \ + + HELPER_FIND_PORTS(in, incount, innames, ".*:capture_.*", JackPortIsOutput, jack_port_name(bun->portstack.in[i % bun->portstack.incount]), innames[i]); + HELPER_FIND_PORTS(out, outcount, outnames, ".*:playback_.*", JackPortIsInput, outnames[i], jack_port_name(bun->portstack.out[i % bun->portstack.outcount])); + +#undef HELPER_FIND_PORTS + + return 0; +} + +void ezjack_close(ezjack_bundle_t *bun) +{ + int i; + + jack_deactivate(bun->client); + + for(i = 0; i < bun->portstack.incount; i++) + { + jack_ringbuffer_free(bun->portstack.inrb[i]); + free(bun->portstack.inbuf[i]); + } + + for(i = 0; i < bun->portstack.outcount; i++) + { + jack_ringbuffer_free(bun->portstack.outrb[i]); + free(bun->portstack.outbuf[i]); + } + + if(bun->fbuf != NULL) + free(bun->fbuf); + + jack_client_close(bun->client); + + free(bun); +} + +int ezjack_activate(ezjack_bundle_t *bun) +{ + return jack_activate(bun->client); +} + +int ezjack_deactivate(ezjack_bundle_t *bun) +{ + return jack_deactivate(bun->client); +} + +int ezjack_read(ezjack_bundle_t *bun, void *buf, int len, ezjack_format_t fmt) +{ + int i, j; + + int fmtsize = _helper_get_fmt_size(fmt); // FIXME: handle erroneous format + int blockalign = bun->portstack.incount * fmtsize; + if(len % blockalign != 0) abort(); // FIXME: do this more gracefully + int reqlen = len/blockalign; + + while(reqlen > 0) + { + int minspace = reqlen * sizeof(float); + + // Get smallest space count + for(i = 0; i < bun->portstack.incount; i++) + { + int cspace = (int)jack_ringbuffer_read_space(bun->portstack.inrb[i]); + + if(cspace < minspace) + minspace = cspace; + } + + minspace /= sizeof(float); + //fprintf(stderr, "minspace %i\n", minspace); + + // Read from ring buffers + if(minspace > 0) + { + // Fetch data + for(i = 0; i < bun->portstack.incount; i++) + { + // FIXME: handle the case where this returns something wrong + jack_ringbuffer_read(bun->portstack.inrb[i], (char *)(bun->portstack.inbuf[i]), minspace*sizeof(float)); + } + + // Read from temporaries + // FIXME: handle all formats +#define HELPER_READ_FORMAT(typ, low, high, wrap) \ + for(j = 0; j < minspace; j++) \ + for(i = 0; i < bun->portstack.incount; i++) \ + { \ + float v = (bun->portstack.inbuf[i][j]); \ + \ + *(typ *)buf = (v <= -1.0f ? low : \ + (v >= 1.0f ? high : (typ)(wrap)) \ + ); \ + \ + buf += sizeof(typ); \ + } \ + + switch(fmt) + { + case EZJackFormatFloat32LE: + case EZJackFormatFloat32Native: + HELPER_READ_FORMAT(float, -1.0f, 1.0f, v); + break; + + case EZJackFormatU8: + HELPER_READ_FORMAT(uint8_t, 0, 255, (v+1.0f)*127.0f); + break; + + case EZJackFormatS8: + HELPER_READ_FORMAT(int8_t, -128, 127, v*127.0f); + break; + + case EZJackFormatS16Native: + case EZJackFormatS16LE: + HELPER_READ_FORMAT(uint16_t, -32768, 32767, v*32767.0f); + break; + + case EZJackFormatU16Native: + case EZJackFormatU16LE: + HELPER_READ_FORMAT(int16_t, 0, 65535, (v+1.0f)*32767.0f); + break; + + } + +#undef HELPER_READ_FORMAT + + reqlen -= minspace; + } + + // Sleep a bit. + // TODO: use a notify system + usleep(1000); + } + + return len; +} + +// TODO: nonblocking version +int ezjack_write(ezjack_bundle_t *bun, void *buf, int len, ezjack_format_t fmt) +{ + int i, j; + + int fmtsize = _helper_get_fmt_size(fmt); // FIXME: handle erroneous format + int blockalign = bun->portstack.outcount * fmtsize; + if(len % blockalign != 0) abort(); // FIXME: do this more gracefully + int reqlen = len/blockalign; + + while(reqlen > 0) + { + int minspace = reqlen * sizeof(float); + + // Get smallest space count + for(i = 0; i < bun->portstack.outcount; i++) + { + int cspace = (int)jack_ringbuffer_write_space(bun->portstack.outrb[i]); + + if(cspace < minspace) + minspace = cspace; + } + + minspace /= sizeof(float); + + // Write to ring buffers + if(minspace > 0) + { + // Write to temporaries + // FIXME: handle all formats +#define HELPER_WRITE_FORMAT(inc, wrap) \ + for(j = 0; j < minspace; j++) \ + for(i = 0; i < bun->portstack.outcount; i++) \ + { \ + bun->portstack.outbuf[i][j] = wrap; \ + buf += inc; \ + } \ + + switch(fmt) + { + case EZJackFormatFloat32LE: + case EZJackFormatFloat32Native: + HELPER_WRITE_FORMAT(sizeof(float), *(float *)buf); + break; + + case EZJackFormatU8: + HELPER_WRITE_FORMAT(1, ((float)*(uint8_t *)buf)/128.0f-1.0f); + break; + + case EZJackFormatS8: + HELPER_WRITE_FORMAT(1, ((float)*(int8_t *)buf)/128.0f); + break; + + case EZJackFormatS16Native: + case EZJackFormatS16LE: + HELPER_WRITE_FORMAT(2, ((float)*(int16_t *)buf)/32768.0f); + break; + + case EZJackFormatU16Native: + case EZJackFormatU16LE: + HELPER_WRITE_FORMAT(2, ((float)*(uint16_t *)buf)/32768.0f-1.0f); + break; + + } + +#undef HELPER_WRITE_FORMAT + + // Commit data + for(i = 0; i < bun->portstack.outcount; i++) + { + // FIXME: handle the case where this returns something wrong + jack_ringbuffer_write(bun->portstack.outrb[i], (char *)(bun->portstack.outbuf[i]), minspace*sizeof(float)); + } + + reqlen -= minspace; + } + + // Sleep a bit. + // TODO: use a notify system + usleep(1000); + } + + return len; +} + + diff --git a/xlibinc/sackit/ezjack.h b/xlibinc/sackit/ezjack.h new file mode 100644 index 0000000..7f9bdf4 --- /dev/null +++ b/xlibinc/sackit/ezjack.h @@ -0,0 +1,94 @@ +/* +EZJACK: a simple wrapper for JACK to make stuff a bit easier +Copyright (c) Ben "GreaseMonkey" Russell, 2014. + +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. +*/ + +#ifndef _EZJACK_H_ +#define _EZJACK_H_ +#include +#include + +typedef enum EZJackFormats +{ + EZJackFormatFloat32Native, + EZJackFormatFloat32LE, + EZJackFormatFloat32BE, + + EZJackFormatU8, + EZJackFormatS8, + + EZJackFormatS16Native, + EZJackFormatS16LE, + EZJackFormatS16BE, + + EZJackFormatU16Native, + EZJackFormatU16LE, + EZJackFormatU16BE, +} ezjack_format_t; + +typedef enum EZJackPortFlags +{ + // Nothing here yet. + ThisOnlyExistsBecauseCDoesntLikeAnEmptyEnum, +} ezjack_portflags_t; + +#define EZJACK_PORTSTACK_MAX 32 +#define EZJACK_RB_SIZE ((1<<17)*sizeof(float)) +typedef struct EZJackPortStack +{ + int incount, outcount; + jack_port_t *in[EZJACK_PORTSTACK_MAX]; + jack_port_t *out[EZJACK_PORTSTACK_MAX]; + jack_ringbuffer_t *inrb[EZJACK_PORTSTACK_MAX]; + jack_ringbuffer_t *outrb[EZJACK_PORTSTACK_MAX]; + + // leave this section alone + float *inbuf[EZJACK_PORTSTACK_MAX]; + float *outbuf[EZJACK_PORTSTACK_MAX]; +} ezjack_portstack_t; + +typedef struct EZJackBundle +{ + jack_client_t *client; + ezjack_portstack_t portstack; + int bufsize; + float freq; + + // leave this section alone + float *fbuf; + int fbuflen; +} ezjack_bundle_t; + +typedef int (*EZJackCallback)(int nframes_in, int nframes_out, ezjack_bundle_t *bun); + +jack_status_t ezjack_get_error(void); + +ezjack_bundle_t *ezjack_open(const char *client_name, int inputs, int outputs, int bufsize, float freq, ezjack_portflags_t flags); +int ezjack_autoconnect(ezjack_bundle_t *bun); +void ezjack_close(ezjack_bundle_t *bun); +int ezjack_activate(ezjack_bundle_t *bun); +int ezjack_deactivate(ezjack_bundle_t *bun); +int ezjack_set_callback(EZJackCallback cb); +int ezjack_read(ezjack_bundle_t *bun, void *buf, int len, ezjack_format_t fmt); +int ezjack_write(ezjack_bundle_t *bun, void *buf, int len, ezjack_format_t fmt); + +#endif /* ifndef _EZJACK_H_ */ + diff --git a/xlibinc/sackit/fixedmath.c b/xlibinc/sackit/fixedmath.c new file mode 100644 index 0000000..dca09c0 --- /dev/null +++ b/xlibinc/sackit/fixedmath.c @@ -0,0 +1,71 @@ +#include "sackit_internal.h" + +uint32_t sackit_mul_fixed_16_int_32(uint32_t a, uint32_t b) +{ + /*uint32_t al = (uint16_t)(a&0xFFFF); + uint32_t bl = (uint16_t)(b&0xFFFF); + uint32_t ah = (uint16_t)(a>>16); + uint32_t bh = (uint16_t)(b>>16); + + return ((al*bl)>>16) + + (bl*ah+bh*al) + + ((bh*ah)<<16);*/ + + + uint64_t aq = a; + uint64_t bq = b; + uint64_t rq = (((uint64_t)aq)*((uint64_t)bq))>>(uint64_t)16; + + return (uint32_t)rq; +} + +uint32_t sackit_div_int_32_32_to_fixed_16(uint32_t a, uint32_t b) +{ + /* + a = q * b + r + a = ah * 2^16 + al + ah = qh * b + rh + al + K = ql * b + rl + al = ql * b + rl - K + + a = (qh * b + rh) * 2^16 + ql * b + rl - K + a = (qh * 2^16 * b) + rh * 2^16 + ql * b + rl - K + a = (qh * 2^16 + ql) * b + rh * 2^16 + rl - K + + the problem here is that rh * 2^16 is probably > b. + setting K to 0, then defining L creatively: + a = (qh * 2^16 + ql + L) * b + (rh * 2^16 + rl - L*b) + + we need (rh * 2^16 + rl) / b for an integer. + L = (rh * 2^16 + rl) / b. + this means: + (rh * 2^16 + rl) = L * b + Lr. + + so we're aiming for + a = (qh * 2^16 + ql + L) * b + (L*b + Lr - L*b) + a = (qh * 2^16 + ql + L) * b + Lr + + This doesn't really demonstrate much. + + */ + /*uint32_t al = (uint32_t)(a<<16); + uint32_t ah = (uint32_t)(a&0xFFFF0000); + + uint32_t rh = ah%b; + uint32_t qh = ah/b; + uint32_t ql = (al+(rh<<16))/b; + */ + + // and here's the easy way out. + uint64_t aq = a; + uint64_t bq = b; + uint64_t rq = (((uint64_t)aq)<<(uint64_t)16)/((uint64_t)bq); + + return (uint32_t)rq; + + //printf("%i %i / %i -> %i %i %i\n" + // ,al,ah,b,rh,qh,ql + //); + //return ql+(qh<<16); + //return (a<<16)/b; +} diff --git a/xlibinc/sackit/litemixer.c b/xlibinc/sackit/litemixer.c new file mode 100644 index 0000000..9581909 --- /dev/null +++ b/xlibinc/sackit/litemixer.c @@ -0,0 +1,30 @@ +#include "sackit_internal.h" + +// IT214p3 mixer: resonant filter mixer +#define MIXER_VER 214 +#define MIXER_ANTICLICK +#define MIXER_NAME sackit_playback_mixstuff_it214fs +#define MIXER_STEREO +#include "mixer_float.h" + +void sackit_playback_mixstuff_it211(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it211s(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it211l(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it211ls(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it212(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it212s(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it212l(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it212ls(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it214(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it214s(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it214l(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it214ls(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it214c(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it214cs(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it214f(sackit_playback_t *sackit, int offs, int len) {} +//void sackit_playback_mixstuff_it214fs(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it214fl(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it214fls(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it214fc(sackit_playback_t *sackit, int offs, int len) {} +void sackit_playback_mixstuff_it214fcs(sackit_playback_t *sackit, int offs, int len) {} + diff --git a/xlibinc/sackit/mixer.c b/xlibinc/sackit/mixer.c new file mode 100644 index 0000000..efaed97 --- /dev/null +++ b/xlibinc/sackit/mixer.c @@ -0,0 +1,177 @@ +#include "sackit_internal.h" + +// IT211 mixer +#define MIXER_NAME sackit_playback_mixstuff_it211 +#define MIXER_VER 211 +#include "mixer_int.h" +#undef MIXER_NAME + +#define MIXER_NAME sackit_playback_mixstuff_it211s +#define MIXER_STEREO +#include "mixer_int.h" +#undef MIXER_STEREO +#undef MIXER_NAME +#undef MIXER_VER + +// IT211 mixer, interpolated +#define MIXER_INTERP_LINEAR +#define MIXER_NAME sackit_playback_mixstuff_it211l +#define MIXER_VER 211 +#include "mixer_int.h" +#undef MIXER_NAME + +#define MIXER_NAME sackit_playback_mixstuff_it211ls +#define MIXER_STEREO +#include "mixer_int.h" +#undef MIXER_STEREO +#undef MIXER_NAME +#undef MIXER_VER +#undef MIXER_INTERP_LINEAR + +// IT212 mixer: IT211 with an anticlick filter for note cuts +#define MIXER_NAME sackit_playback_mixstuff_it212 +#define MIXER_VER 212 +#define MIXER_ANTICLICK +#include "mixer_int.h" +#undef MIXER_NAME + +#define MIXER_NAME sackit_playback_mixstuff_it212s +#define MIXER_STEREO +#include "mixer_int.h" +#undef MIXER_STEREO +#undef MIXER_NAME +#undef MIXER_ANTICLICK +#undef MIXER_VER + +// IT212 mixer, interpolated +#define MIXER_NAME sackit_playback_mixstuff_it212l +#define MIXER_VER 212 +#define MIXER_ANTICLICK +#define MIXER_INTERP_LINEAR +#include "mixer_float.h" +#undef MIXER_NAME + +#define MIXER_NAME sackit_playback_mixstuff_it212ls +#define MIXER_STEREO +#include "mixer_float.h" +#undef MIXER_STEREO +#undef MIXER_NAME +#undef MIXER_ANTICLICK +#undef MIXER_VER +#undef MIXER_INTERP_LINEAR + +// IT214 mixer: floating point mixer +#define MIXER_NAME sackit_playback_mixstuff_it214 +#define MIXER_VER 214 +#define MIXER_ANTICLICK +#include "mixer_float.h" +#undef MIXER_NAME + +#define MIXER_NAME sackit_playback_mixstuff_it214s +#define MIXER_STEREO +#include "mixer_float.h" +#undef MIXER_STEREO +#undef MIXER_NAME +#undef MIXER_ANTICLICK +#undef MIXER_VER + +// IT214 mixer, interpolated linearly +#define MIXER_FILTERED +#define MIXER_NAME sackit_playback_mixstuff_it214l +#define MIXER_VER 214 +#define MIXER_ANTICLICK +#define MIXER_INTERP_LINEAR +#include "mixer_float.h" +#undef MIXER_NAME + +#define MIXER_NAME sackit_playback_mixstuff_it214ls +#define MIXER_STEREO +#include "mixer_float.h" +#undef MIXER_STEREO +#undef MIXER_NAME +#undef MIXER_ANTICLICK +#undef MIXER_VER +#undef MIXER_INTERP_LINEAR + +// IT214 mixer, interpolated cubically +#define MIXER_NAME sackit_playback_mixstuff_it214c +#define MIXER_VER 214 +#define MIXER_ANTICLICK +#define MIXER_INTERP_CUBIC +#include "mixer_float.h" +#undef MIXER_NAME + +#define MIXER_NAME sackit_playback_mixstuff_it214cs +#define MIXER_STEREO +#include "mixer_float.h" +#undef MIXER_STEREO +#undef MIXER_NAME +#undef MIXER_ANTICLICK +#undef MIXER_VER +#undef MIXER_INTERP_CUBIC + +// IT214p3 mixer: resonant filter mixer +#define MIXER_NAME sackit_playback_mixstuff_it214f +#define MIXER_VER 214 +#define MIXER_ANTICLICK +#include "mixer_float.h" +#undef MIXER_NAME + +#define MIXER_NAME sackit_playback_mixstuff_it214fs +#define MIXER_STEREO +#include "mixer_float.h" +#undef MIXER_STEREO +#undef MIXER_NAME +#undef MIXER_ANTICLICK +#undef MIXER_VER + +// IT214p3 mixer, interpolated linearly +#define MIXER_NAME sackit_playback_mixstuff_it214fl +#define MIXER_VER 214 +#define MIXER_ANTICLICK +#define MIXER_INTERP_LINEAR +#include "mixer_float.h" +#undef MIXER_NAME + +#define MIXER_NAME sackit_playback_mixstuff_it214fls +#define MIXER_STEREO +#include "mixer_float.h" +#undef MIXER_STEREO +#undef MIXER_NAME +#undef MIXER_ANTICLICK +#undef MIXER_VER +#undef MIXER_INTERP_LINEAR + +// IT214p3 mixer, interpolated cubically +#define MIXER_NAME sackit_playback_mixstuff_it214fc +#define MIXER_VER 214 +#define MIXER_ANTICLICK +#define MIXER_INTERP_CUBIC +#include "mixer_float.h" +#undef MIXER_NAME + +#define MIXER_NAME sackit_playback_mixstuff_it214fcs +#define MIXER_STEREO +#include "mixer_float.h" +#undef MIXER_STEREO +#undef MIXER_NAME +#undef MIXER_ANTICLICK +#undef MIXER_VER +#undef MIXER_INTERP_CUBIC +#undef MIXER_FILTERED + +// Fast integer mixer, anticlick +#define MIXER_NAME sackit_playback_mixstuff_intfast_a +#define MIXER_VER 212 +#define MIXER_ANTICLICK +#include "mixer_int_fast.h" +#undef MIXER_NAME + +#define MIXER_NAME sackit_playback_mixstuff_intfast_as +#define MIXER_STEREO +#include "mixer_int_fast.h" +#undef MIXER_STEREO +#undef MIXER_NAME +#undef MIXER_ANTICLICK +#undef MIXER_VER + diff --git a/xlibinc/sackit/mixer_float.h b/xlibinc/sackit/mixer_float.h new file mode 100644 index 0000000..a51ccfb --- /dev/null +++ b/xlibinc/sackit/mixer_float.h @@ -0,0 +1,372 @@ +void MIXER_NAME(sackit_playback_t *sackit, int offs, int len) +{ + uint32_t tfreq = sackit->freq; + + int i,j; + int offsend = offs+len; +#ifdef MIXER_STEREO + int pan; + float vl, vr; + offs *= 2; + offsend *= 2; +#endif + + int16_t *buf = &(sackit->buf[offs]); + float *mixbuf = (float *)&(sackit->mixbuf[offs]); + + // just a guess :) + int32_t ramplen = tfreq/400+1; + + float gvol = sackit->gv; // 7 + float mvol = sackit->mv; // 7 + +#ifdef MIXER_STEREO + for(j = 0; j < len*2; j++) +#else + for(j = 0; j < len; j++) +#endif + mixbuf[j] = 0.0f; + +#ifdef MIXER_STEREO + if(sackit->anticlick_f[0] != 0 || sackit->anticlick_f[1] != 0) + { + float rampmul0 = sackit->anticlick_f[0]; + float rampmul1 = sackit->anticlick_f[1]; + sackit->anticlick_f[0] = 0.0f; + sackit->anticlick_f[1] = 0.0f; +#else + if(sackit->anticlick_f[0] != 0.0f) + { + float rampmul = sackit->anticlick_f[0]; + sackit->anticlick_f[0] = 0.0f; +#endif + + for(j = 0; j < ramplen; j++) + { +#ifdef MIXER_STEREO + mixbuf[j*2] += (((float)rampmul0)*(float)(ramplen-j-1))/ramplen; + mixbuf[j*2+1] += (((float)rampmul1)*(float)(ramplen-j-1))/ramplen; +#else + mixbuf[j] += (((float)rampmul)*(float)(ramplen-j-1))/ramplen; +#endif + } + } + + for(i = 0; i < sackit->achn_count; i++) + { + sackit_achannel_t *achn = &(sackit->achn[i]); + +#ifdef MIXER_FILTERED + sackit_filter_calc(sackit, achn); + float fa = achn->filt_coeff[0]; + float fb = achn->filt_coeff[1]; + float fc = achn->filt_coeff[2]; +#ifdef MIXER_STEREO + float k0l = achn->filt_prev[0][0]; + float k0r = achn->filt_prev[1][0]; + float k1l = achn->filt_prev[0][1]; + float k1r = achn->filt_prev[1][1]; +#else + float k0 = achn->filt_prev[0][0]; + float k1 = achn->filt_prev[0][1]; +#endif +#endif + + if(achn->sample == NULL || achn->sample->data == NULL + || achn->offs >= (int32_t)achn->sample->length + || achn->offs < 0) + { + achn->flags &= ~( + SACKIT_ACHN_RAMP + |SACKIT_ACHN_MIXING + |SACKIT_ACHN_PLAYING + |SACKIT_ACHN_SUSTAIN); + } + + if(achn->flags & SACKIT_ACHN_RAMP) + { + achn->flags &= ~SACKIT_ACHN_RAMP; + //ramprem = rampspd; + achn->lramp_f = 0; + + //printf("ramp %i %i %i\n", i, rampspd, (32768+rampspd-1)/rampspd); + //printf("ramp %i %i %i\n", i, rampinc, ramprem); + } + + achn->anticlick_f[0] = 0.0f; + achn->anticlick_f[1] = 0.0f; + + // TODO: ramp stereowise properly + if(achn->flags & SACKIT_ACHN_MIXING) + { +#ifdef MIXER_STEREO + pan = achn->pan; + if(pan == 100) + { + vl = 0x100; + vr = -0x100; + } else { + if(pan <= 32) + { + vl = 0x100; + vr = pan<<3; + } else { + vl = (64-pan)<<3; + vr = 0x100; + } + + // TODO: make this more accurate + int sep = sackit->module->header.sep; + vl = 0x100 * (128-sep) + vl * sep; + vr = 0x100 * (128-sep) + vr * sep; + vl /= 128.0f; + vr /= 128.0f; + } + vl /= 256.0f; + vr /= 256.0f; +#endif + + int32_t zoffs = achn->offs; + float zsuboffs = achn->suboffs_f; + float zlramp = achn->lramp_f; + +#if 1 + // I suspect THIS one is more accurate with regards to IT itself. + int32_t zfreqi = achn->ofreq; + zfreqi = sackit_div_int_32_32_to_fixed_16(zfreqi,tfreq); + float zfreq = zfreqi/65536.0f; +#else + // This thing, of course, is more accurate with regards to maths. + float zfreq = achn->ofreq; + zfreq = ((double)zfreq)/((double)tfreq); +#endif + + //printf("freq %i %i %f\n", zfreq, zoffs, zsuboffs); + + int32_t zlpbeg = achn->sample->loop_begin; + int32_t zlpend = achn->sample->loop_end; + int32_t zlength = achn->sample->length; + uint8_t zflg = achn->sample->flg; + int16_t *zdata = achn->sample->data; + + if((achn->flags & SACKIT_ACHN_SUSTAIN) + && (zflg & IT_SMP_SUSLOOP)) + { + zlpbeg = achn->sample->susloop_begin; + zlpend = achn->sample->susloop_end; + zflg |= IT_SMP_LOOP; + if(zflg & IT_SMP_SUSBIDI) + { + zflg |= IT_SMP_LOOPBIDI; + } else { + zflg &= ~IT_SMP_LOOPBIDI; + } + } + + if(!(zflg & IT_SMP_LOOPBIDI)) + achn->flags &= ~SACKIT_ACHN_REVERSE; + + // TODO: sanity check somewhere! + if(zflg & IT_SMP_LOOP) + zlength = zlpend; + if(achn->flags & SACKIT_ACHN_REVERSE) + zfreq = -zfreq; + + double vol = mvol*achn->evol.y*achn->vol*achn->sv*achn->iv*achn->cv*gvol*achn->fadeout; + vol /= 64.0f*64.0f*64.0f*64.0f*128.0f*64.0f*128.0f*1024.0f*512.0f; + achn->lramp_f = vol; + + // TODO: get ramping working correctly + for(j = 0; j < len; j++) { + float v; + // frozensun.it suggests this exception does NOT exist in IT. + // Of course, interpolation sucks ass, so that's why most of the apps default to noninterpolated. + if(0) // zfreq >= 1.0f) + { + v = zdata[zoffs]; + } else { +#ifdef MIXER_INTERP_LINEAR + // get sample value + float v0 = zdata[zoffs]; + float v1 = ((zoffs+1) == zlength + ? (zflg & IT_SMP_LOOP + ? zdata[zlpbeg] + : 0) + : zdata[(zoffs+1)]); + v = ((v0*(1.0-zsuboffs)) + + (v1*(zsuboffs)))/32768.0f; +#else +#ifdef MIXER_INTERP_CUBIC + // get sample value + // TODO: do this more efficiently / correctly + float v0 = (zoffs-1 < 0 ? 0.0f : zdata[zoffs-1]); + float v1 = zdata[zoffs]; + float v2 = ((zoffs+1) == zlength + ? (zflg & IT_SMP_LOOP + ? zdata[zlpbeg] + : 0) + : zdata[(zoffs+1)]); + float v3 = ((zoffs+2) == zlength + ? (zflg & IT_SMP_LOOP + ? zdata[zlpbeg+1] + : 0) + : zdata[(zoffs+2)]); + + // Reference: http://paulbourke.net/miscellaneous/interpolation/ + float t = zsuboffs; + float t2 = t*t; + float t3 = t2*t; + +#if 1 + // using a Hermite spline + const float bias = 0.0f; + const float tension = 0.0f; + + float m0 = (v1 - v0)*(1.0 + bias)*(1.0 - tension)/2.0 + + (v2 - v1)*(1.0 - bias)*(1.0 - tension)/2.0; + float m1 = (v2 - v1)*(1.0 + bias)*(1.0 - tension)/2.0 + + (v3 - v2)*(1.0 - bias)*(1.0 - tension)/2.0; + float a0 = 2.0*t3 - 3.0*t2 + 1; + float a1 = t3 - 2.0*t2 + t; + float a2 = t3 - t2; + float a3 = -2.0*t3 + 3.0*t2; + + v = a0*v1 + a1*m0 + a2*m1 + a3*v2; +#else + // using a cubic spline + float a0 = v3 - v2 + v1 - v0; + float a1 = -v1 + v0 - a0; + float a2 = v2 - v0; + float a3 = v1; + + v = a0*t3 + a1*t2 + a2*t + a3; + +#endif +#else + v = zdata[zoffs]; +#endif +#endif + } + v /= 32768.0f; + if(j < ramplen) + v *= zlramp + (vol-zlramp)*(j/(float)ramplen); + else + v *= vol; + + // mix +#ifdef MIXER_FILTERED +#ifdef MIXER_STEREO + float vxl = v*vl*fa + k0l*fb + k1l*fc; + float vxr = v*vr*fa + k0r*fb + k1r*fc; + if(vxl < -2.0f) vxl = -2.0f; else if(vxl > 2.0f) vxl = 2.0f; + if(vxr < -2.0f) vxr = -2.0f; else if(vxr > 2.0f) vxr = 2.0f; + if(k0l < -2.0f) k0l = -2.0f; else if(k0l > 2.0f) k0l = 2.0f; + if(k0r < -2.0f) k0r = -2.0f; else if(k0r > 2.0f) k0r = 2.0f; + if(k1l < -2.0f) k1l = -2.0f; else if(k1l > 2.0f) k1l = 2.0f; + if(k1r < -2.0f) k1r = -2.0f; else if(k1r > 2.0f) k1r = 2.0f; + k1l = k0l; + k1r = k0r; + k0l = vxl; + k0r = vxr; + + mixbuf[j*2] += vxl; + mixbuf[j*2+1] += vxr; + achn->anticlick_f[0] = vxl; + achn->anticlick_f[1] = vxr; +#else + float vx = v*fa + k0*fb + k1*fc; + if(vx < -2.0f) vx = -2.0f; else if(vx > 2.0f) vx = 2.0f; + if(k0 < -2.0f) k0 = -2.0f; else if(k0 > 2.0f) k0 = 2.0f; + if(k1 < -2.0f) k1 = -2.0f; else if(k1 > 2.0f) k1 = 2.0f; + k1 = k0; + k0 = vx; + mixbuf[j] += vx; + achn->anticlick_f[0] = vx; +#endif +#else +#ifdef MIXER_STEREO + mixbuf[j*2] += v*vl; + mixbuf[j*2+1] += v*vr; + achn->anticlick_f[0] = v*vl; + achn->anticlick_f[1] = v*vr; +#else + mixbuf[j] += v; + achn->anticlick_f[0] = v; +#endif +#endif + + // update + zsuboffs += zfreq; + int32_t zsuboffs_int = (int32_t)zsuboffs; + zoffs += zsuboffs_int; + zsuboffs -= zsuboffs_int; + + if((zfreq < 0 + ? zoffs < zlpbeg + : zoffs >= (int32_t)zlength)) + { + // TODO: ping-pong/bidirectional loops + // TODO? speed up for tiny loops? + if(zflg & IT_SMP_LOOP) + { + if(zflg & IT_SMP_LOOPBIDI) + { + if(zfreq > 0) + { + zoffs = zlpend*2-1-zoffs; + zfreq = -zfreq; + zsuboffs = 1.0-zsuboffs; + achn->flags |= SACKIT_ACHN_REVERSE; + } else { + zoffs = zlpbeg*2-zoffs; + zfreq = -zfreq; + zsuboffs = 1.0-zsuboffs; + achn->flags &= ~SACKIT_ACHN_REVERSE; + } + } else { + while(zoffs >= (int32_t)zlpend) + zoffs += (zlpbeg-zlpend); + } + } else { + achn->flags &= ~( + SACKIT_ACHN_MIXING + |SACKIT_ACHN_PLAYING + |SACKIT_ACHN_SUSTAIN); + break; + } + } + } + + achn->offs = zoffs; + achn->suboffs_f = zsuboffs; +#ifdef MIXER_FILTERED +#ifdef MIXER_STEREO + achn->filt_prev[0][0] = k0l; + achn->filt_prev[1][0] = k0r; + achn->filt_prev[0][1] = k1l; + achn->filt_prev[1][1] = k1r; +#else + achn->filt_prev[0][0] = k0; + achn->filt_prev[0][1] = k1; +#endif +#endif + } else if(achn->flags & SACKIT_ACHN_PLAYING) { + // TODO: update offs/suboffs + } + } + + // stick into the buffer +#ifdef MIXER_STEREO + for(j = 0; j < len*2; j++) +#else + for(j = 0; j < len; j++) +#endif + { + int32_t bv = -mixbuf[j]*32768.0f; + if(bv < -32768) bv = -32768; + else if(bv > 32767) bv = 32767; + + buf[j] = bv; + } +} + diff --git a/xlibinc/sackit/mixer_int.h b/xlibinc/sackit/mixer_int.h new file mode 100644 index 0000000..c90669c --- /dev/null +++ b/xlibinc/sackit/mixer_int.h @@ -0,0 +1,354 @@ +void MIXER_NAME(sackit_playback_t *sackit, int offs, int len) +{ + uint32_t tfreq = sackit->freq; + + int i,j; + int offsend = offs+len; +#ifdef MIXER_STEREO + int pan, vl, vr; + offs *= 2; + offsend *= 2; +#endif + + int16_t *buf = &(sackit->buf[offs]); + int32_t *mixbuf = (int32_t *)&(sackit->mixbuf[offs]); + + // just a guess :) +#if MIXER_VER <= 211 + int32_t ramplen = tfreq/500+1; +#else + int32_t ramplen = tfreq/400+1; +#endif + + int32_t gvol = sackit->gv; // 7 + int32_t mvol = sackit->mv; // 7 + +#ifdef MIXER_STEREO + for(j = 0; j < len*2; j++) +#else + for(j = 0; j < len; j++) +#endif + mixbuf[j] = 0; + +#ifdef MIXER_ANTICLICK +#ifdef MIXER_STEREO + if(sackit->anticlick[0] != 0 || sackit->anticlick[1] != 0) + { + int32_t rampmul0 = sackit->anticlick[0]; + int32_t rampmul1 = sackit->anticlick[1]; + sackit->anticlick[0] = 0; + sackit->anticlick[1] = 0; +#else + if(sackit->anticlick[0] != 0) + { + int32_t rampmul = sackit->anticlick[0]; + sackit->anticlick[0] = 0; +#endif + + for(j = 0; j < ramplen; j++) + { +#ifdef MIXER_STEREO + mixbuf[j*2] += (((int32_t)rampmul0)*(int32_t)(ramplen-j-1))/ramplen; + mixbuf[j*2+1] += (((int32_t)rampmul1)*(int32_t)(ramplen-j-1))/ramplen; +#else + mixbuf[j] += (((int32_t)rampmul)*(int32_t)(ramplen-j-1))/ramplen; +#endif + } + } +#endif + + for(i = 0; i < sackit->achn_count; i++) + { + sackit_achannel_t *achn = &(sackit->achn[i]); + + if(achn->sample == NULL || achn->sample->data == NULL + || achn->offs >= (int32_t)achn->sample->length + || achn->offs < 0) + { + achn->flags &= ~( + SACKIT_ACHN_RAMP + |SACKIT_ACHN_MIXING + |SACKIT_ACHN_PLAYING + |SACKIT_ACHN_SUSTAIN); + } + + if(achn->flags & SACKIT_ACHN_RAMP) + { + achn->flags &= ~SACKIT_ACHN_RAMP; + //ramprem = rampspd; + achn->lramp = 0; + + //printf("ramp %i %i %i\n", i, rampspd, (32768+rampspd-1)/rampspd); + //printf("ramp %i %i %i\n", i, rampinc, ramprem); + } + +#ifdef MIXER_ANTICLICK + achn->anticlick[0] = 0; + achn->anticlick[1] = 0; +#endif + + // TODO: ramp stereowise properly + if(achn->flags & SACKIT_ACHN_MIXING) + { +#ifdef MIXER_STEREO + pan = achn->pan; + if(pan == 100) + { + vl = 0x100; + vr = -0x100; + } else { + if(pan <= 32) + { + vl = 0x100; + vr = pan<<3; + } else { + vl = (64-pan)<<3; + vr = 0x100; + } + + int sep = sackit->module->header.sep; + vl = 0x100 * (128-sep) + vl * sep; + vr = 0x100 * (128-sep) + vr * sep; + vl >>= 7; + vr >>= 7; + } +#endif + + int32_t zoffs = achn->offs; + int32_t zsuboffs = achn->suboffs; + int32_t zfreq = achn->ofreq; + int32_t zlramp = achn->lramp; + + zfreq = sackit_div_int_32_32_to_fixed_16(zfreq,tfreq); + + //printf("freq %i %i %i\n", zfreq, zoffs, zsuboffs); + + int32_t zlpbeg = achn->sample->loop_begin; + int32_t zlpend = achn->sample->loop_end; + int32_t zlength = achn->sample->length; + uint8_t zflg = achn->sample->flg; + int16_t *zdata = achn->sample->data; + + if((achn->flags & SACKIT_ACHN_SUSTAIN) + && (zflg & IT_SMP_SUSLOOP)) + { + zlpbeg = achn->sample->susloop_begin; + zlpend = achn->sample->susloop_end; + zflg |= IT_SMP_LOOP; + if(zflg & IT_SMP_SUSBIDI) + { + zflg |= IT_SMP_LOOPBIDI; + } else { + zflg &= ~IT_SMP_LOOPBIDI; + } + } + + if(!(zflg & IT_SMP_LOOPBIDI)) + achn->flags &= ~SACKIT_ACHN_REVERSE; + + // TODO: sanity check somewhere! + if(zflg & IT_SMP_LOOP) + zlength = zlpend; + if(achn->flags & SACKIT_ACHN_REVERSE) + zfreq = -zfreq; + + int32_t vol = 0x8000; + /*vol = ((int32_t)achn->vol) // 6 + *((int32_t)achn->sv) // 6 + *((int32_t)achn->cv) // 6 + *gvol // 7 + ; + //vol += (1<<9); + vol >>= 10; + vol = (vol*mvol)>>7; // 7*/ + // TODO: sort the order / rounding out + // 4) FV = Vol * SV * IV * CV * GV * VEV * NFC / 2^41 + /*vol = (vol*((int32_t)achn->vol))>>6; + vol = (vol*((int32_t)achn->sv))>>6; + vol = (vol*((int32_t)achn->iv))>>7; + vol = (vol*((int32_t)achn->cv))>>6; + vol = (vol*gvol)>>7; + vol = (vol*((int32_t)achn->evol.y))>>6; + vol = (vol*((int32_t)achn->fadeout))>>10; + vol = (vol*mvol)>>7;*/ + { + /*int64_t bvol = 0x8000; + bvol = (bvol*(int64_t)achn->evol.y)>>14; + bvol = (bvol*(int64_t)achn->vol)>>6; + bvol = (bvol*(int64_t)achn->sv)>>6; + bvol = (bvol*(int64_t)achn->iv)>>6; + bvol = (bvol*(int64_t)achn->cv)>>6; + bvol = (bvol*(int64_t)gvol)>>7; + bvol = (bvol*(int64_t)achn->fadeout)>>10; + bvol = (bvol*(int64_t)mvol)>>7; + vol = (bvol)>>1;*/ + int64_t bvol = 1; + bvol = (bvol*(int64_t)achn->evol.y); + bvol = (bvol*(int64_t)achn->vol); + bvol = (bvol*(int64_t)achn->sv); + bvol = (bvol*(int64_t)achn->iv); + bvol = (bvol*(int64_t)achn->cv); + bvol = (bvol*(int64_t)gvol); + bvol = (bvol*(int64_t)achn->fadeout); + bvol = (bvol*(int64_t)mvol); + vol = (bvol)>>(1+14+6+6+6+6+7+10+7-15); + } + //printf("%04X\n", vol); + //vol += 0x0080; + //vol &= 0x7F00; + + achn->lramp = vol; + + int32_t rampmul = zlramp; + int32_t ramprem = ramplen; + int32_t rampdelta = (vol-zlramp); + int negdepth = (rampdelta < 0); + int32_t rampdelta_i = rampdelta; + if(negdepth) + rampdelta = -rampdelta; + int32_t rampspd = (rampdelta+0x0080)&~0x00FF; + + rampspd = rampspd / (ramplen+1); + + rampspd &= ~3; + + if(negdepth) + { + rampspd = -rampspd; + //rampspd -= 4; + } + + /* + if(rampdelta != 0) + printf("%5i %04X / %5i %04X mod90 is %5i => %5i \n", vol, vol + , rampdelta + , rampdelta_i&0xFFFF + , (rampdelta_i+(ramplen+1)*0x10000) % (ramplen+1) + , rampspd); + */ + + /* + Ramp speeds: + 06BF NOT 16 + 0B40 = 32 + 0CC0 = 36 + 0F00 = 40 + 1200 = 48 + 1800 = 68 + 1E00 = 84 + 1ED8 NOT 84 (it's 88!) + 27C0 = 112 + 3000 = 136 + + D000 = -136 + E800 NOT -68 + EE00 = -48 + F400 = -32 + F4C0 (?) -28 + FA00 (?) -16 + */ + + //printf("%i\n", rampspd); + for(j = 0; j < len; j++) { +#ifdef MIXER_INTERP_LINEAR + // get sample value + int32_t v0 = zdata[zoffs]; + int32_t v1 = ((zoffs+1) == zlength + ? (zflg & IT_SMP_LOOP + ? zdata[zlpbeg] + : 0) + : zdata[(zoffs+1)]); + int32_t v = ((v0*((65535-zsuboffs)))>>16) + + ((v1*(zsuboffs))>>16); +#else + int32_t v = zdata[zoffs]; +#endif + + if(ramprem > 0) + { + v = (v*rampmul+0x8000)>>16; + rampmul += rampspd; + ramprem--; + } else { + v = ((v*vol+0x8000)>>16); + } + + // mix +#ifdef MIXER_STEREO + mixbuf[j*2] += v*vl>>8; + mixbuf[j*2+1] += v*vr>>8; +#else + mixbuf[j] += v; +#endif +#ifdef MIXER_ANTICLICK +#ifdef MIXER_STEREO + achn->anticlick[0] = v*vl>>8; + achn->anticlick[1] = v*vr>>8; +#else + achn->anticlick[0] = v; +#endif +#endif + + // update + zsuboffs += zfreq; + zoffs += (((int32_t)zsuboffs)>>16); + zsuboffs &= 0xFFFF; + + if((zfreq < 0 + ? zoffs < zlpbeg + : zoffs >= (int32_t)zlength)) + { + // TODO: ping-pong/bidirectional loops + // TODO? speed up for tiny loops? + if(zflg & IT_SMP_LOOP) + { + if(zflg & IT_SMP_LOOPBIDI) + { + if(zfreq > 0) + { + zoffs = zlpend*2-1-zoffs; + zfreq = -zfreq; + zsuboffs = 0x10000-zsuboffs; + achn->flags |= SACKIT_ACHN_REVERSE; + } else { + zoffs = zlpbeg*2-zoffs; + zfreq = -zfreq; + zsuboffs = 0x10000-zsuboffs; + achn->flags &= ~SACKIT_ACHN_REVERSE; + } + } else { + while(zoffs >= (int32_t)zlpend) + zoffs += (zlpbeg-zlpend); + } + } else { + achn->flags &= ~( + SACKIT_ACHN_MIXING + |SACKIT_ACHN_PLAYING + |SACKIT_ACHN_SUSTAIN); + break; + } + } + } + + achn->offs = zoffs; + achn->suboffs = zsuboffs; + } else if(achn->flags & SACKIT_ACHN_PLAYING) { + // TODO: update offs/suboffs + } + } + + // stick into the buffer +#ifdef MIXER_STEREO + for(j = 0; j < len*2; j++) +#else + for(j = 0; j < len; j++) +#endif + { + int32_t bv = -mixbuf[j]; + if(bv < -32768) bv = -32768; + else if(bv > 32767) bv = 32767; + + buf[j] = bv; + } +} + diff --git a/xlibinc/sackit/mixer_int_fast.h b/xlibinc/sackit/mixer_int_fast.h new file mode 100644 index 0000000..9bab500 --- /dev/null +++ b/xlibinc/sackit/mixer_int_fast.h @@ -0,0 +1,424 @@ +void MIXER_NAME(sackit_playback_t *sackit, int offs, int len) +{ + uint32_t tfreq = sackit->freq; + + int i,j; + int offsend = offs+len; +#ifdef MIXER_STEREO + int pan, vl, vr; + offs *= 2; + offsend *= 2; +#endif + + int16_t *buf = &(sackit->buf[offs]); + int32_t *mixbuf = (int32_t *)&(sackit->mixbuf[offs]); + +#if MIXER_VER <= 211 + int32_t ramplen = tfreq/500+1; +#else + int32_t ramplen = tfreq/400+1; +#endif + + int32_t gvol = sackit->gv; // 7 + int32_t mvol = sackit->mv; // 7 + +#ifdef MIXER_STEREO + for(j = 0; j < len*2; j++) +#else + for(j = 0; j < len; j++) +#endif + mixbuf[j] = 0; + +#ifdef MIXER_ANTICLICK +#ifdef MIXER_STEREO + if(sackit->anticlick[0] != 0 || sackit->anticlick[1] != 0) + { + int32_t rampmul0 = sackit->anticlick[0]; + int32_t rampmul1 = sackit->anticlick[1]; + sackit->anticlick[0] = 0; + sackit->anticlick[1] = 0; +#else + if(sackit->anticlick[0] != 0) + { + int32_t rampmul = sackit->anticlick[0]; + sackit->anticlick[0] = 0; +#endif + + for(j = 0; j < ramplen; j++) + { +#ifdef MIXER_STEREO + mixbuf[j*2] += (((int32_t)rampmul0)*(int32_t)(ramplen-j-1))/ramplen; + mixbuf[j*2+1] += (((int32_t)rampmul1)*(int32_t)(ramplen-j-1))/ramplen; +#else + mixbuf[j] += (((int32_t)rampmul)*(int32_t)(ramplen-j-1))/ramplen; +#endif + } + } +#endif + + for(i = 0; i < sackit->achn_count; i++) + { + sackit_achannel_t *achn = &(sackit->achn[i]); + + if(achn->sample == NULL || achn->sample->data == NULL + || achn->offs >= (int32_t)achn->sample->length + || achn->offs < 0) + { + achn->flags &= ~( + SACKIT_ACHN_RAMP + |SACKIT_ACHN_MIXING + |SACKIT_ACHN_PLAYING + |SACKIT_ACHN_SUSTAIN); + } + + if(achn->flags & SACKIT_ACHN_RAMP) + { + achn->flags &= ~SACKIT_ACHN_RAMP; + achn->lramp = 0; + } + +#ifdef MIXER_ANTICLICK + achn->anticlick[0] = 0; + achn->anticlick[1] = 0; +#endif + + // TODO: ramp stereowise properly + if(achn->flags & SACKIT_ACHN_MIXING) + { +#ifdef MIXER_STEREO + pan = achn->pan; + if(pan == 100) + { + vl = 0x100; + vr = -0x100; + } else { + if(pan <= 32) + { + vl = 0x100; + vr = pan<<3; + } else { + vl = (64-pan)<<3; + vr = 0x100; + } + + int sep = sackit->module->header.sep; + vl = 0x100 * (128-sep) + vl * sep; + vr = 0x100 * (128-sep) + vr * sep; + vl >>= 7; + vr >>= 7; + } +#endif + + int32_t zoffs = achn->offs; + int32_t zsuboffs = achn->suboffs; + int32_t zfreq = achn->ofreq; + int32_t zlramp = achn->lramp; + + zfreq = sackit_div_int_32_32_to_fixed_16(zfreq,tfreq); + + //printf("freq %i %i %i\n", zfreq, zoffs, zsuboffs); + + int32_t zlpbeg = achn->sample->loop_begin; + int32_t zlpend = achn->sample->loop_end; + int32_t zlength = achn->sample->length; + uint8_t zflg = achn->sample->flg; + int16_t *zdata = achn->sample->data; + + if((achn->flags & SACKIT_ACHN_SUSTAIN) + && (zflg & IT_SMP_SUSLOOP)) + { + zlpbeg = achn->sample->susloop_begin; + zlpend = achn->sample->susloop_end; + zflg |= IT_SMP_LOOP; + if(zflg & IT_SMP_SUSBIDI) + { + zflg |= IT_SMP_LOOPBIDI; + } else { + zflg &= ~IT_SMP_LOOPBIDI; + } + } + + if(!(zflg & IT_SMP_LOOPBIDI)) + achn->flags &= ~SACKIT_ACHN_REVERSE; + + // TODO: sanity check somewhere! + if(zflg & IT_SMP_LOOP) + zlength = zlpend; + if(achn->flags & SACKIT_ACHN_REVERSE) + zfreq = -zfreq; + + int32_t vol = 0x8000; + // TODO: sort the order / rounding out + // 4) FV = Vol * SV * IV * CV * GV * VEV * NFC / 2^41 + { + /*int64_t bvol = 0x8000; + bvol = (bvol*(int64_t)achn->evol.y)>>14; + bvol = (bvol*(int64_t)achn->vol)>>6; + bvol = (bvol*(int64_t)achn->sv)>>6; + bvol = (bvol*(int64_t)achn->iv)>>6; + bvol = (bvol*(int64_t)achn->cv)>>6; + bvol = (bvol*(int64_t)gvol)>>7; + bvol = (bvol*(int64_t)achn->fadeout)>>10; + bvol = (bvol*(int64_t)mvol)>>7; + vol = (bvol)>>1;*/ + int64_t bvol = 1; + bvol = (bvol*(int64_t)achn->evol.y); + bvol = (bvol*(int64_t)achn->vol); + bvol = (bvol*(int64_t)achn->sv); + bvol = (bvol*(int64_t)achn->iv); + bvol = (bvol*(int64_t)achn->cv); + bvol = (bvol*(int64_t)gvol); + bvol = (bvol*(int64_t)achn->fadeout); + bvol = (bvol*(int64_t)mvol); + vol = (bvol)>>(1+14+6+6+6+6+7+10+7-14); + } + //printf("%04X\n", vol); + //vol += 0x0080; + //vol &= 0x7F00; + + achn->lramp = vol; + +#ifdef MIXER_STEREO + int vlpre = (vol*vl)>>8; + int vrpre = (vol*vr)>>8; +#endif + + int32_t rampmul = zlramp; + int32_t ramprem = ramplen; + int32_t rampdelta = (vol-zlramp); + int negdepth = (rampdelta < 0); + int32_t rampdelta_i = rampdelta; + if(negdepth) + rampdelta = -rampdelta; + int32_t rampspd = (rampdelta+0x0080)&~0x00FF; + + rampspd = rampspd / (ramplen+1); + + rampspd &= ~3; + + if(negdepth) + { + rampspd = -rampspd; + } + + if(ramprem > len) ramprem = len; + +#ifndef INTFAST_MIX1 +#define INTFAST_MIX1(vol) \ + /* get sample value */ \ + int32_t v = zdata[zoffs]; \ + v = (v*(vol))>>16; \ + /* mix */ \ + int vout = v; \ + mixbuf[j] += vout; \ + /* update */ \ + zsuboffs += zfreq; \ + zoffs += (((int32_t)zsuboffs)>>16); \ + zsuboffs &= 0xFFFF; +#endif + +#ifndef INTFAST_MIX2 +#define INTFAST_MIX2(vl, vr) \ + /* get sample value */ \ + int32_t v = zdata[zoffs]; \ + /* mix */ \ + int voutl = (v*(vl))>>16; \ + int voutr = (v*(vr))>>16; \ + mixbuf[j*2+0] += voutl; \ + mixbuf[j*2+1] += voutr; \ + /* update */ \ + zsuboffs += zfreq; \ + zoffs += (((int32_t)zsuboffs)>>16); \ + zsuboffs &= 0xFFFF; +#endif + +#ifndef INTFAST_AC1 +#define INTFAST_AC1 \ + achn->anticlick[0] = vout; +#endif + +#ifndef INTFAST_AC2 +#define INTFAST_AC2 \ + achn->anticlick[0] = voutl; \ + achn->anticlick[1] = voutr; +#endif + +#ifndef INTFAST_TERM_NOLOOP +#define INTFAST_TERM_NOLOOP \ + if(zoffs >= (int32_t)zlength) \ + { \ + achn->flags &= ~( \ + SACKIT_ACHN_MIXING \ + |SACKIT_ACHN_PLAYING \ + |SACKIT_ACHN_SUSTAIN); \ + j = len; \ + break; \ + } +#endif + +#ifndef INTFAST_TERM_LOOP +#define INTFAST_TERM_LOOP \ + if(zoffs >= (int32_t)zlength) \ + { \ + while(zoffs >= (int32_t)zlpend) \ + zoffs += (zlpbeg-zlpend); \ + } +#endif + +#ifndef INTFAST_TERM_BIDI +#define INTFAST_TERM_BIDI \ + if((zfreq < 0 \ + ? zoffs < zlpbeg \ + : zoffs >= (int32_t)zlength)) \ + { \ + if(zfreq > 0) \ + { \ + zoffs = zlpend*2-1-zoffs; \ + zfreq = -zfreq; \ + zsuboffs = 0x10000-zsuboffs; \ + achn->flags |= SACKIT_ACHN_REVERSE; \ + } else { \ + zoffs = zlpbeg*2-zoffs; \ + zfreq = -zfreq; \ + zsuboffs = 0x10000-zsuboffs; \ + achn->flags &= ~SACKIT_ACHN_REVERSE; \ + } \ + } +#endif + +// TODO: estimate termination lengths (effectively (32 - 32.16)/32: that is, ((i32<<16) - i32_16)/(i32<<16)) +#ifndef INTFAST_TLEN_NOBIDI +#define INTFAST_TLEN_NOBIDI \ + (zlength - zoffs) +#endif + +#ifndef INTFAST_MIX_HANDLER +#define INTFAST_MIX_HANDLER(Mix1, Mix2, Ac, Term) \ + for(j = 0; j < ramprem; j++) { \ + Mix1; \ + Ac; \ + rampmul += rampspd; \ + Term; \ + } \ + for(; j < len; j++) { \ + Mix2; \ + Ac; \ + Term; \ + } +#endif + + if((zflg & IT_SMP_LOOP) && (zflg & IT_SMP_LOOPBIDI)) + { +#ifdef MIXER_STEREO +# ifdef MIXER_ANTICLICK + INTFAST_MIX_HANDLER( + INTFAST_MIX2((rampmul*vl) >> 8, (rampmul*vr) >> 8), + INTFAST_MIX2(vlpre, vrpre), + INTFAST_AC2, + INTFAST_TERM_BIDI); +# else + INTFAST_MIX_HANDLER( + INTFAST_MIX2((rampmul*vl) >> 8, (rampmul*vr) >> 8), + INTFAST_MIX2(vlpre, vrpre), + , + INTFAST_TERM_BIDI); +# endif +#else +# ifdef MIXER_ANTICLICK + INTFAST_MIX_HANDLER( + INTFAST_MIX1(rampmul), + INTFAST_MIX1(vol), + INTFAST_AC1, + INTFAST_TERM_BIDI); +# else + INTFAST_MIX_HANDLER( + INTFAST_MIX1(rampmul), + INTFAST_MIX1(vol), + , + INTFAST_TERM_BIDI); +# endif +#endif + } else if((zflg & IT_SMP_LOOP) && !(zflg & IT_SMP_LOOPBIDI)) { +#ifdef MIXER_STEREO +# ifdef MIXER_ANTICLICK + INTFAST_MIX_HANDLER( + INTFAST_MIX2((rampmul*vl) >> 8, (rampmul*vr) >> 8), + INTFAST_MIX2(vlpre, vrpre), + INTFAST_AC2, + INTFAST_TERM_LOOP); +# else + INTFAST_MIX_HANDLER( + INTFAST_MIX2((rampmul*vl) >> 8, (rampmul*vr) >> 8), + INTFAST_MIX2(vlpre, vrpre), + , + INTFAST_TERM_LOOP); +# endif +#else +# ifdef MIXER_ANTICLICK + INTFAST_MIX_HANDLER( + INTFAST_MIX1(rampmul), + INTFAST_MIX1(vol), + INTFAST_AC1, + INTFAST_TERM_LOOP); +# else + INTFAST_MIX_HANDLER( + INTFAST_MIX1(rampmul), + INTFAST_MIX1(vol), + , + INTFAST_TERM_LOOP); +# endif +#endif + } else { +#ifdef MIXER_STEREO +# ifdef MIXER_ANTICLICK + INTFAST_MIX_HANDLER( + INTFAST_MIX2((rampmul*vl) >> 8, (rampmul*vr) >> 8), + INTFAST_MIX2(vlpre, vrpre), + INTFAST_AC2, + INTFAST_TERM_NOLOOP); +# else + INTFAST_MIX_HANDLER( + INTFAST_MIX2((rampmul*vl) >> 8, (rampmul*vr) >> 8), + INTFAST_MIX2(vlpre, vrpre), + , + INTFAST_TERM_NOLOOP); +# endif +#else +# ifdef MIXER_ANTICLICK + INTFAST_MIX_HANDLER( + INTFAST_MIX1(rampmul), + INTFAST_MIX1(vol), + INTFAST_AC1, + INTFAST_TERM_NOLOOP); +# else + INTFAST_MIX_HANDLER( + INTFAST_MIX1(rampmul), + INTFAST_MIX1(vol), + , + INTFAST_TERM_NOLOOP); +# endif +#endif + } + + achn->offs = zoffs; + achn->suboffs = zsuboffs; + } else if(achn->flags & SACKIT_ACHN_PLAYING) { + // TODO: update offs/suboffs + } + } + + // stick into the buffer +#ifdef MIXER_STEREO + for(j = 0; j < len*2; j++) +#else + for(j = 0; j < len; j++) +#endif + { + int32_t bv = -mixbuf[j]; + if(bv < -32768) bv = -32768; + else if(bv > 32767) bv = 32767; + + buf[j] = bv; + } +} + diff --git a/xlibinc/sackit/objects.c b/xlibinc/sackit/objects.c new file mode 100644 index 0000000..ce70272 --- /dev/null +++ b/xlibinc/sackit/objects.c @@ -0,0 +1,688 @@ +#include "sackit_internal.h" + +it_module_t *sackit_module_new(void) +{ + int i; + + it_module_t *module = malloc(sizeof(it_module_t)); + + for(i = 0; i < MAX_ORDERS; i++) + module->orders[i] = 0xFF; + for(i = 0; i < MAX_INSTRUMENTS; i++) + module->instruments[i] = NULL; + for(i = 0; i < MAX_SAMPLES; i++) + module->samples[i] = NULL; + for(i = 0; i < MAX_PATTERNS; i++) + module->patterns[i] = NULL; + + return module; +} + +void sackit_module_free(it_module_t *module) +{ + int i; + + for(i = 0; i < MAX_INSTRUMENTS; i++) + if(module->instruments[i] != NULL) + free(module->instruments[i]); + for(i = 0; i < MAX_SAMPLES; i++) + if(module->samples[i] != NULL) + { + if(module->samples[i]->data != NULL) + free(module->samples[i]->data); + + free(module->samples[i]); + } + for(i = 0; i < MAX_PATTERNS; i++) + if(module->patterns[i] != NULL) + free(module->patterns[i]); + + free(module); +} + +char sackit_file_getc(sackit_reader_t *reader) +{ + return fgetc((FILE *)reader->in); +} + +size_t sackit_file_read(sackit_reader_t *reader, void *out, size_t size) +{ + return fread(out, size, 1, (FILE *)reader->in); +} + +void sackit_file_seek(sackit_reader_t *reader, long offset, int mode) +{ + fseek((FILE *)reader->in, offset, mode); +} + +long sackit_file_tell(sackit_reader_t *reader) +{ + return ftell((FILE *)reader->in); +} + +char sackit_mem_getc(sackit_reader_t *reader) +{ + sackit_reader_data_mem_t *data = (sackit_reader_data_mem_t *)reader->in; + if (data->pos >= data->len) return EOF; + else return data->ptr[data->pos++]; +} + +size_t sackit_mem_read(sackit_reader_t *reader, void *out, size_t psize) +{ + sackit_reader_data_mem_t *data = (sackit_reader_data_mem_t *)reader->in; + size_t size = data->len - data->pos; + if (psize < size) size = psize; + memcpy(out, &(data->ptr[data->pos]), size); + data->pos += size; + return size > 0 ? 1 : 0; +} + +void sackit_mem_seek(sackit_reader_t *reader, long offset, int mode) +{ + sackit_reader_data_mem_t *data = (sackit_reader_data_mem_t *)reader->in; + switch (mode) + { + case SEEK_CUR: + data->pos += offset; + break; + case SEEK_SET: + data->pos = offset; + break; + case SEEK_END: + data->pos = data->len - offset; + break; + } + + if (data->pos < 0) data->pos = 0; +} + +long sackit_mem_tell(sackit_reader_t *reader) +{ + sackit_reader_data_mem_t *data = (sackit_reader_data_mem_t *)reader->in; + return data->pos; +} + +it_module_t *sackit_module_load_offs(const char *fname, int fboffs) +{ + it_module_t *module; + sackit_reader_t reader; + + FILE *fp = fopen(fname, "rb"); + if(fp == NULL) + { + perror("sackit_module_load"); + return NULL; + } + + reader.in = (void *)fp; + reader.getch = &sackit_file_getc; + reader.read = &sackit_file_read; + reader.seek = &sackit_file_seek; + reader.tell = &sackit_file_tell; + + module = sackit_module_load_offs_internal(&reader, fboffs); + + fclose(fp); + return module; +} + +it_module_t *sackit_module_load_memory(const void *data, const long length) +{ + it_module_t *module; + sackit_reader_t reader; + sackit_reader_data_mem_t memdata; + + memdata.ptr = data; + memdata.pos = 0; + memdata.len = length; + + reader.in = &memdata; + reader.getch = &sackit_mem_getc; + reader.read = &sackit_mem_read; + reader.seek = &sackit_mem_seek; + reader.tell = &sackit_mem_tell; + + module = sackit_module_load_offs_internal(&reader, 0); + + return module; +} + +it_module_t *sackit_module_load_offs_internal(sackit_reader_t *reader, int fboffs) +{ + int i, j, k; + + // create module + it_module_t *module = sackit_module_new(); + + // load header + reader->seek(reader, fboffs, SEEK_SET); + if(reader->read(reader, &(module->header), sizeof(it_module_header_t)) != 1) + { + fprintf(stderr, "sackit_module_load: could not read header\n"); + sackit_module_free(module); + return NULL; + } + + // check magic + if(memcmp(module->header.magic, "IMPM", 4)) + { + fprintf(stderr, "sackit_module_load: invalid magic\n"); + sackit_module_free(module); + return NULL; + } + + // sanity checks + if(module->header.ordnum > MAX_ORDERS + || module->header.insnum > MAX_INSTRUMENTS + || module->header.smpnum > MAX_SAMPLES + || module->header.patnum > MAX_PATTERNS) + { + fprintf(stderr, "sackit_module_load: header limits exceeded\n"); + sackit_module_free(module); + return NULL; + } + + module->header.song_name[25] = 0x00; + //printf("module name: \"%s\"\n", module->header.song_name); + + if(reader->read(reader, module->orders, module->header.ordnum) != 1) + { + fprintf(stderr, "sackit_module_load: could not read orderlist\n"); + sackit_module_free(module); + return NULL; + } + + static uint32_t offset_instruments[MAX_INSTRUMENTS]; + static uint32_t offset_samples[MAX_SAMPLES]; + static uint32_t offset_patterns[MAX_PATTERNS]; + + if((module->header.insnum != 0 && reader->read(reader, offset_instruments, module->header.insnum*4) != 1) + || (module->header.smpnum != 0 && reader->read(reader, offset_samples, module->header.smpnum*4) != 1) + || (module->header.patnum != 0 && reader->read(reader, offset_patterns, module->header.patnum*4) != 1)) + { + fprintf(stderr, "sackit_module_load: could not read pointers from header\n"); + sackit_module_free(module); + return NULL; + } + + // instruments + for(i = 0; i < module->header.insnum; i++) + { + reader->seek(reader, fboffs + offset_instruments[i], SEEK_SET); + module->instruments[i] = malloc(sizeof(it_instrument_t)); + +#ifdef _MSC_VER + reader->read(reader, module->instruments[i], sizeof(it_instrument_t)); +#else + //reader->read(reader, module->instruments[i], sizeof(it_instrument_t)); + // XXX: work around a compiler bug in MinGW GCC 4.7.2 + reader->read(reader, module->instruments[i], (void *)(&((it_instrument_t *)0)->evol) - (void *)0); +#endif + + for(j = 0; j < 3; j++) + { + it_envelope_t *ev = NULL; + + switch(j) + { + case 0: ev = &module->instruments[i]->evol; break; + case 1: ev = &module->instruments[i]->epan; break; + case 2: ev = &module->instruments[i]->epitch; break; + } + + ev->flg = reader->getch(reader); + ev->num = reader->getch(reader); + ev->lpb = reader->getch(reader); + ev->lpe = reader->getch(reader); + ev->slb = reader->getch(reader); + ev->sle = reader->getch(reader); + + for(k = 0; k < 25; k++) + { + int vy = reader->getch(reader); + int vxl = reader->getch(reader); + int vxh = reader->getch(reader); + + ev->points[k].y = vy; + ev->points[k].x = vxl | (vxh<<8); + } + + reader->getch(reader); + } + } + + // samples + for(i = 0; i < module->header.smpnum; i++) + { + reader->seek(reader, fboffs + offset_samples[i], SEEK_SET); + it_sample_t *smp = malloc(sizeof(it_sample_t)); + module->samples[i] = smp; + reader->read(reader, smp, sizeof(it_sample_t)-sizeof(int16_t *)); + + smp->data = NULL; + if(smp->samplepointer != 0 && smp->length != 0 && (smp->flg & IT_SMP_EXISTS)) + { + // NO WE ARE NOT SUPPORTING STEREO SAMPLES PISS OFF MODPLUG + reader->seek(reader, fboffs + smp->samplepointer, SEEK_SET); + smp->data = malloc(smp->length*sizeof(int16_t)); + + // check compression flag + if(smp->flg & IT_SMP_COMPRESS) + { + // worst case scenario + static uint8_t buf[65538]; + + // calc some things + int blkunlen = (smp->flg & IT_SMP_16BIT + ? 0x4000 + : 0x8000); + int blkbasewidth = (smp->flg & IT_SMP_16BIT + ? 17 + : 9); + int blkbaseshift = (smp->flg & IT_SMP_16BIT + ? 0 + : 8); + int blkbasebits = (smp->flg & IT_SMP_16BIT + ? 4 + : 3); + + int offs; + for(offs = 0; offs < (int)smp->length; offs += blkunlen) + { + // clear block + for(j = 0; j < blkunlen && j+offs < (int)smp->length; j++) + smp->data[j+offs] = 0; + + // get length + reader->read(reader, buf, 2); + int blklen = buf[0]|(buf[1]<<8); + + // get block + reader->read(reader, buf, blklen); + + //printf("block = %i bytes\n", blklen); + + // decompress + int boffs = 0; + int dw = blkbasewidth; + for(j = 0; j < blkunlen && j+offs < (int)smp->length && boffs < (blklen<<3); j++) + { + //printf("%08X %i %i\n", offs, j, dw); + int bbigoffs, bsuboffs; + bbigoffs = (boffs>>3); + bsuboffs = (boffs&7); + + // read value + int v = buf[bbigoffs] + |(buf[bbigoffs+1]<<8) + |(buf[bbigoffs+2]<<16); + v >>= bsuboffs; + v &= (1<>3); + bsuboffs = (boffs&7); + v = buf[bbigoffs] + |(buf[bbigoffs+1]<<8); + v >>= bsuboffs; + v &= (1<= dw) + v++; + + // change bit width + dw = v; + j--; + continue; + } + } else if(dw == blkbasewidth) { + //printf("TYPE C\n"); + // type C: bps+1 bits + // is the top bit set? + if(v & (1<<(dw-1))) + { + // use the bottom 8 bits + // TODO: confirm this is what happens + dw = v&255; + dw++; + + // is this out of range? + if(dw > blkbasewidth || dw == 0) + { + // bail out + fprintf(stderr, + "IT214 block error [%08X/%08X/%i]: invalid width %i\n" + , reader->tell(reader) - blklen - 2 + , reader->tell(reader) - blklen + (boffs>>3) + , j + , dw); + break; + } + + j--; + continue; + } + } else { + // type B: 7 through bps bits + // is this 01...1x000 through 10...0y111? + // ( 8bps: x=1, y=0) + // (16bps: x=0, y=1) + if(v >= (1<<(dw-1))-(1<<(blkbasebits-1)) + || v <= ~((1<<(dw-1))-(1<<(blkbasebits-1)))) + { + // steal next 3/4 bits + v += (1<<(blkbasebits-1)); + v &= 15; + + // calculate new bit width + v++; + if(v >= dw) + v++; + + // change bit width + dw = v; + j--; + continue; + } + } + + // store value + //printf("v = %i\n", v); + smp->data[j+offs] = v<length; j++) + smp->data[j+offs] += smp->data[j+offs-1]; + + // convert + if(!(smp->cvt & 0x01)) + { + // TODO! + } + + if(smp->cvt & 0x04) + { + for(j = 1; j < blkunlen && j+offs < (int)smp->length; j++) + smp->data[j+offs] += smp->data[j+offs-1]; + } + + // TODO: not repeat ourselves + // TODO: other conversion flags + } + } else { + // load + if(smp->flg & IT_SMP_16BIT) + { + reader->read(reader, smp->data, smp->length*2); + } else { + for(j = 0; j < (int)smp->length; j++) + smp->data[j] = (reader->getch(reader))<<8; + } + + // convert + if(!(smp->cvt & 0x01)) + { + // TODO! + for(j = 0; j < (int)smp->length; j++) + smp->data[j] ^= 0x8000; + } + + // TODO: other conversion flags + } + } + } + + // patterns + for(i = 0; i < module->header.patnum; i++) + { + reader->seek(reader, fboffs + offset_patterns[i], SEEK_SET); + module->patterns[i] = malloc(sizeof(it_pattern_t)); + reader->read(reader, module->patterns[i], sizeof(it_pattern_t)-65536); + reader->read(reader, 8+(uint8_t *)module->patterns[i], module->patterns[i]->length); + } + + return module; +} + +void sackit_playback_free(sackit_playback_t *sackit) +{ + if(sackit->buf != NULL) + free(sackit->buf); + if(sackit->mixbuf != NULL) + free(sackit->mixbuf); + + free(sackit); +} + +void sackit_playback_reset_env(sackit_envelope_t *aenv, int8_t def) +{ + aenv->carry_idx = aenv->idx = 0; + aenv->carry_x = aenv->x = 0; + aenv->def = def; + aenv->y = def*256; + aenv->carry_flags = aenv->flags = 0; +} + +void sackit_playback_reset_achn(sackit_achannel_t *achn) +{ + achn->note = 253; + + achn->freq = 0; + achn->ofreq = 0; + achn->offs = 0; + achn->suboffs = 0; + achn->suboffs_f = 0.0f; + + achn->flags = 0; + + achn->instrument = NULL; + achn->sample = NULL; + + achn->sv = 0; + achn->vol = 0; + achn->fv = 0; + achn->cv = 0; + achn->iv = 128; + achn->pan = 32; + + achn->anticlick[0] = 0; + achn->anticlick[1] = 0; + achn->anticlick_f[0] = 0.0f; + achn->anticlick_f[1] = 0.0f; + + achn->svib_speed = 0; + achn->svib_type = 0; + achn->svib_depth = 0; + achn->svib_rate = 0; + achn->svib_power = 0; + achn->svib_offs = 0; + + achn->lramp = 0; + + achn->fadeout = 1024; + + achn->filt_cut = 127; + achn->filt_res = 0; + achn->filt_prev[0][0] = 0.0f; + achn->filt_prev[0][1] = 0.0f; + achn->filt_prev[1][0] = 0.0f; + achn->filt_prev[1][1] = 0.0f; + + achn->next = achn->prev = NULL; + achn->parent = NULL; + + sackit_playback_reset_env(&(achn->evol), 64); + sackit_playback_reset_env(&(achn->epan), 0); + sackit_playback_reset_env(&(achn->epitch), 0); +} + +void sackit_playback_reset_pchn(sackit_pchannel_t *pchn) +{ + pchn->achn = NULL; + pchn->bg_achn = NULL; + pchn->tfreq = 0; + pchn->nfreq = 0; + pchn->freq = 0; + pchn->note = 253; + pchn->lins = 0; + + pchn->cv = 64; + pchn->vol = 0; + pchn->pan = 32; + + pchn->slide_vol = 0; + pchn->slide_vol_cv = 0; + pchn->slide_vol_gv = 0; + pchn->slide_pan = 0; + pchn->slide_pitch = 0; + pchn->slide_porta = 0; + pchn->arpeggio = 0; + pchn->note_cut = 0; + pchn->note_delay = 0; + pchn->vib_speed = 0; + pchn->vib_depth = 0; + pchn->vib_offs = 0; + pchn->vib_type = 0; + pchn->vib_lins = 0; + pchn->tre_speed = 0; + pchn->tre_depth = 0; + pchn->tre_offs = 0; + pchn->tre_type = 0; + pchn->trm_val = 0; + pchn->trm_flags = 0; + pchn->trm_cur_on = 0; + pchn->trm_cur_off = 0; + pchn->rtg_val = 0; + pchn->rtg_flags = 0; + pchn->rtg_counter = 0; + + pchn->loop_start = 0; + pchn->loop_times = 0; + + pchn->nna = 0; + + pchn->eff_slide_vol = 0; + pchn->eff_slide_vol_cv = 0; + pchn->eff_slide_vol_gv = 0; + pchn->eff_slide_vol_veff = 0; + pchn->eff_slide_pitch = 0; + pchn->eff_slide_porta = 0; + pchn->eff_sample_offs = 0; + pchn->eff_misc = 0; + pchn->eff_arpeggio = 0; + pchn->eff_vibrato = 0; + pchn->eff_tremolo = 0; + pchn->eff_tempo = 0; + pchn->eff_tremor = 0x11; + pchn->eff_retrig = 0x00; + + pchn->filt_cut = 127; + pchn->filt_res = 0; + + pchn->instrument = NULL; + pchn->sample = NULL; +} + +void sackit_playback_reset2(sackit_playback_t *sackit, int buf_len, int achn_count, + void (*f_mix)(sackit_playback_t *sackit, int offs, int len), int mixer_bytes, int freq) +{ + int i; + + sackit->current_tick = 1; + sackit->max_tick = sackit->module->header.is; + sackit->row_counter = 1; + sackit->tempo_inc = 0; + + sackit->current_row = 0xFFFE; + sackit->process_row = 0xFFFE; + sackit->break_row = 0; + sackit->number_of_rows = 64; + + sackit->current_pattern = 0; + sackit->process_order = -1; + + sackit->pat_ptr = 0; + sackit->pat_row = 0; + + sackit->gv = sackit->module->header.gv; + sackit->mv = sackit->module->header.mv; + sackit->anticlick[0] = 0; + sackit->anticlick[1] = 0; + sackit->anticlick_f[0] = 0.0f; + sackit->anticlick_f[1] = 0.0f; + + sackit->tempo = sackit->module->header.it; + + sackit->achn_count = achn_count; + sackit->f_mix = f_mix; + sackit->mixer_bytes = mixer_bytes; + sackit->freq = freq; + sackit->buf_len = buf_len; + sackit->buf_tick_rem = 0; + //printf("%i\n", buf_len); + sackit->buf = malloc(sizeof(int16_t)*mixer_bytes*sackit->buf_len); + sackit->mixbuf = malloc(sizeof(int32_t)*mixer_bytes*sackit->buf_len); + + for(i = 0; i < SACKIT_MAX_ACHANNEL; i++) + sackit_playback_reset_achn(&(sackit->achn[i])); + for(i = 0; i < 64; i++) + { + sackit_playback_reset_pchn(&(sackit->pchn[i])); + + /*sackit->pchn[i].achn = &(sackit->achn[i]); + sackit->pchn[i].achn->parent = &(sackit->pchn[i]);*/ + + sackit->pchn[i].cv = sackit->module->header.chnl_vol[i]; + sackit->pchn[i].pan = sackit->module->header.chnl_pan[i]; + } +} + +void sackit_playback_reset(sackit_playback_t *sackit, int buf_len, int achn_count, int mixeridx) +{ + // deprecated function + sackit_playback_reset2(sackit, buf_len, achn_count, + fnlist_itmixer[mixeridx], itmixer_bytes[mixeridx], 44100); + +} + +sackit_playback_t *sackit_playback_new2(it_module_t *module, int buf_len, int achn_count, + void (*f_mix)(sackit_playback_t *sackit, int offs, int len), int mixer_bytes, int freq) +{ + // allocate + sackit_playback_t *sackit = malloc(sizeof(sackit_playback_t)); + sackit->module = module; + sackit_playback_reset2(sackit, buf_len, achn_count, f_mix, mixer_bytes, freq); + + return sackit; +} +sackit_playback_t *sackit_playback_new(it_module_t *module, int buf_len, int achn_count, int mixeridx) +{ + // deprecated function + return sackit_playback_new2(module, buf_len, achn_count, + fnlist_itmixer[mixeridx], itmixer_bytes[mixeridx], 44100); +} + +it_module_t *sackit_module_load(const char *fname) +{ + return sackit_module_load_offs(fname, 0); +} + diff --git a/xlibinc/sackit/playroutine.c b/xlibinc/sackit/playroutine.c new file mode 100644 index 0000000..da7bc12 --- /dev/null +++ b/xlibinc/sackit/playroutine.c @@ -0,0 +1,563 @@ +#include "sackit_internal.h" + +void (*(fnlist_itmixer[]))(sackit_playback_t *sackit, int offs, int len) = { + sackit_playback_mixstuff_it211, + sackit_playback_mixstuff_it211s, + sackit_playback_mixstuff_it211l, + sackit_playback_mixstuff_it211ls, + sackit_playback_mixstuff_it212, + sackit_playback_mixstuff_it212s, + sackit_playback_mixstuff_it212l, + sackit_playback_mixstuff_it212ls, + sackit_playback_mixstuff_it214, + sackit_playback_mixstuff_it214s, + sackit_playback_mixstuff_it214l, + sackit_playback_mixstuff_it214ls, + sackit_playback_mixstuff_it214c, + sackit_playback_mixstuff_it214cs, + sackit_playback_mixstuff_it214f, + sackit_playback_mixstuff_it214fs, + sackit_playback_mixstuff_it214fl, + sackit_playback_mixstuff_it214fls, + sackit_playback_mixstuff_it214fc, + sackit_playback_mixstuff_it214fcs, + sackit_playback_mixstuff_intfast_a, + sackit_playback_mixstuff_intfast_as, +}; + +int itmixer_bytes[] = { 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4 }; + +void sackit_update_effects(sackit_playback_t *sackit) +{ + int i; + + sackit->tempo += sackit->tempo_inc; + if(sackit->tempo < 32) + sackit->tempo = 32; + if(sackit->tempo > 255) + sackit->tempo = 255; + + for(i = 0; i < 64; i++) + { + sackit_pchannel_t *pchn = &(sackit->pchn[i]); + + sackit_effect_volslide_cv(sackit, pchn, pchn->slide_vol_cv); + sackit_effect_volslide_gv(sackit, pchn, pchn->slide_vol_gv); + sackit_effect_volslide(sackit, pchn, pchn->slide_vol); + sackit_effect_retrig(sackit, pchn, 0); + + // TODO: confirm order + sackit_effect_pitchslide(sackit, pchn, pchn->slide_pitch); + sackit_effect_portaslide(sackit, pchn, pchn->slide_porta); + + if(pchn->achn != NULL) + pchn->achn->ofreq = pchn->achn->freq; + + sackit_effect_vibrato(sackit, pchn); + sackit_effect_tremolo(sackit, pchn); + + uint16_t arp = (pchn->arpeggio>>4)&15; + if(arp != 0) + { + uint32_t arpmul = (uint32_t)pitch_table[(arp+60)*2+1]; + arpmul <<= 16; + arpmul += (uint32_t)pitch_table[(arp+60)*2]; + if(pchn->achn != NULL) + pchn->achn->ofreq = sackit_mul_fixed_16_int_32(arpmul, pchn->achn->ofreq); + } + + pchn->arpeggio = ((pchn->arpeggio<<4)&0xFFF)|((pchn->arpeggio>>8)&15); + + sackit_effect_tremor(sackit, pchn); + + if(pchn->note_cut != 0) + { + pchn->note_cut--; + if(pchn->note_cut == 0) + sackit_nna_note_cut(sackit, pchn->achn); + } + + if(pchn->note_delay != 0) + { + pchn->note_delay--; + if(pchn->note_delay == 0) + { + sackit_update_effects_chn(sackit, pchn + ,pchn->note_delay_note + ,pchn->note_delay_ins + ,pchn->note_delay_vol + ,0,0); + pchn->note_delay = sackit->max_tick; + } + } + } +} + +void sackit_update_pattern(sackit_playback_t *sackit) +{ + int i; + + it_pattern_t *pat = sackit->module->patterns[sackit->current_pattern]; + int ptr = sackit->pat_ptr; + uint8_t *data = (pat == NULL ? NULL : pat->data); + + if(sackit->pat_row > sackit->process_row) + { + sackit->pat_row = 0; + ptr = 0; + } + + uint8_t note[64], ins[64], vol[64], eft[64], efp[64]; + + for(i = 0; i < 64; i++) + { + note[i] = 253; + ins[i] = 0; + vol[i] = 255; + eft[i] = 0; + efp[i] = 0; + } + + //printf("pat_row %i %i\n", sackit->pat_row, sackit->process_row); + + while(sackit->pat_row <= sackit->process_row) + { + while(data != NULL && data[ptr] != 0x00) + { + uint8_t cval = data[ptr++]; + uint8_t chn = ((cval-1)&0x3F); + sackit_pchannel_t *pchn = &(sackit->pchn[chn]); + + if(cval&0x80) + pchn->lmask = data[ptr++]; + + if(pchn->lmask&0x01) + pchn->ldata[0] = data[ptr++]; + if(pchn->lmask&0x02) + pchn->ldata[1] = data[ptr++]; + if(pchn->lmask&0x04) + pchn->ldata[2] = data[ptr++]; + if(pchn->lmask&0x08) + { + pchn->ldata[3] = data[ptr++]; + pchn->ldata[4] = data[ptr++]; + } + + if(sackit->pat_row == sackit->process_row) + { + if(pchn->lmask&0x11) + note[chn] = pchn->ldata[0]; + if(pchn->lmask&0x22) + ins[chn] = pchn->ldata[1]; + if(pchn->lmask&0x44) + vol[chn] = pchn->ldata[2]; + if(pchn->lmask&0x88) + { + eft[chn] = pchn->ldata[3]; + efp[chn] = pchn->ldata[4]; + } + } + } + ptr++; + + sackit->pat_row++; + } + + sackit->tempo_inc = 0; + + for(i = 0; i < 64; i++) + sackit_update_effects_chn(sackit, &(sackit->pchn[i]), + note[i], ins[i], vol[i], eft[i], efp[i]); + + sackit->pat_ptr = ptr; +} + +void sackit_env_update(sackit_playback_t *sackit, sackit_achannel_t *achn + , sackit_envelope_t *aenv, it_envelope_t *ienv) +{ + if(!(ienv->flg & IT_ENV_ON)) + { + aenv->y = 256*(int32_t)aenv->def; + return; + } + + // TODO: check the case where points[0].x != 0 + // TODO: check the case where lpbeg/end are out of range + // TODO: clamp x correctly + + int lpbeg, lpend; + + int can_fade = 1; + int can_bail = 1; + + lpbeg = lpend = ienv->num-1; + + if(ienv->flg & IT_ENV_LOOP) + { + lpbeg = ienv->lpb; + lpend = ienv->lpe; + can_fade = 0; + can_bail = 0; + } + + if((ienv->flg & IT_ENV_SUSLOOP) && (achn->flags & SACKIT_ACHN_SUSTAIN)) + { + lpbeg = ienv->slb; + lpend = ienv->sle; + can_fade = 0; + can_bail = 0; + } + + int iy0 = ienv->points[aenv->idx].y; + int iy1 = ienv->points[aenv->idx+1].y; + int ix0 = ienv->points[aenv->idx].x; + int ix1 = ienv->points[aenv->idx+1].x; + if(aenv->x <= ix0) + { + aenv->y = iy0*256; + } else if(aenv->x >= ix1) { + aenv->y = iy1*256; + } else { + // TODO: get correct rounding + aenv->y = iy0*256 + (256*(iy1-iy0)*(aenv->x-ix0))/(ix1-ix0); + } + + aenv->x++; + //printf("k %i %i\n",lpend,aenv->x); + if(aenv->x > ix1 || aenv->idx == lpend) + { + aenv->idx++; + + if(aenv->idx >= lpend) + { + aenv->idx = lpbeg; + aenv->x = ienv->points[lpbeg].x; + //printf("E %i %i\n",aenv->x,aenv->def); + + if(aenv->def == 64) + { + if(can_fade) + sackit_nna_note_fade(sackit, achn); + if(can_bail && ienv->points[lpend].y == 0) + { + sackit_nna_note_cut(sackit, achn); + //printf("loldie\n"); + } + } + } + // biscuitworld.it exposes a weird quirk caused by modplug tracker sucking ass + if(aenv->x > ienv->points[aenv->idx].x) + aenv->x = ienv->points[aenv->idx].x+1; + } +} + +/* + +once again, ITTECH.TXT: + + ┌─────────────────────────────────────────────────────────┐ + │ Set note volume to volume set for each channel │ + │ Set note frequency to frequency set for each channel │ + └────────────┬────────────────────────────────────────────┘ + │ + ┌────────────┴────────────┐ + │ Decrease tick counter │ Yes + │ Is tick counter 0 ? ├─────────────────────────┐ + └────────────┬────────────┘ │ + │ │ + No │ ┌─────────────────────┴──────────────────┐ + ┌────────────┴────────────┐ │ Tick counter = Tick counter set │ + │ Update effects for each │ │ (the current 'speed') │ + │ channel as required. │ │ Decrease Row counter. │ + │ │ │ Is row counter 0? │ + └───┬─────────────────────┘ └────────────┬──────────┬────────────────┘ + │ No │ │ + │ ┌─────────────────────┘ │ Yes + │ │ │ + │ ┌─────────────┴──────────────┐ ┌───────────────┴────────────────┐ + │ │ Call update-effects for │ │ Row counter = 1 │ + │ │ each channel. │ │ │ + │ └──────────────┬─────────────┘ │ Increase ProcessRow │ + │ │ │ Is ProcessRow > NumberOfRows? │ + ├─────────────────┘ └────────┬──────────────────┬────┘ + │ Yes │ │ No + │ ┌────────────────────────────────┴──────────────┐ │ + │ │ ProcessRow = BreakRow │ │ + │ │ BreakRow = 0 │ │ + │ │ Increase ProcessOrder │ │ + │ │ while Order[ProcessOrder] = 0xFEh, │ │ + │ │ increase ProcessOrder │ │ + │ │ if Order[ProcessOrder] = 0xFFh, │ │ + │ │ ProcessOrder = 0 │ │ + │ │ CurrentPattern = Order[ProcessOrder] │ │ + │ └─────────────────────┬─────────────────────────┘ │ + │ │ │ + │ ├─────────────────────────────┘ + │ │ + │ ┌─────────────────────┴──────────────────────────┐ + │ │ CurrentRow = ProcessRow │ + │ │ Update Pattern Variables (includes jumping to │ + │ │ the appropriate row if requried and getting │ + │ │ the NumberOfRows for the pattern) │ + │ └─────────────────────┬──────────────────────────┘ + │ │ + ├───────────────────────────────┘ + │ + ┌───┴───────────────┐ Yes ┌───────────────────────────────┐ + │ Instrument mode? ├──────────────────┤ Update Envelopes as required │ + └───┬───────────────┘ │ Update fadeout as required │ + │ │ Calculate final volume if req │ + │ No (Sample mode) │ Calculate final pan if req │ + │ │ Process sample vibrato if req │ + ┌───┴─────────────────────────────────┐└───────────────┬───────────────┘ + │ Calculate final volume if required │ │ + │ Calculate final pan if requried │ │ + │ Process sample vibrato if required │ │ + └───┬─────────────────────────────────┘ │ + │ │ + │ │ + └─────────────────────────┬────────────────────────┘ + │ + ┌────────────────┴──────────────────┐ + │ Output sound!!! │ + └───────────────────────────────────┘ +*/ + + +void sackit_tick(sackit_playback_t *sackit) +{ + int i; + /* + printf("%i %i %i %i %i\n" + ,sackit->current_tick + ,sackit->max_tick + ,sackit->process_row + ,sackit->process_order + ,sackit->current_pattern);*/ + + for(i = 0; i < sackit->achn_count; i++) + { + sackit_achannel_t *achn = &(sackit->achn[i]); + // Set note volume to volume set for each channel + if(achn->parent != NULL && achn->parent->achn == achn) + achn->vol = achn->parent->vol; + // not on the graph D: + if(achn->parent != NULL && achn->parent->achn == achn) + achn->pan = achn->parent->pan; + + // Set note frequency to frequency set for each channel + if(achn->parent != NULL && achn->parent->achn == achn) + achn->freq = achn->parent->freq; + achn->ofreq = achn->freq; + } + + // Decrease tick counter + sackit->current_tick--; + + // Is tick counter 0 ? + if(sackit->current_tick == 0) + { + // Yes + // Tick counter = Tick counter set (the current 'speed') + sackit->current_tick = sackit->max_tick; + + // Decrease Row counter. + sackit->row_counter--; + + // Is row counter 0? + if(sackit->row_counter == 0) + { + // Yes + // Row counter = 1 + // NOTE: DONE LATER + sackit->row_counter = 0; + + // Increase ProcessRow + sackit->process_row++; + + // Is ProcessRow > NumberOfRows? + if(sackit->process_row >= sackit->number_of_rows) + { + // Yes + // ProcessRow = BreakRow + sackit->process_row = sackit->break_row; + + // BreakRow = 0 + sackit->break_row = 0; + + // Increase ProcessOrder + sackit->process_order++; + + // while Order[ProcessOrder] = 0xFEh, + // increase ProcessOrder + while(sackit->module->orders[sackit->process_order] == 0xFE) + sackit->process_order++; + + // if Order[ProcessOrder] = 0xFFh, + // ProcessOrder = 0 + if(sackit->module->orders[sackit->process_order] == 0xFF) + sackit->process_order = 0; + + // NOT LISTED ON CHART: Repeat the "while" loop --GM + while(sackit->module->orders[sackit->process_order] == 0xFE) + sackit->process_order++; + + // TODO: handle the case where we get 0xFF again + + // CurrentPattern = Order[ProcessOrder] + sackit->current_pattern = sackit->module->orders[sackit->process_order]; + sackit->number_of_rows = (sackit->module->patterns[sackit->current_pattern] == NULL + ? 64 + : sackit->module->patterns[sackit->current_pattern]->rows); + sackit->pat_row = -1; + + // clear the pattern previous values + // atrk-bu spits out broken files D: + for(i=0;i<64;i++) + { + sackit_pchannel_t *pchn = &(sackit->pchn[i]); + pchn->lmask = 0; + pchn->ldata[0] = 253; + pchn->ldata[1] = 0; + pchn->ldata[2] = 255; + pchn->ldata[3] = 0; + pchn->ldata[4] = 0; + } + } + + // CurrentRow = ProcessRow + sackit->current_row = sackit->process_row; + + // Update Pattern Variables (includes jumping to + // the appropriate row if requried and getting + // the NumberOfRows for the pattern) + sackit_update_pattern(sackit); + + // Row counter = 1 + // (later than noted in ITTECH.TXT) + if(sackit->row_counter == 0) + sackit->row_counter = 1; + } else { + // No + // Call update-effects for each channel. + sackit_update_effects(sackit); + } + } else { + // No + // Update effects for each channel as required. + sackit_update_effects(sackit); + } + + // ---------------------------------------------------- + + // Instrument mode? + // TODO! + if(sackit->module->header.flags & IT_MOD_INSTR) + { + // Yes + for(i = 0; i < sackit->achn_count; i++) + { + sackit_achannel_t *achn = &(sackit->achn[i]); + if(achn->flags & SACKIT_ACHN_PLAYING) + { + // Update Envelopes as required + sackit_env_update(sackit, achn, &(achn->evol), &(achn->instrument->evol)); + sackit_env_update(sackit, achn, &(achn->epan), &(achn->instrument->epan)); + sackit_env_update(sackit, achn, &(achn->epitch), &(achn->instrument->epitch)); + + if(achn->instrument != NULL) + { + if(achn->epitch.y != 0 && !(achn->instrument->epitch.flg & IT_ENV_FILTER)) + { + // TODO: analyse this more closely + if(sackit->module->header.flags & IT_MOD_LINEAR) + achn->ofreq = sackit_pitchslide_linear(achn->ofreq, achn->epitch.y/32); + else + achn->ofreq = sackit_pitchslide_amiga_fine(achn->ofreq, achn->epitch.y/4); + } + } + + // Update fadeout as required + if(achn->flags & SACKIT_ACHN_FADEOUT) + { + achn->fadeout -= achn->instrument->fadeout; + if(achn->fadeout <= 0) + { + sackit_nna_note_cut(sackit, achn); + } + } + } + } + } + /* + from ITTECH.TXT: + + Abbreviations: + FV = Final Volume (Ranges from 0 to 128). In versions 1.04+, mixed output + devices are reduced further to a range from 0 to 64 due to lack of + memory. + Vol = Volume at which note is to be played. (Ranges from 0 to 64) + SV = Sample Volume (Ranges from 0 to 64) + IV = Instrument Volume (Ranges from 0 to 128) + CV = Channel Volume (Ranges from 0 to 64) + GV = Global Volume (Ranges from 0 to 128) + VEV = Volume Envelope Value (Ranges from 0 to 64) + + In Sample mode, the following calculation is done: + FV = Vol * SV * CV * GV / 262144 + Note that 262144 = 2^18 - So bit shifting can be done. + + In Instrument mode the following procedure is used: + 1) Update volume envelope value. Check for loops / end of envelope. + 2) If end of volume envelope (ie. position >= 200 or VEV = 0FFh), then turn + on note fade. + 3) If notefade is on, then NoteFadeComponent (NFC) = NFC - FadeOut + ; NFC should be initialised to 1024 when a note is played. + 4) FV = Vol * SV * IV * CV * GV * VEV * NFC / 2^41 + */ + + for(i = 0; i < sackit->achn_count; i++) + { + sackit_achannel_t *achn = &(sackit->achn[i]); + + // Calculate final volume if required + // TODO! + + // Calculate final pan if requried + // FIXME: Lacking stereo wavewriter! Can only guess here! + // TODO! + + // Process sample vibrato if required + sackit_effect_samplevibrato(sackit, achn); + } + + // ---------------------------------------------------- + + // Output sound!!! + // -- handled elsewhere +} + +void sackit_playback_update(sackit_playback_t *sackit) +{ + int offs = 0; + + while(offs+sackit->buf_tick_rem <= sackit->buf_len) + { + if(sackit->buf_tick_rem != 0) + { + sackit->f_mix(sackit, offs, sackit->buf_tick_rem); + } + offs += sackit->buf_tick_rem; + + sackit_tick(sackit); + sackit->buf_tick_rem = (sackit->freq*10)/(sackit->tempo*4); + } + + if(offs != (int)sackit->buf_len) + { + sackit->f_mix(sackit, offs, sackit->buf_len-offs); + sackit->buf_tick_rem -= sackit->buf_len-offs; + } + //printf("rem %i row %i\n", sackit->buf_tick_rem, sackit->process_row); +} + diff --git a/xlibinc/sackit/playroutine_effects.c b/xlibinc/sackit/playroutine_effects.c new file mode 100644 index 0000000..4e8befe --- /dev/null +++ b/xlibinc/sackit/playroutine_effects.c @@ -0,0 +1,911 @@ +#include "sackit_internal.h" + +void sackit_filter_calc(sackit_playback_t *sackit, sackit_achannel_t *achn) +{ + int cut = achn->filt_cut<<8; + if(achn->instrument != NULL && (achn->instrument->epitch.flg & IT_ENV_FILTER) != 0 && (achn->instrument->epitch.flg & IT_ENV_ON) != 0) + cut = (cut*(achn->epitch.y+8192))>>14; + + if(cut < 0) + { + printf("ERROR: cutoff should NOT be negative! (%i)\n", cut); + fflush(stdout); + abort(); + } + + //if(cut != 127*256) + // printf("%i\n", cut); + + int res = achn->filt_res; + + if(cut == 127*256 && res == 0) + { + achn->filt_coeff[0] = 1; + achn->filt_coeff[1] = 0; + achn->filt_coeff[2] = 0; + return; + } + + float r = pow(2.0, cut * -0.00016276040696538985) * 0.0012166619999334216 * sackit->freq; + float d2 = quality_factor_table[res]; + //printf("%.9f %.9f\n", r, d2); + + float d = d2 * (r + 1.0f) - 1.0f; + float e = r*r; + + float a = 1.0f / (1.0f + d + e); + float b = (d + 2.0f*e) * a; + float c = -e * a; + + achn->filt_coeff[0] = a; + achn->filt_coeff[1] = b; + achn->filt_coeff[2] = c; + //printf("filt %f %f %f | %i %i\n", a,b,c, cut, res); +} + +void sackit_note_retrig(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int offs) +{ + sackit_achannel_t *lachn = pchn->achn; + sackit_nna_allocate(sackit, pchn); + + pchn->achn->instrument = pchn->instrument; + pchn->achn->sample = pchn->sample; + + pchn->achn->note = pchn->note; + + pchn->achn->freq = pchn->freq; + pchn->achn->offs = offs; + pchn->achn->suboffs = 0; + pchn->achn->suboffs_f = 0.0; + pchn->achn->cv = pchn->cv; + pchn->achn->filt_cut = pchn->filt_cut; + pchn->achn->filt_res = pchn->filt_res; + if(pchn->instrument != NULL) + pchn->achn->iv = pchn->instrument->gbv; + if(pchn->sample != NULL) + { + pchn->achn->sv = pchn->sample->gvl; + pchn->achn->svib_speed = pchn->sample->vis; + pchn->achn->svib_depth = pchn->sample->vid; + pchn->achn->svib_type = pchn->sample->vit; + pchn->achn->svib_rate = pchn->sample->vir; + + // TODO: work out what to do with old effects mode + if(pchn->achn->offs >= (int32_t)pchn->sample->length) + pchn->achn->offs = 0; + } + pchn->achn->svib_offs = 0; // TODO: confirm + pchn->achn->svib_power = 0; + + pchn->achn->flags |= ( + SACKIT_ACHN_MIXING + |SACKIT_ACHN_PLAYING + |SACKIT_ACHN_RAMP + |SACKIT_ACHN_SUSTAIN); + + if(pchn->achn != lachn && lachn != NULL) + { + if(pchn->achn->instrument == lachn->instrument) + { + pchn->achn->evol.x = lachn->evol.x; + pchn->achn->evol.idx = lachn->evol.idx; + pchn->achn->epan.x = lachn->epan.x; + pchn->achn->epan.idx = lachn->epan.idx; + pchn->achn->epitch.x = lachn->epitch.x; + pchn->achn->epitch.idx = lachn->epitch.idx; + } + } + + if(pchn->instrument == NULL || (pchn->instrument->evol.flg & IT_ENV_CARRY) == 0) + { + pchn->achn->evol.x = 0; + pchn->achn->evol.idx = 0; + } + if(pchn->instrument == NULL || (pchn->instrument->epan.flg & IT_ENV_CARRY) == 0) + { + pchn->achn->epan.idx = 0; + pchn->achn->epan.x = 0; + } + if(pchn->instrument == NULL || (pchn->instrument->epitch.flg & IT_ENV_CARRY) == 0) + { + pchn->achn->epitch.x = 0; + pchn->achn->epitch.idx = 0; + } + + if(pchn->instrument != NULL) + { + pchn->achn->evol.flags = pchn->instrument->evol.flg; + pchn->achn->epan.flags = pchn->instrument->epan.flg; + pchn->achn->epitch.flags = pchn->instrument->epitch.flg; + } + + pchn->achn->fadeout = 1024; + + pchn->achn->flags &= ~( + SACKIT_ACHN_REVERSE + |SACKIT_ACHN_FADEOUT + |SACKIT_ACHN_BACKGND); +} + +void sackit_update_effects_chn(sackit_playback_t *sackit, sackit_pchannel_t *pchn, + uint8_t note, uint8_t ins, uint8_t vol, uint8_t eft, uint8_t efp) +{ + //if(note != 253) + // printf("N %i %i\n", note, ins); + + uint8_t vnote = note; + + pchn->slide_vol = 0; + pchn->slide_vol_cv = 0; + pchn->slide_vol_gv = 0; + pchn->slide_pan = 0; + pchn->slide_pitch = 0; + pchn->slide_porta = 0; + pchn->arpeggio = 0; + pchn->vib_speed = 0; + pchn->vib_depth = 0; + pchn->tre_speed = 0; + pchn->tre_depth = 0; + pchn->trm_flags &= ~1; + pchn->rtg_flags &= ~1; + pchn->rtg_val = 0; + + pchn->note_cut = 0; + pchn->note_delay = 0; + pchn->note_delay_note = note; + pchn->note_delay_ins = ins; + pchn->note_delay_vol = vol; + + int16_t slide_vol_now = 0; + int16_t slide_vol_cv_now = 0; + int16_t slide_vol_gv_now = 0; + int16_t slide_pan_now = 0; + int16_t slide_pitch_now = 0; + int16_t slide_pitch_fine_now = 0; + + int flag_slide_porta = 0; + int flag_retrig = 0; + int flag_vibrato = 0; + int flag_tremolo = 0; + int flag_done_instrument = 0; + int flag_nna_set = -1; + int flag_s7x = -1; + + int can_set_cut = 1; + int can_set_res = 1; + + uint32_t new_sample_offset = 0; + + uint8_t el = efp&15; + uint8_t eh = efp>>4; + int vfp = 0; + switch(eft) + { + case 0x01: // Axx - Set Speed (mislabelled as "Tempo" in ITTECH.TXT --GM) + if(efp != 0x00) + { + sackit->max_tick = efp; + sackit->current_tick = efp; + } + break; + + case 0x02: // Bxx - Jump to Order + sackit->process_order = efp - 1; + sackit->process_row = 0xFFFE; // indicates new pattern internally for IT... + break; + + case 0x03: // Cxx - Break to Row + sackit->break_row = efp; + sackit->process_row = 0xFFFE; + break; + + case 0x04: // Dxx - Volume slide + case 0x0B: // Kxx - (vibrato + vol slide) + case 0x0C: // Lxx - (porta to note + vol slide) + // TODO: confirm behaviour + if(efp == 0) + { + efp = pchn->eff_slide_vol; + el = efp&15; + eh = efp>>4; + } else { + pchn->eff_slide_vol = efp; + } + + if(el == 0) + pchn->slide_vol += eh; + else if(eh == 0) + pchn->slide_vol -= el; + else if(el == 0xF) + slide_vol_now += eh; + else if(eh == 0xF) + slide_vol_now -= el; + + if(efp == 0x0F || efp == 0xF0) + slide_vol_now += eh-el; + + efp = eh = el = 0; + break; + + case 0x05: // Exx - (pitch slide down) + case 0x06: // Fxx - (pitch slide up) + if(efp == 0) + { + efp = pchn->eff_slide_pitch; + } else { + pchn->eff_slide_pitch = efp; + } + + // TODO: confirm behaviour + if(efp <= 0xDF) + { + pchn->slide_pitch += (eft == 0x05 ? -1 : 1)*efp; + } else if(efp <= 0xEF) { + slide_pitch_fine_now += (eft == 0x05 ? -1 : 1)*(efp&15); + } else { + slide_pitch_now += (eft == 0x05 ? -1 : 1)*(efp&15); + } + break; + + case 0x09: // Ixx - (tremor) + if(efp == 0) + { + efp = pchn->eff_slide_pitch; + } else { + pchn->eff_slide_pitch = efp; + } + + pchn->eff_tremor = efp; + pchn->trm_val = efp; + pchn->trm_flags |= 1; + + break; + + case 0x0A: // Jxx - (arpeggio) + if(efp == 0) + { + efp = pchn->eff_arpeggio; + } else { + pchn->eff_arpeggio = efp; + } + pchn->arpeggio = efp; + break; + + case 0x0D: // Mxx - (channel volume) + // TODO: confirm behaviour + if(efp <= 64) + { + pchn->cv = efp; + if(pchn->achn != NULL) + pchn->achn->cv = efp; + } + break; + + case 0x0E: // Nxx - (channel volume slide) + // TODO: confirm behaviour + if(efp == 0) + { + efp = pchn->eff_slide_vol_cv; + el = efp&15; + eh = efp>>4; + } else { + pchn->eff_slide_vol_cv = efp; + } + + if(el == 0) + pchn->slide_vol_cv += eh; + else if(eh == 0) + pchn->slide_vol_cv -= el; + else if(el == 0xF) + slide_vol_cv_now += eh; + else if(eh == 0xF) + slide_vol_cv_now -= el; + + efp = eh = el = 0; + break; + + case 0x0F: // Oxx - (sample offset) + // TODO: get out-of-range behaviour correct! + if(efp == 0) + { + efp = pchn->eff_sample_offs; + } else { + pchn->eff_sample_offs = efp; + } + + // TODO: SAx + new_sample_offset = efp<<8; + + break; + + case 0x11: // Qxx - (retrigger) + if(efp == 0) + { + efp = pchn->eff_retrig; + } else { + pchn->eff_retrig = efp; + } + + pchn->rtg_flags |= 1; + pchn->rtg_val = efp; + + break; + + case 0x12: // Rxx - (tremolo) + // TODO: check if x,y independent + if((efp&0x0F) == 0) + efp |= (pchn->eff_tremolo&0x0F); + if((efp&0xF0) == 0) + efp |= (pchn->eff_tremolo&0xF0); + + pchn->eff_tremolo = efp; + + pchn->tre_speed += (efp>>4)*4; + pchn->tre_depth += (efp&15)*(eft == 0x15 ? 1 : 4); + + //if(!(sackit->module->header.flags & IT_MOD_OLDFX)) + flag_tremolo = 1; + break; + case 0x13: // Sxx - (miscellaneous) + if(efp == 0) + { + efp = pchn->eff_misc; + el = efp&15; + eh = efp>>4; + } else { + pchn->eff_misc = efp; + } + switch(eh) + { + case 0x6: // S6x - (delay by x ticks) + sackit->current_tick += el; + break; + case 0x7: // S7x - (misc ins stuff) + switch(el) + { + case 0x0: // S70 - (past note cut) + sackit_nna_past_note(sackit, pchn->achn, 0); + break; + case 0x1: // S71 - (past note off) + sackit_nna_past_note(sackit, pchn->achn, 2); + break; + case 0x2: // S72 - (past note fade) + sackit_nna_past_note(sackit, pchn->achn, 3); + break; + case 0x3: // S73 - (NNA = cut) + flag_nna_set = 0; + break; + case 0x4: // S74 - (NNA = continue) + flag_nna_set = 1; + break; + case 0x5: // S75 - (NNA = off) + flag_nna_set = 2; + break; + case 0x6: // S76 - (NNA = fade) + flag_nna_set = 3; + break; + case 0x7: // S77 - (vol env off) + flag_s7x = 0x7; + break; + case 0x8: // S78 - (vol env on) + flag_s7x = 0x8; + break; + case 0x9: // S79 - (pan env off) + flag_s7x = 0x9; + break; + case 0xA: // S7A - (pan env on) + flag_s7x = 0xA; + break; + case 0xB: // S7B - (pitch env off) + flag_s7x = 0xB; + break; + case 0xC: // S7C - (pitch env on) + flag_s7x = 0xC; + break; + } break; + case 0xB: // SBx - (loopback) + // TRIVIA: + // Before 1.04, this used song-global variables (as in S3M) + // Before 2.10, this didn't set the loop start after a successful looping. + if(el == 0) + { + // TODO: sort out the nasty SBx/Cxx/Bxx combos + // (IIRC there's only one weird one) + pchn->loop_start = sackit->current_row-1; + } else { + if(pchn->loop_times == 0) + { + pchn->loop_times = el; + } else { + pchn->loop_times--; + if(pchn->loop_times == 0) + { + pchn->loop_start = sackit->current_row; + break; + } + } + sackit->process_row = pchn->loop_start; + } + break; + case 0xC: // SCx - (note cut) + pchn->note_cut = (el == 0 ? 1 : el); + break; + case 0xD: // SDx - (note delay) + pchn->note_delay = (el == 0 ? 1 : el); + if(ins != 0) + pchn->lins = ins; + return; // cut this part! + break; + case 0xE: // SEx - (pattern delay) + if(sackit->row_counter == 0) + { + sackit->row_counter = el+1; + } + break; + } + break; + + case 0x14: // Txx - (tempo) + if(efp == 0) + { + efp = pchn->eff_tempo; + } else { + pchn->eff_tempo = efp; + } + + if(efp < 0x10) + { + sackit->tempo_inc = -efp; + } else if(efp < 0x20) { + sackit->tempo_inc = efp-0x10; + } else { + sackit->tempo = efp; + } + break; + + case 0x16: // Vxx - (global volume) + if(efp <= 0x80) + { + sackit->gv = efp; + } + break; + + case 0x17: // Wxx - (global volume slide) + // TODO: confirm behaviour + if(efp == 0) + { + efp = pchn->eff_slide_vol_gv; + el = efp&15; + eh = efp>>4; + } else { + pchn->eff_slide_vol_gv = efp; + } + + if(el == 0) + pchn->slide_vol_gv += eh; + else if(eh == 0) + pchn->slide_vol_gv -= el; + else if(el == 0xF) + slide_vol_gv_now += eh; + else if(eh == 0xF) + slide_vol_gv_now -= el; + + efp = eh = el = 0; + break; + + case 0x18: // Xxx - (panning) + pchn->pan = (pchn->pan & 0x80) | ((efp+2)>>2); + if(pchn->achn != NULL) + pchn->achn->pan = pchn->pan; + break; + + case 0x1A: // Zxx - (MIDI) + // TODO: load MIDI data from the file itself + if(efp <= 0x7F) + { + pchn->filt_cut = efp; + if(pchn->achn != NULL) + pchn->achn->filt_cut = pchn->filt_cut; + + can_set_cut = 0; + } else if(efp <= 0x8F) { + pchn->filt_res = (efp-0x80)*0x08; + if(pchn->achn != NULL) + pchn->achn->filt_res = pchn->filt_res; + + can_set_res = 0; + } + break; + } + + switch(eft) + { + case 0x07: // Gxx - (porta to note) + case 0x0C: // Lxx - (porta to note + vol slide) + if(efp == 0) + { + efp = (sackit->module->header.flags & IT_MOD_COMPGXX + ? pchn->eff_slide_porta + : pchn->eff_slide_pitch); + } else if(sackit->module->header.flags & IT_MOD_COMPGXX) { + pchn->eff_slide_porta = efp; + } else { + pchn->eff_slide_pitch = efp; + } + + pchn->slide_porta += efp; + flag_slide_porta = 1; + // TODO: confirm behaviour + break; + + case 0x08: // Hxx - (vibrato) + case 0x15: // Uxx - (fine vibrato) + case 0x0B: // Kxx - (vibrato + vol slide) + // TODO: check if x,y independent + if((efp&0x0F) == 0) + efp |= (pchn->eff_vibrato&0x0F); + if((efp&0xF0) == 0) + efp |= (pchn->eff_vibrato&0xF0); + + pchn->eff_vibrato = efp; + + pchn->vib_speed += (efp>>4)*4; + pchn->vib_depth += (efp&15)*(eft == 0x15 ? 1 : 4); + + //if(!(sackit->module->header.flags & IT_MOD_OLDFX)) + flag_vibrato = 1; + break; + } + + if(vol <= 64) + { + // volume + // (OPTIONAL: Feel free to emulate pre-voleffects stuff. + // (Turn the limit up to <= 127.)) + pchn->vol = vol; + if(pchn->achn != NULL) + pchn->achn->vol = pchn->vol; + } else if (vol <= 74) { + // Ax + if(vol == 65) + { + vfp = pchn->eff_slide_vol_veff; + } else { + pchn->eff_slide_vol_veff = vfp = ((int16_t)(vol-65)); + } + slide_vol_now += vfp; + } else if (vol <= 84) { + // Bx + if(vol == 75) + { + vfp = pchn->eff_slide_vol_veff; + } else { + pchn->eff_slide_vol_veff = vfp = ((int16_t)(vol-75)); + } + slide_vol_now -= vfp; + } else if (vol <= 94) { + // Cx + if(vol == 85) + { + vfp = pchn->eff_slide_vol_veff; + } else { + pchn->eff_slide_vol_veff = vfp = ((int16_t)(vol-85)); + } + pchn->slide_vol += vfp; + } else if (vol <= 104) { + // Dx + if(vol == 95) + { + vfp = pchn->eff_slide_vol_veff; + } else { + pchn->eff_slide_vol_veff = vfp = ((int16_t)(vol-95)); + } + pchn->slide_vol -= vfp; + } else if (vol <= 114) { + // Ex + if(vol == 105) + { + vfp = pchn->eff_slide_pitch; + } else { + pchn->eff_slide_pitch = vfp = ((int16_t)(vol-105))*4; + } + + pchn->slide_pitch -= vfp; + } else if (vol <= 124) { + // Fx + if(vol == 115) + { + vfp = pchn->eff_slide_pitch; + } else { + vfp = pchn->eff_slide_pitch = ((int16_t)(vol-115))*4; + } + + pchn->slide_pitch += vfp; + } else if (vol <= 127) { + // DO NOTHING + } else if (vol <= 192) { + // panning + pchn->pan = (pchn->pan & 0x80) | (vol-128); + if(pchn->achn != NULL) + pchn->achn->pan = pchn->pan; + } else if (vol <= 202) { + // Gx + + if(vol == 193) + { + vfp = (sackit->module->header.flags & IT_MOD_COMPGXX + ? pchn->eff_slide_porta + : pchn->eff_slide_pitch); + } else if(sackit->module->header.flags & IT_MOD_COMPGXX) { + pchn->eff_slide_porta = vfp = slide_table[vol-194]; + } else { + pchn->eff_slide_pitch = vfp = slide_table[vol-194]; + } + + pchn->slide_porta += vfp; + flag_slide_porta = 1; + } else if (vol <= 212) { + // Hx + } + + it_sample_t *psmp = pchn->sample; + if(ins != 0) + { + if(sackit->module->header.flags & IT_MOD_INSTR) + { + uint8_t xnote = (note <= 119 ? note : pchn->note); + + it_instrument_t *cins = sackit->module->instruments[ins-1]; + if(cins == NULL) + cins = pchn->instrument; + else + pchn->instrument = cins; + + // TODO: confirm behaviour + if(cins != NULL) + { + if(cins->notesample[xnote][1] != 0) + { + it_sample_t *csmp = sackit->module->samples[cins->notesample[xnote][1]-1]; + if(csmp != NULL) + pchn->sample = csmp; + } + + if(note <= 119) + vnote = cins->notesample[xnote][0]; + + if((cins->ifc & 0x80) != 0 && can_set_cut) + { + pchn->filt_cut = (cins->ifc & 0x7F); + if(pchn->achn != NULL) + pchn->achn->filt_cut = pchn->filt_cut; + } + + if((cins->ifr & 0x80) != 0 && can_set_res) + { + pchn->filt_res = (cins->ifr & 0x7F); + if(pchn->achn != NULL) + pchn->achn->filt_res = pchn->filt_res; + } + + flag_done_instrument = 1; + } + } else { + pchn->instrument = NULL; + it_sample_t *csmp = sackit->module->samples[ins-1]; + if(csmp != NULL) + pchn->sample = csmp; + } + + if(/*ins != pchn->lins && */pchn->instrument != NULL && flag_nna_set == -1) + { + flag_nna_set = pchn->instrument->nna; + } + + if(pchn->sample != NULL) + { + if(vol > 64) + { + pchn->vol = pchn->sample->vol; + if(pchn->achn != NULL && (flag_nna_set == -1 || flag_nna_set == 0)) + pchn->achn->vol = pchn->vol; + } + } + + if((( + pchn->achn == NULL || (!(pchn->achn->flags & SACKIT_ACHN_PLAYING)) + )|| pchn->lins != ins) + && note == 253 + && pchn->note != 253) + { + note = pchn->note; + } + + pchn->lins = ins; + } + + if(note <= 119) + { + // actual note + it_sample_t *csmp; + + if(sackit->module->header.flags & IT_MOD_INSTR) + { + uint8_t xnote = note; + + it_instrument_t *cins = pchn->instrument; + if(cins != NULL) + { + // TODO: confirm behaviour + if(cins->notesample[xnote][1] != 0) + { + csmp = sackit->module->samples[cins->notesample[xnote][1]-1]; + if(csmp != NULL) + pchn->sample = csmp; + } + + vnote = cins->notesample[xnote][0]; + } + } + + uint32_t nfreq = + ((uint32_t)(pitch_table[vnote*2])) + | (((uint32_t)(pitch_table[vnote*2+1]))<<16); + + //printf("N %i %i %i %i\n", note, vnote, ins, nfreq); + if(pchn->sample != NULL) + { + int flag_isnt_sliding = (pchn->achn == NULL || (!(pchn->achn->flags & SACKIT_ACHN_PLAYING)) || !flag_slide_porta); + it_sample_t *nsmp = (psmp != NULL && (sackit->module->header.flags & IT_MOD_COMPGXX) && (!flag_isnt_sliding) ? pchn->achn->sample : pchn->sample); + nfreq = sackit_mul_fixed_16_int_32(nfreq, nsmp->c5speed); + pchn->tfreq = nfreq; + pchn->note = note; + + if(flag_isnt_sliding) + { + pchn->freq = pchn->nfreq = nfreq; + flag_retrig = 1; + } + + // TODO: handle carry correctly in this case (it's weird) + if(flag_slide_porta && pchn->achn != NULL && (sackit->module->header.flags & IT_MOD_COMPGXX) != 0) + { + if(pchn->instrument == NULL || (pchn->instrument->evol.flg & IT_ENV_CARRY) == 0) + { + pchn->achn->evol.x = 0; + pchn->achn->evol.idx = 0; + } + if(pchn->instrument == NULL || (pchn->instrument->epan.flg & IT_ENV_CARRY) == 0) + { + pchn->achn->epan.idx = 0; + pchn->achn->epan.x = 0; + } + if(pchn->instrument == NULL || (pchn->instrument->epitch.flg & IT_ENV_CARRY) == 0) + { + pchn->achn->epitch.x = 0; + pchn->achn->epitch.idx = 0; + } + } + } + } else if(note == 255) { + // note off + sackit_nna_note_off(sackit, pchn->achn); + } else if(note == 254) { + // note cut + sackit_nna_note_cut(sackit, pchn->achn); + } else if(note != 253) { + // note fade + sackit_nna_note_fade(sackit, pchn->achn); + } + + if(flag_retrig) + { + if(!flag_done_instrument) + { + + // FIXME: this is messy! it shouldn't be duplicated twice! + if(sackit->module->header.flags & IT_MOD_INSTR) + { + it_instrument_t *cins = sackit->module->instruments[pchn->lins-1]; + if(cins == NULL) + cins = pchn->instrument; + else + pchn->instrument = cins; + + // TODO: confirm behaviour + if(cins == NULL) + { + flag_retrig = 0; + } else if(cins->notesample[pchn->note][1] != 0) { + // FIXME: do i need to do something with the note HERE? + it_sample_t *csmp = sackit->module->samples[cins->notesample[pchn->note][1]-1]; + if(csmp == NULL) + csmp = pchn->sample; + else { + pchn->sample = csmp; + } + + if(csmp == NULL) + flag_retrig = 0; + } + } else { + pchn->instrument = NULL; + it_sample_t *csmp = sackit->module->samples[pchn->lins-1]; + if(csmp == NULL) + csmp = pchn->sample; + else + pchn->sample = csmp; + + if(csmp == NULL) + flag_retrig = 0; + } + + if(!flag_retrig) + sackit_nna_allocate(sackit, pchn); + } + + if(flag_retrig) + { + pchn->rtg_counter = 0; + if(flag_vibrato) + pchn->vib_offs = 0; + sackit_note_retrig(sackit, pchn, new_sample_offset); + } + } + + if(flag_vibrato && pchn->vib_lins != pchn->lins) + { + pchn->vib_lins = pchn->lins; + pchn->vib_offs = 0; + } + + if(flag_nna_set != -1) + pchn->nna = flag_nna_set; + + if(pchn->achn != NULL) + switch(flag_s7x) + { + case 0x7: + pchn->achn->evol.flags &= ~IT_ENV_ON; + break; + case 0x8: + pchn->achn->evol.flags |= IT_ENV_ON; + break; + case 0x9: + pchn->achn->epan.flags &= ~IT_ENV_ON; + break; + case 0xA: + pchn->achn->epan.flags |= IT_ENV_ON; + break; + case 0xB: + pchn->achn->epitch.flags &= ~IT_ENV_ON; + break; + case 0xC: + pchn->achn->epitch.flags |= IT_ENV_ON; + break; + } + + // update slides & stuff + sackit_effect_volslide_cv(sackit, pchn, slide_vol_cv_now); + sackit_effect_volslide_gv(sackit, pchn, slide_vol_gv_now); + sackit_effect_volslide(sackit, pchn, slide_vol_now); + sackit_effect_retrig(sackit, pchn, flag_retrig); + sackit_effect_pitchslide(sackit, pchn, slide_pitch_now); + sackit_effect_pitchslide_fine(sackit, pchn, slide_pitch_fine_now); + if(pchn->achn != NULL) + pchn->achn->ofreq = pchn->achn->freq; + if(flag_vibrato) + { + if(sackit->module->header.flags & IT_MOD_OLDFX) + { + sackit_effect_vibrato_nooffs(sackit, pchn); + } else { + sackit_effect_vibrato(sackit, pchn); + } + } + if(flag_tremolo) + { + sackit_effect_tremolo(sackit, pchn); + } + sackit_effect_tremor(sackit, pchn); +} diff --git a/xlibinc/sackit/playroutine_nna.c b/xlibinc/sackit/playroutine_nna.c new file mode 100644 index 0000000..14b8ef6 --- /dev/null +++ b/xlibinc/sackit/playroutine_nna.c @@ -0,0 +1,292 @@ +#include "sackit_internal.h" + +void sackit_nna_note_cut(sackit_playback_t *sackit, sackit_achannel_t *achn) +{ + if(achn == NULL) + return; + + achn->flags &= ~( + SACKIT_ACHN_MIXING + |SACKIT_ACHN_PLAYING + |SACKIT_ACHN_SUSTAIN); + + sackit->anticlick[0] += achn->anticlick[0]; + sackit->anticlick[1] += achn->anticlick[1]; + sackit->anticlick_f[0] += achn->anticlick_f[0]; + sackit->anticlick_f[1] += achn->anticlick_f[1]; + achn->anticlick[0] = 0; + achn->anticlick[1] = 0; + achn->anticlick_f[0] = 0.0f; + achn->anticlick_f[1] = 0.0f; + achn->filt_prev[0][0] = 0.0f; + achn->filt_prev[0][1] = 0.0f; + achn->filt_prev[1][0] = 0.0f; + achn->filt_prev[1][1] = 0.0f; +} + +void sackit_nna_note_off(sackit_playback_t *sackit, sackit_achannel_t *achn) +{ + if(achn == NULL) + return; + + achn->flags &= ~SACKIT_ACHN_SUSTAIN; + + if(achn->instrument != NULL) + { + it_instrument_t *cins = achn->instrument; + if(cins->evol.flg & IT_ENV_ON) + { + if(cins->evol.flg & IT_ENV_LOOP) + achn->flags |= SACKIT_ACHN_FADEOUT; + } else { + achn->flags |= SACKIT_ACHN_FADEOUT; + } + } +} + +void sackit_nna_note_fade(sackit_playback_t *sackit, sackit_achannel_t *achn) +{ + if(achn == NULL) + return; + + achn->flags |= SACKIT_ACHN_FADEOUT; +} + +void sackit_nna_past_note(sackit_playback_t *sackit, sackit_achannel_t *achn, int nna) +{ + while(achn != NULL) + { + sackit_achannel_t *achn_next = achn->next; + switch(nna) + { + case 0: + sackit_nna_note_cut(sackit, achn); + break; + case 2: + sackit_nna_note_off(sackit, achn); + break; + case 3: + sackit_nna_note_fade(sackit, achn); + break; + } + achn = achn_next; + } +} + +/* +from ITTECH.TXT: + +The player in Impulse Tracker 'allocates' channels to notes whenever they +are *PLAYED*. In sample mode, the allocation is simple: + Virtual Channel (number) = 'Host' channel (number) + +In instrument mode, the following procedure is used: + + Check if channel is already playing ---Yes--> set 'background' flag on. + | 'Trigger' NNA. If NNA=cut, + No then use this virtual + | channel. + | | + |<------------------ else -----------------/ + | + v + Search and find the first non-active virtual channel. + | + Non-active channel found? ----Yes----> Use this for playback. + | + No + | + v + Search through and find the channel of lowest volume that is in the # + 'background' (ie. no longer controlled directly) # + | # + Background channel found? ----Yes----> Use this for playback. # + | # + No # + | # + v # + Return error - the note is *NOT* allocated a channel, and hence is not # + played. # + + This is actually quite a simple process... just that it's another of + those 'hassles' to have to write... + + ### Note: This is by far the simplest implementation of congestion + resolution. IT 2.03 and above have a greatly enhanced + method which more selectively removes the most insignificant + channel. Obviously, there is no best way to do this - I + encourage you to experiment and find new algorithms for + yourself. +*/ + +void sackit_nna_allocate(sackit_playback_t *sackit, sackit_pchannel_t *pchn) +{ + int i; + + // TODO: copy NNA info to the achn (or pchn?) + + sackit_achannel_t *old_achn = NULL; + + // Do a duplicate check + // TODO: analyse this more deeply + if(pchn->bg_achn != NULL) + { + sackit_achannel_t *achn = pchn->bg_achn; + while(achn != NULL) + { + sackit_achannel_t *achn_next = achn->next; + + int dca_do = 0; + + if(achn->instrument != NULL && achn != pchn->achn) + { + switch(achn->instrument->dct) + { + case 0: // Off + break; + case 1: // Note + dca_do = (achn->note == pchn->note); + break; + case 2: // Instrument + dca_do = (achn->instrument == pchn->instrument); + break; + case 3: // Sample + dca_do = (achn->sample == pchn->sample); + break; + } + } + + if(dca_do) + { + //printf("DCA!\n"); + switch(achn->instrument->dca) + { + case 0: + sackit_nna_note_cut(sackit, achn); + break; + case 1: + sackit_nna_note_off(sackit, achn); + break; + case 2: + sackit_nna_note_fade(sackit, achn); + break; + } + } + + achn = achn_next; + } + } + + //printf("NNATRIG %016llX %016llX\n", pchn, pchn->achn); + // Check if playing + if(pchn->achn != NULL) + { + old_achn = pchn->achn; + + //printf("NNA %i %016llX\n", pchn->nna, old_achn); + + if(pchn->nna == 0) + { + sackit_nna_note_cut(sackit, old_achn); + return; + } + if(!(old_achn->flags & SACKIT_ACHN_PLAYING)) + return; + + if(pchn->nna == 2) + sackit_nna_note_off(sackit, old_achn); + if(pchn->nna == 3) + sackit_nna_note_fade(sackit, old_achn); + + old_achn->flags |= SACKIT_ACHN_BACKGND; + pchn->achn = NULL; + } + + // Search and find the first non-active virtual channel. + for(i = 0; i < sackit->achn_count; i++) + { + sackit_achannel_t *achn = &(sackit->achn[i]); + if(!(achn->flags & SACKIT_ACHN_PLAYING)) + { + if(achn->parent != NULL) + { + if(achn->parent->achn == achn) + achn->parent->achn = NULL; + if(achn->parent->bg_achn == achn) + achn->parent->bg_achn = achn->next; + } + + if(achn->prev != NULL) + achn->prev->next = achn->next; + if(achn->next != NULL) + achn->next->prev = achn->prev; + + sackit_playback_reset_achn(achn); + + pchn->bg_achn = old_achn; + pchn->achn = achn; + achn->parent = pchn; + + achn->prev = NULL; + achn->next = old_achn; + if(old_achn != NULL) + old_achn->prev = achn; + + //printf("%i\n", i); + return; + } + } + + // Search through and find the channel of lowest volume that is in the + // 'background' (ie. no longer controlled directly) + int tvol = 0xFFFFFF; + int ti = 0; + for(i = 0; i < sackit->achn_count; i++) + { + sackit_achannel_t *achn = &(sackit->achn[i]); + if(achn->parent != NULL && achn->parent->achn == achn) + continue; // foreground channel - SKIP THIS + + int cvol = (int32_t)achn->sv; + cvol *= (int32_t)achn->iv; + cvol *= (int32_t)achn->vol; + cvol *= (int32_t)achn->cv; + cvol >>= 16; + cvol *= (int32_t)achn->evol.y; + cvol *= (int32_t)achn->fadeout; + + if(cvol < tvol) + { + tvol = cvol; + ti = i; + } + } + + // There's our channel. + sackit_achannel_t *achn = &(sackit->achn[ti]); + if(achn->parent != NULL) + { + if(achn->parent->achn == achn) + achn->parent->achn = NULL; + if(achn->parent->bg_achn == achn) + achn->parent->bg_achn = achn->next; + } + + if(achn->prev != NULL) + achn->prev->next = achn->next; + if(achn->next != NULL) + achn->next->prev = achn->prev; + + if(old_achn == achn) + old_achn = achn->next; + pchn->bg_achn = old_achn; + sackit_playback_reset_achn(achn); + + pchn->achn = achn; + achn->parent = pchn; + + achn->prev = NULL; + achn->next = old_achn; + if(old_achn != NULL) + old_achn->prev = achn; +} diff --git a/xlibinc/sackit/sackit.h b/xlibinc/sackit/sackit.h new file mode 100644 index 0000000..53231cc --- /dev/null +++ b/xlibinc/sackit/sackit.h @@ -0,0 +1,370 @@ +#define AMICLK (938308796416LL) +#define AMIMUL (65536LL) + +// note, these aren't the IT limits (smps is 99 afaik) +// but they should be fine for now --GM +#define MAX_ORDERS 256 +#define MAX_INSTRUMENTS 256 +#define MAX_SAMPLES 256 +#define MAX_PATTERNS 256 + +#define SACKIT_MAX_ACHANNEL 256 + +enum { + MIXER_IT211 = 0, + MIXER_IT211S, + MIXER_IT211L, + MIXER_IT211LS, + + MIXER_IT212, + MIXER_IT212S, + MIXER_IT212L, + MIXER_IT212LS, + + MIXER_IT214, + MIXER_IT214S, + MIXER_IT214L, + MIXER_IT214LS, + MIXER_IT214C, + MIXER_IT214CS, + + MIXER_IT214F, + MIXER_IT214FS, + MIXER_IT214FL, + MIXER_IT214FLS, + MIXER_IT214FC, + MIXER_IT214FCS, + + MIXER_INTFAST_A, + MIXER_INTFAST_AS, +}; + +#pragma pack(push, 1) +typedef struct it_pattern +{ + uint16_t length; + uint16_t rows; + uint32_t reserved; + uint8_t data[65536]; +} it_pattern_t; +#pragma pack(pop) + +#define IT_SMP_EXISTS 0x01 +#define IT_SMP_16BIT 0x02 +#define IT_SMP_STEREO 0x04 +#define IT_SMP_COMPRESS 0x08 +#define IT_SMP_LOOP 0x10 +#define IT_SMP_SUSLOOP 0x20 +#define IT_SMP_LOOPBIDI 0x40 +#define IT_SMP_SUSBIDI 0x80 + +#pragma pack(push, 1) +typedef struct it_sample +{ + uint8_t magic[4]; // IMPS + uint8_t dos_filename[13]; + uint8_t gvl, flg, vol; + uint8_t sample_name[26]; + uint8_t cvt, dfp; + uint32_t length; + uint32_t loop_begin, loop_end; + uint32_t c5speed; + uint32_t susloop_begin, susloop_end; + uint32_t samplepointer; + uint8_t vis,vid,vir,vit; + int16_t *data; +} it_sample_t; +#pragma pack(pop) + +#define IT_ENV_ON 0x01 +#define IT_ENV_LOOP 0x02 +#define IT_ENV_SUSLOOP 0x04 +#define IT_ENV_CARRY 0x08 +#define IT_ENV_FILTER 0x80 +// toDONE: confirm which bit ENV_CARRY *really* uses +// (it's not in ITTECH - dammit Jeff...) +// - yes, it IS that flag. Apparently it was added in IT 2.14p5 or MAYBE p4 (it's not in p3!). + +#pragma pack(push, 1) +typedef struct it_envelope +{ + uint8_t flg,num; + uint8_t lpb,lpe; + uint8_t slb,sle; +#pragma pack(push, 1) + struct { + int8_t y; + uint16_t x; + } points[25]; +#pragma pack(pop) + uint8_t resv1; +} it_envelope_t; +#pragma pack(pop) + +#define IT_MOD_STEREO 0x01 +#define IT_MOD_VOL0MIX 0x02 /* Most. Useless. Flag. Ever. */ +#define IT_MOD_INSTR 0x04 +#define IT_MOD_LINEAR 0x08 +#define IT_MOD_OLDFX 0x10 +#define IT_MOD_COMPGXX 0x20 +#define IT_MOD_USEPWD 0x40 +#define IT_MOD_GETMIDI 0x80 +#define IT_SPECIAL_MESSAGE 0x01 +//define IT_SPECIAL_ 0x02 // unknown +//define IT_SPECIAL_ 0x04 // unknown +#define IT_SPECIAL_HASMIDI 0x08 + +#pragma pack(push, 1) +typedef struct it_instrument +{ + uint8_t magic[4]; // IMPI + uint8_t dos_filename[13]; + uint8_t nna,dct,dca; + uint16_t fadeout; + uint8_t pps,ppc; + uint8_t gbv,dfp; + uint8_t rv,rp; + uint16_t trkvers; + uint8_t nos,resv1; + uint8_t instrument_name[26]; + uint8_t ifc,ifr; + uint8_t mch,mpr; + uint16_t midibnk; + uint8_t notesample[120][2]; + it_envelope_t evol; + it_envelope_t epan; + it_envelope_t epitch; +} it_instrument_t; +#pragma pack(pop) + +typedef struct it_module_header it_module_header_t; + +typedef struct it_module +{ +#pragma pack(push, 1) + struct it_module_header { + uint8_t magic[4]; // IMPM + uint8_t song_name[26]; + uint16_t philigt; + uint16_t ordnum, insnum, smpnum, patnum; + uint16_t cwtv, cmwt; + uint16_t flags, special; + uint8_t gv, mv, is, it, sep, pwd; + uint16_t msglgth; + uint32_t message_offset; + uint32_t timestamp; // reserved my ass --GM + uint8_t chnl_pan[64]; + uint8_t chnl_vol[64]; + } header; +#pragma pack(pop) + + uint8_t orders[MAX_ORDERS]; + it_instrument_t *instruments[MAX_INSTRUMENTS]; + it_sample_t *samples[MAX_SAMPLES]; + it_pattern_t *patterns[MAX_PATTERNS]; +} it_module_t; + +#define SACKIT_ENV_PLAYING 0x100 +#define SACKIT_ENV_SUSTAIN 0x200 +typedef struct sackit_envelope +{ + int8_t idx; + int16_t x; + int32_t y; + int8_t def; // "default". + int8_t lpbeg,lpend; + int8_t susbeg,susend; + uint8_t flags; + + // YES, the X is stored for the carry flag stuff! + // (this can be noticed with compat Gxx) + int16_t carry_x; + int8_t carry_idx; + uint8_t carry_flags; +} sackit_envelope_t; + +// audio channel +#define SACKIT_ACHN_PLAYING 0x01 +#define SACKIT_ACHN_MIXING 0x02 +#define SACKIT_ACHN_RAMP 0x04 +#define SACKIT_ACHN_REVERSE 0x08 +#define SACKIT_ACHN_SUSTAIN 0x10 +#define SACKIT_ACHN_FADEOUT 0x20 +#define SACKIT_ACHN_BACKGND 0x40 + +typedef struct sackit_achannel sackit_achannel_t; +typedef struct sackit_pchannel sackit_pchannel_t; + +struct sackit_achannel +{ + uint8_t note; + + int32_t ofreq; + int32_t freq; + int32_t offs; + int32_t suboffs; + float suboffs_f; + uint16_t flags; + uint8_t vol,sv,iv,cv; // TODO: more stuff + uint8_t pan; + uint16_t fv; + int32_t lramp; + float lramp_f; + int16_t fadeout; + + int filt_cut; + int filt_res; + float filt_prev[2][2]; + float filt_coeff[3]; + + int32_t anticlick[2]; + float anticlick_f[2]; + + int32_t svib_speed; + int32_t svib_type; + int32_t svib_depth; + int32_t svib_rate; + int32_t svib_power; + int32_t svib_offs; + + sackit_achannel_t *prev,*next; + sackit_pchannel_t *parent; + + it_instrument_t *instrument; + it_sample_t *sample; + sackit_envelope_t evol,epan,epitch; +}; + +// pattern channel +struct sackit_pchannel +{ + sackit_achannel_t *achn; + sackit_achannel_t *bg_achn; + + uint32_t tfreq; + uint32_t nfreq; + int32_t freq; + uint8_t note; + uint8_t lins; + uint8_t cv; + uint8_t pan; + uint8_t vol; + + uint8_t nna; + + int16_t slide_vol; + int16_t slide_pan; + int16_t slide_pitch; + int16_t slide_porta; + int16_t slide_vol_cv; + int16_t slide_vol_gv; + uint16_t arpeggio; + uint16_t note_cut; + uint16_t note_delay; + uint16_t note_delay_note; + uint16_t note_delay_ins; + uint16_t note_delay_vol; + uint16_t vib_speed; + int16_t vib_depth; + uint16_t vib_offs; + uint16_t vib_type; + uint16_t vib_lins; + uint16_t tre_speed; + int16_t tre_depth; + uint16_t tre_offs; + uint16_t tre_type; + uint8_t trm_val; + uint8_t trm_flags; + uint8_t trm_cur_on; + uint8_t trm_cur_off; + uint8_t rtg_val; + uint8_t rtg_flags; + uint8_t rtg_counter; + + int16_t loop_start; + uint8_t loop_times; + + uint8_t eff_slide_vol; + uint8_t eff_slide_vol_cv; + uint8_t eff_slide_vol_gv; + uint8_t eff_slide_vol_veff; + uint8_t eff_slide_pitch; + uint8_t eff_slide_porta; + uint8_t eff_sample_offs; + uint8_t eff_misc; + uint8_t eff_arpeggio; + uint8_t eff_vibrato; + uint8_t eff_tremolo; + uint8_t eff_tempo; + uint8_t eff_tremor; + uint8_t eff_retrig; + + int filt_cut; + int filt_res; + + it_instrument_t *instrument; + it_sample_t *sample; + + uint8_t lmask,ldata[5]; +}; + +typedef struct sackit_playback sackit_playback_t; +struct sackit_playback +{ + it_module_t *module; + + uint16_t current_tick; + uint16_t max_tick; + uint16_t row_counter; + + uint16_t current_row; + uint16_t process_row; + uint16_t break_row; + uint16_t number_of_rows; // TODO? refactor into pattern? + + uint16_t current_pattern; + uint16_t process_order; + + uint16_t pat_ptr; // index of next row + uint16_t pat_row; + + uint16_t tempo; + int16_t tempo_inc; + + uint32_t buf_len; + uint32_t buf_tick_rem; + void (*f_mix)(sackit_playback_t *sackit, int offs, int len); + int mixer_bytes; + int freq; + int16_t *buf; + int32_t *mixbuf; + + uint8_t gv,mv; + int32_t anticlick[2]; + float anticlick_f[2]; + + uint16_t achn_count; + sackit_pchannel_t pchn[64]; + sackit_achannel_t achn[SACKIT_MAX_ACHANNEL]; +}; + +extern void (*(fnlist_itmixer[]))(sackit_playback_t *sackit, int offs, int len); + +// objects.c +it_module_t *sackit_module_new(void); +void sackit_module_free(it_module_t *module); +it_module_t *sackit_module_load(const char *fname); +it_module_t *sackit_module_load_memory(const void *data, const long length); +it_module_t *sackit_module_load_offs(const char *fname, int fboffs); +void sackit_playback_free(sackit_playback_t *sackit); +void sackit_playback_reset2(sackit_playback_t *sackit, int buf_len, int achn_count, + void (*f_mix)(sackit_playback_t *sackit, int offs, int len), int mixer_bytes, int freq); +void sackit_playback_reset(sackit_playback_t *sackit, int buf_len, int achn_count, int mixeridx); +sackit_playback_t *sackit_playback_new2(it_module_t *module, int buf_len, int achn_count, + void (*f_mix)(sackit_playback_t *sackit, int offs, int len), int mixer_bytes, int freq); +sackit_playback_t *sackit_playback_new(it_module_t *module, int buf_len, int achn_count, int mixeridx); + +// playroutine.c +extern int itmixer_bytes[]; +void sackit_playback_update(sackit_playback_t *sackit); + diff --git a/xlibinc/sackit/sackit_internal.h b/xlibinc/sackit/sackit_internal.h new file mode 100644 index 0000000..2094d06 --- /dev/null +++ b/xlibinc/sackit/sackit_internal.h @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include + +#include + +#include "sackit.h" + +typedef struct sackit_reader sackit_reader_t; +struct sackit_reader +{ + void *in; + char (*getch)(sackit_reader_t *reader); + size_t (*read)(sackit_reader_t *reader, void *out, size_t size); + void (*seek)(sackit_reader_t *reader, long offset, int mode); + long (*tell)(sackit_reader_t *reader); +}; + +typedef struct sackit_reader_data_mem sackit_reader_data_mem_t; +struct sackit_reader_data_mem +{ + const char* ptr; + long pos, len; +}; + +// effects.c +uint32_t sackit_pitchslide_linear(uint32_t freq, int16_t amt); +uint32_t sackit_pitchslide_linear_fine(uint32_t freq, int16_t amt); +uint32_t sackit_pitchslide_amiga_fine(uint32_t freq, int16_t amt); +void sackit_effect_volslide(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int8_t amt); +void sackit_effect_volslide_cv(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int8_t amt); +void sackit_effect_volslide_gv(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int8_t amt); +void sackit_effect_pitchslide(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int16_t amt); +void sackit_effect_pitchslide_fine(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int16_t amt); +void sackit_effect_portaslide(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int16_t amt); +void sackit_effect_vibrato_nooffs(sackit_playback_t *sackit, sackit_pchannel_t *pchn); +void sackit_effect_vibrato(sackit_playback_t *sackit, sackit_pchannel_t *pchn); +void sackit_effect_tremolo_nooffs(sackit_playback_t *sackit, sackit_pchannel_t *pchn); +void sackit_effect_tremolo(sackit_playback_t *sackit, sackit_pchannel_t *pchn); +void sackit_effect_tremor(sackit_playback_t *sackit, sackit_pchannel_t *pchn); +void sackit_effect_retrig(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int first_note); +void sackit_effect_samplevibrato(sackit_playback_t *sackit, sackit_achannel_t *achn); + +// fixedmath.c +uint32_t sackit_mul_fixed_16_int_32(uint32_t a, uint32_t b); +uint32_t sackit_div_int_32_32_to_fixed_16(uint32_t a, uint32_t b); + +// mixer_*.c +void sackit_playback_mixstuff_it211(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it211s(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it211l(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it211ls(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it212(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it212s(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it212l(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it212ls(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it214(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it214s(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it214l(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it214ls(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it214c(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it214cs(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it214f(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it214fs(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it214fl(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it214fls(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it214fc(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_it214fcs(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_intfast_a(sackit_playback_t *sackit, int offs, int len); +void sackit_playback_mixstuff_intfast_as(sackit_playback_t *sackit, int offs, int len); + +// objects.c +it_module_t *sackit_module_load_offs_internal(sackit_reader_t *reader, int fboffs); +void sackit_playback_reset_achn(sackit_achannel_t *achn); +void sackit_playback_reset_pchn(sackit_pchannel_t *pchn); + +// playroutine.c +void sackit_filter_calc(sackit_playback_t *sackit, sackit_achannel_t *achn); +void sackit_note_retrig(sackit_playback_t *sackit, sackit_pchannel_t *pchn, int offs); +void sackit_update_effects_chn(sackit_playback_t *sackit, sackit_pchannel_t *pchn, + uint8_t note, uint8_t ins, uint8_t vol, uint8_t eft, uint8_t efp); +void sackit_update_effects(sackit_playback_t *sackit); +void sackit_update_pattern(sackit_playback_t *sackit); +void sackit_nna_allocate(sackit_playback_t *sackit, sackit_pchannel_t *pchn); +void sackit_nna_note_off(sackit_playback_t *sackit, sackit_achannel_t *achn); +void sackit_nna_note_cut(sackit_playback_t *sackit, sackit_achannel_t *achn); +void sackit_nna_note_fade(sackit_playback_t *sackit, sackit_achannel_t *achn); +void sackit_nna_past_note(sackit_playback_t *sackit, sackit_achannel_t *achn, int nna); +void sackit_tick(sackit_playback_t *sackit); + +// tables.c +extern int8_t fine_sine_data[]; +extern int8_t fine_ramp_down_data[]; +extern int8_t fine_square_wave[]; +extern uint16_t pitch_table[]; +extern uint16_t fine_linear_slide_up_table[]; +extern uint16_t linear_slide_up_table[]; +extern uint16_t fine_linear_slide_down_table[]; +extern uint16_t linear_slide_down_table[]; +extern uint8_t slide_table[]; +extern float quality_factor_table[]; + diff --git a/xlibinc/sackit/tables.c b/xlibinc/sackit/tables.c new file mode 100644 index 0000000..01f15c6 --- /dev/null +++ b/xlibinc/sackit/tables.c @@ -0,0 +1,240 @@ +#include + +// TABLES LOLS +// official tables from ITTECH.TXT of course. +int8_t fine_sine_data[] = { + 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, + 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, + 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, + 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, + 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, + 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, + 0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23, + -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44, + -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59, + -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60, + -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46, + -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26, + -24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2, +}; + +int8_t fine_ramp_down_data[] = { + 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56, + 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, + 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, + 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32, + 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24, + 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16, + 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8, + -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16, + -16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24, + -24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32, + -32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40, + -40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48, + -48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56, + -56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64, +}; + +int8_t fine_square_wave[] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +// ignoring EmptyPattern for now --GM + +// ----------------------------------------------------------------------------- + +uint16_t pitch_table[] = { // Values are 16.16 bit + 2048, 0, 2170, 0, 2299, 0, 2435, 0, 2580, 0, 2734, 0, // C-0 + 2896, 0, 3069, 0, 3251, 0, 3444, 0, 3649, 0, 3866, 0, //>B-0 + + 4096, 0, 4340, 0, 4598, 0, 4871, 0, 5161, 0, 5468, 0, // C-1 + 5793, 0, 6137, 0, 6502, 0, 6889, 0, 7298, 0, 7732, 0, //>B-1 + + 8192, 0, 8679, 0, 9195, 0, 9742, 0, 10321, 0, 10935, 0, + 11585, 0, 12274, 0, 13004, 0, 13777, 0, 14596, 0, 15464, 0, + + 16384, 0, 17358, 0, 18390, 0, 19484, 0, 20643, 0, 21870, 0, + 23170, 0, 24548, 0, 26008, 0, 27554, 0, 29193, 0, 30929, 0, + + 32768, 0, 34716, 0, 36781, 0, 38968, 0, 41285, 0, 43740, 0, + 46341, 0, 49097, 0, 52016, 0, 55109, 0, 58386, 0, 61858, 0, + + 0, 1, 3897, 1, 8026, 1, 12400, 1, 17034, 1, 21944, 1, + 27146, 1, 32657, 1, 38496, 1, 44682, 1, 51236, 1, 58179, 1, + + 0, 2, 7794, 2, 16051, 2, 24800, 2, 34068, 2, 43888, 2, + 54292, 2, 65314, 2, 11456, 3, 23828, 3, 36936, 3, 50823, 3, + + 0, 4, 15588, 4, 32103, 4, 49600, 4, 2601, 5, 22240, 5, + 43048, 5, 65092, 5, 22912, 6, 47656, 6, 8336, 7, 36110, 7, + + 0, 8, 31176, 8, 64205, 8, 33663, 9, 5201, 10, 44481, 10, + 20559, 11, 64648, 11, 45823, 12, 29776, 13, 16671, 14, 6684, 15, + + 0, 16, 62352, 16, 62875, 17, 1790, 19, 10403, 20, 23425, 21, + 41118, 22, 63761, 23, 26111, 25, 59552, 26, 33342, 28, 13368, 30, +}; + +uint16_t fine_linear_slide_up_table[] = { // Values are 16.16 bit + 0, 1, 59, 1, 118, 1, 178, 1, 237, 1, // 0->4 + 296, 1, 356, 1, 415, 1, 475, 1, 535, 1, // 5->9 + 594, 1, 654, 1, 714, 1, 773, 1, 833, 1, // 10->14 + 893, 1, // 15 +}; + +uint16_t linear_slide_up_table[] = { // Value = 2^(Val/192), Values are 16.16 bit + 0, 1, 237, 1, 475, 1, 714, 1, 953, 1, // 0->4 + 1194, 1, 1435, 1, 1677, 1, 1920, 1, 2164, 1, // 5->9 + 2409, 1, 2655, 1, 2902, 1, 3149, 1, 3397, 1, // 10->14 + 3647, 1, 3897, 1, 4148, 1, 4400, 1, 4653, 1, // 15->19 + 4907, 1, 5157, 1, 5417, 1, 5674, 1, 5932, 1, // 20->24 + 6190, 1, 6449, 1, 6710, 1, 6971, 1, 7233, 1, // 25->29 + 7496, 1, 7761, 1, 8026, 1, 8292, 1, 8559, 1, // 30->34 + 8027, 1, 9096, 1, 9366, 1, 9636, 1, 9908, 1, // 35->39 + 10181, 1, 10455, 1, 10730, 1, 11006, 1, 11283,1, // 40->44 + 11560, 1, 11839, 1, 12119, 1, 12400, 1, 12682,1, // 45->49 + 12965, 1, 13249, 1, 13533, 1, 13819, 1, 14106,1, // 50->54 + 14394, 1, 14684, 1, 14974, 1, 15265, 1, 15557,1, // 55->59 + 15850, 1, 16145, 1, 16440, 1, 16737, 1, 17034,1, // 60->64 + 17333, 1, 17633, 1, 17933, 1, 18235, 1, 18538,1, // 65->69 + 18842, 1, 19147, 1, 19454, 1, 19761, 1, 20070,1, // 70->74 + 20379, 1, 20690, 1, 21002, 1, 21315, 1, 21629,1, // 75->79 + 21944, 1, 22260, 1, 22578, 1, 22897, 1, 23216,1, // 80->84 + 23537, 1, 23860, 1, 24183, 1, 24507, 1, 24833,1, // 85->89 + 25160, 1, 25488, 1, 25817, 1, 26148, 1, 26479,1, // 90->94 + 26812, 1, 27146, 1, 27481, 1, 27818, 1, 28155,1, // 95->99 + 28494, 1, 28834, 1, 29175, 1, 29518, 1, 29862,1, // 100->104 + 30207, 1, 30553, 1, 30900, 1, 31248, 1, 31599,1, // 105->109 + 31951, 1, 32303, 1, 32657, 1, 33012, 1, 33369,1, // 110->114 + 33726, 1, 34085, 1, 34446, 1, 34807, 1, 35170,1, // 115->119 + 35534, 1, 35900, 1, 36267, 1, 36635, 1, 37004,1, // 120->124 + 37375, 1, 37747, 1, 38121, 1, 38496, 1, 38872,1, // 125->129 + 39250, 1, 39629, 1, 40009, 1, 40391, 1, 40774,1, // 130->134 + 41158, 1, 41544, 1, 41932, 1, 42320, 1, 42710,1, // 135->139 + 43102, 1, 43495, 1, 43889, 1, 44285, 1, 44682,1, // 140->144 + 45081, 1, 45481, 1, 45882, 1, 46285, 1, 46690,1, // 145->149 + 47095, 1, 47503, 1, 47917, 1, 48322, 1, 48734,1, // 150->154 + 49147, 1, 49562, 1, 49978, 1, 50396, 1, 50815,1, // 155->159 + 51236, 1, 51658, 1, 52082, 1, 52507, 1, 52934,1, // 160->164 + 53363, 1, 53793, 1, 54224, 1, 54658, 1, 55092,1, // 165->169 + 55529, 1, 55966, 1, 56406, 1, 56847, 1, 57289,1, // 170->174 + 57734, 1, 58179, 1, 58627, 1, 59076, 1, 59527,1, // 175->179 + 59979, 1, 60433, 1, 60889, 1, 61346, 1, 61805,1, // 180->184 + 62265, 1, 62727, 1, 63191, 1, 63657, 1, 64124,1, // 185->189 + 64593, 1, 65064, 1, 0, 2, 474, 2, 950, 2, // 190->194 + 1427, 2, 1906, 2, 2387, 2, 2870, 2, 3355, 2, // 195->199 + 3841, 2, 4327, 2, 4818, 2, 5310, 2, 5803, 2, // 200->204 + 6298, 2, 6795, 2, 7294, 2, 7794, 2, 8296, 2, // 205->209 + 8800, 2, 9306, 2, 9814, 2, 10323, 2, 10835,2, // 210->214 + 11348, 2, 11863, 2, 12380, 2, 12899, 2, 13419,2, // 215->219 + 13942, 2, 14467, 2, 14993, 2, 15521, 2, 16051,2, // 220->224 + 16583, 2, 17117, 2, 17653, 2, 18191, 2, 18731,2, // 225->229 + 19273, 2, 19817, 2, 20362, 2, 20910, 2, 21460,2, // 230->234 + 22011, 2, 22565, 2, 23121, 2, 23678, 2, 24238,2, // 235->239 + 24800, 2, 25363, 2, 25929, 2, 25497, 2, 27067,2, // 240->244 + 27639, 2, 28213, 2, 28789, 2, 29367, 2, 29947,2, // 245->249 + 30530, 2, 31114, 2, 31701, 2, 32289, 2, 32880,2, // 250->254 + 33473, 2, 34068, 2 // 255->256 +}; + +uint16_t fine_linear_slide_down_table[] = { // Values are 0.16 bit + 65535, 65477, 65418, 65359, 65300, 65241, 65182, 65359, // 0->7 + 65065, 65006, 64947, 64888, 64830, 64772, 64713, 64645, // 8->15 +}; + +uint16_t linear_slide_down_table[] = { // Values are 0.16 bit + 65535, 65300, 65065, 64830, 64596, 64364, 64132, 63901, // 0->7 + 63670, 63441, 63212, 62984, 62757, 62531, 62306, 62081, // 8->15 + 61858, 61635, 61413, 61191, 60971, 60751, 60532, 60314, // 16->23 + 60097, 59880, 59664, 59449, 59235, 59022, 58809, 58597, // 24->31 + 58386, 58176, 57966, 57757, 57549, 57341, 57135, 56929, // 32->39 + 56724, 56519, 56316, 56113, 55911, 55709, 55508, 55308, // 40->47 + 55109, 54910, 54713, 54515, 54319, 54123, 53928, 53734, // 48->55 + 53540, 53347, 53155, 52963, 52773, 52582, 52393, 52204, // 56->63 + 52016, 51829, 51642, 51456, 51270, 51085, 50901, 50718, // 64->71 + 50535, 50353, 50172, 49991, 49811, 49631, 49452, 49274, // 72->79 + 49097, 48920, 48743, 48568, 48393, 48128, 48044, 47871, // 80->87 + 47699, 47527, 47356, 47185, 47015, 46846, 46677, 46509, // 88->95 + 46341, 46174, 46008, 45842, 45677, 45512, 45348, 45185, // 96->103 + 45022, 44859, 44698, 44537, 44376, 44216, 44057, 43898, //104->111 + 43740, 43582, 43425, 43269, 43113, 42958, 42803, 42649, //112->119 + 42495, 42342, 42189, 42037, 41886, 41735, 41584, 41434, //120->127 + 41285, 41136, 40988, 40840, 40639, 40566, 40400, 40253, //128->135 + 40110, 39965, 39821, 39678, 39535, 39392, 39250, 39109, //136->143 + 38968, 38828, 38688, 38548, 38409, 38271, 38133, 37996, //144->151 + 37859, 37722, 37586, 37451, 37316, 37181, 37047, 36914, //152->159 + 36781, 36648, 36516, 36385, 36254, 36123, 35993, 35863, //160->167 + 35734, 35605, 35477, 35349, 35221, 35095, 34968, 34842, //168->175 + 34716, 34591, 34467, 34343, 34219, 34095, 33973, 33850, //176->183 + 33728, 33607, 33486, 33365, 33245, 33125, 33005, 32887, //184->191 + 32768, 32650, 32532, 32415, 32298, 32182, 32066, 31950, //192->199 + 31835, 31720, 31606, 31492, 31379, 31266, 31153, 31041, //200->207 + 30929, 30817, 30706, 30596, 30485, 30376, 30226, 30157, //208->215 + 30048, 29940, 29832, 29725, 29618, 29511, 29405, 29299, //216->223 + 29193, 29088, 28983, 28879, 28774, 28671, 28567, 28464, //224->231 + 28362, 28260, 28158, 28056, 27955, 27855, 27754, 27654, //232->239 + 27554, 27455, 27356, 27258, 27159, 27062, 26964, 26867, //240->247 + 26770, 26674, 26577, 26482, 26386, 26291, 26196, 26102, //248->255 + 26008, // 256 +}; + +uint8_t slide_table[] = { + 1, 4, 8, 16, 32, 64, 96, 128, 255 +}; + +// this one's from the little bit of source Jeff released and converted to actual floating point by Storlek (I think). +float quality_factor_table[] = { + 1.000000000000000000000000000, 0.978644609451293945312500000, 0.957745254039764404296875000, 0.937292218208312988281250000, + 0.917275905609130859375000000, 0.897687137126922607421875000, 0.878516674041748046875000000, 0.859755575656890869140625000, + 0.841395139694213867187500000, 0.823426783084869384765625000, 0.805842161178588867187500000, 0.788633108139038085937500000, + 0.771791517734527587890625000, 0.755309581756591796875000000, 0.739179670810699462890625000, 0.723394155502319335937500000, + 0.707945764064788818359375000, 0.692827284336090087890625000, 0.678031682968139648437500000, 0.663552045822143554687500000, + 0.649381637573242187500000000, 0.635513842105865478515625000, 0.621942162513732910156250000, 0.608660340309143066406250000, + 0.595662117004394531250000000, 0.582941532135009765625000000, 0.570492565631866455078125000, 0.558309495449066162109375000, + 0.546386539936065673828125000, 0.534718215465545654296875000, 0.523299098014831542968750000, 0.512123823165893554687500000, + 0.501187205314636230468750000, 0.490484178066253662109375000, 0.480009675025939941406250000, 0.469758868217468261718750000, + 0.459726989269256591796875000, 0.449909329414367675781250000, 0.440301328897476196289062500, 0.430898517370223999023437500, + 0.421696513891220092773437500, 0.412690997123718261718750000, 0.403877824544906616210937500, 0.395252853631973266601562500, + 0.386812061071395874023437500, 0.378551512956619262695312500, 0.370467394590377807617187500, 0.362555921077728271484375000, + 0.354813396930694580078125000, 0.347236216068267822265625000, 0.339820832014083862304687500, 0.332563817501068115234375000, + 0.325461775064468383789062500, 0.318511426448822021484375000, 0.311709463596343994140625000, 0.305052787065505981445312500, + 0.298538267612457275390625000, 0.292162865400314331054687500, 0.285923600196838378906250000, 0.279817581176757812500000000, + 0.273841977119445800781250000, 0.267993956804275512695312500, 0.262270838022232055664062500, 0.256669938564300537109375000, + 0.251188635826110839843750000, 0.245824411511421203613281250, 0.240574732422828674316406250, 0.235437154769897460937500000, + 0.230409294366836547851562500, 0.225488811731338500976562500, 0.220673412084579467773437500, 0.215960830450057983398437500, + 0.211348906159400939941406250, 0.206835463643074035644531250, 0.202418401837348937988281250, 0.198095679283142089843750000, + 0.193865269422531127929687500, 0.189725190401077270507812500, 0.185673534870147705078125000, 0.181708395481109619140625000, + 0.177827939391136169433593750, 0.174030348658561706542968750, 0.170313864946365356445312500, 0.166676744818687438964843750, + 0.163117289543151855468750000, 0.159633859992027282714843750, 0.156224802136421203613281250, 0.152888566255569458007812500, + 0.149623572826385498046875000, 0.146428287029266357421875000, 0.143301263451576232910156250, 0.140240997076034545898437500, + 0.137246102094650268554687500, 0.134315147995948791503906250, 0.131446793675422668457031250, 0.128639698028564453125000000, + 0.125892534852027893066406250, 0.123204052448272705078125000, 0.120572984218597412109375000, 0.117998093366622924804687500, + 0.115478195250034332275390625, 0.113012112677097320556640625, 0.110598690807819366455078125, 0.108236812055110931396484375, + 0.105925373733043670654296875, 0.103663295507431030273437500, 0.101449519395828247070312500, 0.099283024668693542480468750, + 0.097162798047065734863281250, 0.095087841153144836425781250, 0.093057207763195037841796875, 0.091069929301738739013671875, + 0.089125096797943115234375000, 0.087221793830394744873046875, 0.085359133780002593994140625, 0.083536252379417419433593750, + 0.081752300262451171875000000, 0.080006450414657592773437500, 0.078297875821590423583984375, 0.076625794172286987304687500, + 0.074989423155784606933593750, 0.073387987911701202392578125, 0.071820758283138275146484375, 0.070286996662616729736328125, + 0.068785987794399261474609375, 0.067317038774490356445312500, 0.065879456698894500732421875, 0.064472571015357971191406250, +}; +