Mercurial > codedump
changeset 126:8e4ee43d3b81
remove submodules
line wrap: on
line diff
--- a/.hgsub Fri Jun 23 22:44:17 2023 -0400 +++ b/.hgsub Sun Oct 01 03:48:43 2023 -0400 @@ -1,1 +0,0 @@ -win95kggui/sub/ft2play = [git]https://github.com/8bitbubsy/ft2play \ No newline at end of file
--- a/.hgsubstate Fri Jun 23 22:44:17 2023 -0400 +++ b/.hgsubstate Sun Oct 01 03:48:43 2023 -0400 @@ -1,1 +0,0 @@ -c2b7df5f31c6e0cb61a0509e11482e3a48cb3ac9 win95kggui/sub/ft2play
--- a/win95kggui/Makefile Fri Jun 23 22:44:17 2023 -0400 +++ b/win95kggui/Makefile Sun Oct 01 03:48:43 2023 -0400 @@ -7,7 +7,7 @@ windres src/icon.rc src/icon.o -I./include ./bin2h src/bergsm.xm mv hdata.h include/bergsm.h - $(CC) -o win95kggui.exe src/win95kggui.c src/icon.o sub/ft2play/audiodrivers/winmm/*.c sub/ft2play/*.c $(CFLAGS) $(LDFLAGS) + $(CC) -o win95kggui.exe src/win95kggui.c src/icon.o dep/ft2play/audiodrivers/winmm/*.c dep/ft2play/*.c $(CFLAGS) $(LDFLAGS) clean: rm -f include/bergsm.h *.o src/*.o *.exe
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/.gitignore Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,24 @@ + +*.db +*.ilk +*.pdb +*.log +*.res +*.tlog +*.obj +*.idb +*.exe +*.enc +*.cache +ft2play/vs2019_project/.vs/ft2play/v16/.suo +*.ipch +.DS_Store +ft2play/vs2019_project/Release/ft2play.vcxproj.FileListAbsolute.txt +ft2play/vs2019_project/x64/Debug/ft2play.vcxproj.FileListAbsolute.txt +*.cod +ft2play/vs2019_project/Debug/ft2play.vcxproj.FileListAbsolute.txt +*.opendb +*.db-shm +*.db-wal +*.recipe +ft2play/vs2019_project/Release/ft2play.iobj
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/CMakeLists.txt Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.7) + +project(ft2play C) + +find_package(SDL2 REQUIRED) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${ft2play_SOURCE_DIR}/ft2play/release/other/") + +file(GLOB ft2play_SRC + "${ft2play_SOURCE_DIR}/audiodrivers/sdl/*.c" + "${ft2play_SOURCE_DIR}/*.c" + "${ft2play_SOURCE_DIR}/ft2play/src/*.c" +) + +add_executable(ft2play ${ft2play_SRC}) + +target_include_directories(ft2play SYSTEM + PRIVATE ${SDL2_INCLUDE_DIRS}) + +if("${SDL2_LIBRARIES}" STREQUAL "") + message(WARNING "SDL2_LIBRARIES wasn't set, manually setting to SDL2::SDL2") + set(SDL2_LIBRARIES "SDL2::SDL2") +endif() + +target_link_libraries(ft2play + PRIVATE m pthread ${SDL2_LIBRARIES}) + +target_compile_definitions(ft2play + PRIVATE AUDIODRIVER_SDL) + +install(TARGETS ft2play + RUNTIME DESTINATION bin)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/LICENSE Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2020-2021, Olav Sørensen +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/README.md Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,18 @@ +# ft2play +Aims to be a bit-accurate C port of Fasttracker 2.09's XM replayer (SB16/WAV render mode). \ +This is a direct port of the original asm/Pascal source codes. \ +\ +The project contains example code in the ft2play folder on how to interface with the API. + +# Notes +- To compile ft2play (the test program) on macOS/Linux, you need SDL2 +- When compiling, you need to pass the driver to use as a compiler pre-processor definition (f.ex. AUDIODRIVER_WINMM, check "pmplay.h") +- This is <i>not</i> the same replayer/mixer code used in the FT2 clone (the FT2 clone also uses a port, but it has some audio precision improvements) +- The accuracy has only been compared against a handful of songs +- The code may not be 100% thread-safe (or safe in general), and as such I don't really recommend using this replayer in other projects. My primary goal was to create an accurate C port that people can use for reference. + +# How to test accuracy +1) Open FT2.08 or FT2.09 (use a fresh program start for every render) and load an XM/MOD module. Make sure "Stereo" and "Interpolation" are enabled in the config screen +2) Save as WAV with the following settings: Frequency = 44100, Amplification = 10 (not 4!) +3) Render the same song to WAV using ft2play (f.ex. "ft2play mysong.xm --render-to-wav") +4) Use a program capable of verifying the binary integrity between the two output files. If they differ, you found a problem, please create a GitHub issue for it :)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/audiodrivers/how_to_write_drivers.txt Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,30 @@ +---- How to write your own audio driver for ft2play ---- + +1) Include the header "../../pmp_mix.h" + +2) Implement the following functions using your audio API of choice: + + void lockMixer(void); // waits for the current mixing block to finish and disables further mixing + void unlockMixer(void); // enables mixing again + bool openMixer(int32_t mixingFrequency, int32_t mixingBufferSize); // 8000..96000, 256..8192 (true if ok, false if fail) + void closeMixer(void); + +3) When the audio API is requesting samples, make a call to mix_UpdateBuffer(), f.ex.: + + mix_UpdateBuffer((int16_t *)stream, len / 4); + +4) Make your own preprocessor define (f.ex. AUDIODRIVER_ALSA) and pass it to the compiler during compilation + (also remember to add the correct driver .c file to the compilation script) + +5) In "pmplay.h", insert your preprocessor define and include in the "AUDIO DRIVERS" #ifdef chain and + include your audio driver header in there. + +NOTE: + lockMixer() should be implemented in a way where you wait until the mix_UpdateBuffer() call has finished (important), + then you block further calls to mix_UpdateBuffer() until the mixer is unlocked again. + You should not send zeroes to the audio device while it's locked, as the lock/unlock pairs are usually called within + a very short time frame anyway. + +------------------------------------------------------- + +You can look at audiodrivers/sdl/sdldriver.c if you need some references...
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/audiodrivers/sdl/sdldriver.c Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,64 @@ +// SDL audio driver for ft2play + +#include <SDL2/SDL.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include "../../pmp_mix.h" + +static SDL_AudioDeviceID dev; + +static void SDLCALL audioCallback(void *userdata, Uint8 *stream, int len) +{ + mix_UpdateBuffer((int16_t *)stream, len / 4); // pmp_mix.c function + (void)userdata; +} + +void lockMixer(void) +{ + if (dev != 0) + SDL_LockAudioDevice(dev); +} + +void unlockMixer(void) +{ + if (dev != 0) + SDL_UnlockAudioDevice(dev); +} + +bool openMixer(int32_t mixingFrequency, int32_t mixingBufferSize) +{ + SDL_AudioSpec want, have; + + if (dev != 0) + return true; + + if (SDL_Init(SDL_INIT_AUDIO) != 0) + return false; + + memset(&want, 0, sizeof (want)); + want.freq = mixingFrequency; + want.format = AUDIO_S16; + want.channels = 2; + want.samples = (uint16_t)mixingBufferSize; + want.callback = audioCallback; + + dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0); + if (dev == 0) + return false; + + SDL_PauseAudioDevice(dev, false); + return true; +} + +void closeMixer(void) +{ + if (dev != 0) + { + SDL_PauseAudioDevice(dev, true); + SDL_CloseAudioDevice(dev); + dev = 0; + } + + SDL_Quit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/audiodrivers/sdl/sdldriver.h Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,9 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +void lockMixer(void); +void unlockMixer(void); +bool openMixer(int32_t mixingFrequency, int32_t mixingBufferSize); +void closeMixer(void);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/audiodrivers/winmm/winmm.c Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,177 @@ +/* winmm audio driver for ft2play +** +** Warning: This might not be 100% thread-safe or lock-safe! +*/ + +#define WIN32_LEAN_AND_MEAN + +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <windows.h> +#include <mmsystem.h> +#include "../../pmp_mix.h" + +#define MIX_BUF_NUM 4 + +static volatile BOOL mixerOpened, mixerBusy, mixerLocked; +static uint8_t currBuffer; +static int16_t *audioBuffer[MIX_BUF_NUM]; +static int32_t bufferSize; +static HANDLE hThread, hAudioSem; +static WAVEHDR waveBlocks[MIX_BUF_NUM]; +static HWAVEOUT hWave; + +static DWORD WINAPI mixThread(LPVOID lpParam) +{ + (void)lpParam; + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + while (mixerOpened) + { + WAVEHDR *waveBlock = &waveBlocks[currBuffer]; + + if (!mixerLocked) + { + mixerBusy = true; + mix_UpdateBuffer((int16_t *)waveBlock->lpData, bufferSize); // pmp_mix.c function + mixerBusy = false; + } + + waveOutWrite(hWave, waveBlock, sizeof (WAVEHDR)); + currBuffer = (currBuffer + 1) % MIX_BUF_NUM; + WaitForSingleObject(hAudioSem, INFINITE); // wait for buffer fill request + } + + return 0; +} + +static void CALLBACK waveProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) +{ + if (uMsg == WOM_DONE) + ReleaseSemaphore(hAudioSem, 1, NULL); + + (void)hWaveOut; + (void)uMsg; + (void)dwInstance; + (void)dwParam1; + (void)dwParam2; +} + +void lockMixer(void) +{ + mixerLocked = true; + while (mixerBusy); +} + +void unlockMixer(void) +{ + mixerBusy = false; + mixerLocked = false; +} + +void closeMixer(void) +{ + mixerOpened = false; + mixerBusy = false; + + if (hAudioSem != NULL) + ReleaseSemaphore(hAudioSem, 1, NULL); + + if (hThread != NULL) + { + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hThread); + hThread = NULL; + } + + if (hAudioSem != NULL) + { + CloseHandle(hAudioSem); + hAudioSem = NULL; + } + + if (hWave != NULL) + { + waveOutReset(hWave); + + for (int32_t i = 0; i < MIX_BUF_NUM; i++) + { + if (waveBlocks[i].dwUser != 0xFFFF) + waveOutUnprepareHeader(hWave, &waveBlocks[i], sizeof (WAVEHDR)); + } + + waveOutClose(hWave); + hWave = NULL; + } + + for (int32_t i = 0; i < MIX_BUF_NUM; i++) + { + if (audioBuffer[i] != NULL) + { + free(audioBuffer[i]); + audioBuffer[i] = NULL; + } + } +} + +bool openMixer(int32_t mixingFrequency, int32_t mixingBufferSize) +{ + DWORD threadID; + WAVEFORMATEX wfx; + + // don't unprepare headers on error + for (int32_t i = 0; i < MIX_BUF_NUM; i++) + waveBlocks[i].dwUser = 0xFFFF; + + closeMixer(); + bufferSize = mixingBufferSize; + + ZeroMemory(&wfx, sizeof (wfx)); + wfx.nSamplesPerSec = mixingFrequency; + wfx.wBitsPerSample = 16; + wfx.nChannels = 2; + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8); + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + + if (waveOutOpen(&hWave, WAVE_MAPPER, &wfx, (DWORD_PTR)&waveProc, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) + goto omError; + + // create semaphore for buffer fill requests + hAudioSem = CreateSemaphore(NULL, MIX_BUF_NUM - 1, MIX_BUF_NUM, NULL); + if (hAudioSem == NULL) + goto omError; + + // allocate WinMM mix buffers + for (int32_t i = 0; i < MIX_BUF_NUM; i++) + { + audioBuffer[i] = (int16_t *)calloc(mixingBufferSize, wfx.nBlockAlign); + if (audioBuffer[i] == NULL) + goto omError; + } + + // initialize WinMM mix headers + memset(waveBlocks, 0, sizeof (waveBlocks)); + for (int32_t i = 0; i < MIX_BUF_NUM; i++) + { + waveBlocks[i].lpData = (LPSTR)audioBuffer[i]; + waveBlocks[i].dwBufferLength = mixingBufferSize * wfx.nBlockAlign; + waveBlocks[i].dwFlags = WHDR_DONE; + + if (waveOutPrepareHeader(hWave, &waveBlocks[i], sizeof (WAVEHDR)) != MMSYSERR_NOERROR) + goto omError; + } + + currBuffer = 0; + mixerOpened = true; + + hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)mixThread, NULL, 0, &threadID); + if (hThread == NULL) + goto omError; + + return TRUE; + +omError: + closeMixer(); + return FALSE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/audiodrivers/winmm/winmm.h Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,9 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +void lockMixer(void); +void unlockMixer(void); +bool openMixer(int32_t mixingFrequency, int32_t mixingBufferSize); +void closeMixer(void);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/make-linux.sh Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,10 @@ +#!/bin/bash + +rm release/other/ft2play &> /dev/null +echo Compiling, please wait... + +gcc -DNDEBUG -DAUDIODRIVER_SDL ../audiodrivers/sdl/*.c ../*.c src/*.c -g0 -lSDL2 -lm -lpthread -Wshadow -Winit-self -Wall -Wno-uninitialized -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -O3 -o release/other/ft2play + +rm ../*.o src/*.o &> /dev/null + +echo Done. The executable can be found in \'release/other\' if everything went well.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/make-macos-arm.sh Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,12 @@ +#!/bin/bash + +echo Compiling arm64 binary, please wait... + +rm release/other/ft2play &> /dev/null + +clang -target arm64-apple-macos11 -mmacosx-version-min=11.0 -arch arm64 -march=armv8.3-a+sha3 -I/Library/Frameworks/SDL2.framework/Headers -F/Library/Frameworks -g0 -DNDEBUG -DAUDIODRIVER_SDL ../audiodrivers/sdl/*.c ../*.c src/*.c -O3 -lm -Winit-self -Wno-deprecated -Wextra -Wunused -mno-ms-bitfields -Wno-missing-field-initializers -Wswitch-default -framework SDL2 -framework Cocoa -lm -o release/other/ft2play +strip release/other/ft2play +install_name_tool -change @rpath/SDL2.framework/Versions/A/SDL2 @executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2 release/other/ft2play + +rm ../*.o src/*.o &> /dev/null +echo Done. The executable can be found in \'release/other\' if everything went well.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/make-macos-intel.sh Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,12 @@ +#!/bin/bash + +echo Compiling 64-bit Intel binary, please wait... + +rm release/other/ft2play &> /dev/null + +clang -mmacosx-version-min=10.7 -arch x86_64 -mmmx -mfpmath=sse -msse2 -I/Library/Frameworks/SDL2.framework/Headers -F/Library/Frameworks -g0 -DNDEBUG -DAUDIODRIVER_SDL ../audiodrivers/sdl/*.c ../*.c src/*.c -O3 -lm -Winit-self -Wno-deprecated -Wextra -Wunused -mno-ms-bitfields -Wno-missing-field-initializers -Wswitch-default -framework SDL2 -framework Cocoa -lm -o release/other/ft2play +strip release/other/ft2play +install_name_tool -change @rpath/SDL2.framework/Versions/A/SDL2 @executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2 release/other/ft2play + +rm ../*.o src/*.o &> /dev/null +echo Done. The executable can be found in \'release/other\' if everything went well.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/make-msys2-clang.sh Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,10 @@ +#!/bin/bash + +rm release/win64/ft2play &> /dev/null +echo Compiling, please wait... + +clang -DNDEBUG -DAUDIODRIVER_WINMM ../audiodrivers/winmm/*.c ../*.c src/*.c -g0 -lwinmm -lm -lpthread -Wshadow -Winit-self -Wall -Wno-uninitialized -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -m64 -mmmx -mfpmath=sse -msse2 -O3 -s -o release/win64/ft2play + +rm ../*.o src/*.o &> /dev/null + +echo Done. The executable can be found in \'release/win64\' if everything went well.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/make-msys2-gcc.sh Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,10 @@ +#!/bin/bash + +rm release/win64/ft2play &> /dev/null +echo Compiling, please wait... + +gcc -DNDEBUG -DAUDIODRIVER_WINMM ../audiodrivers/winmm/*.c ../*.c src/*.c -g0 -lwinmm -lm -lpthread -Wshadow -Winit-self -Wall -Wno-uninitialized -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -m64 -mmmx -mfpmath=sse -msse2 -O3 -s -o release/win64/ft2play + +rm ../*.o src/*.o &> /dev/null + +echo Done. The executable can be found in \'release/win64\' if everything went well.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/release/other/.gitignore Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/release/win32/.gitignore Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/src/ft2play.c Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,324 @@ +/* Example program for interfacing with ft2play. +** +** Please excuse my disgusting platform-independant code here... +*/ + +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "../../pmplay.h" +#include "posix.h" + +// defaults when not overriden by argument switches +#define DEFAULT_MIX_FREQ 44100 +#define DEFAULT_MIX_BUFSIZE 1024 +#define DEFAULT_MIX_AMP 10 +#define DEFAULT_MASTERVOL 256 +#define DEFAULT_INTRP_FLAG true +#define DEFAULT_VRAMP_FLAG true + +// set to true if you want ft2play to always render to WAV +#define DEFAULT_WAVRENDER_MODE_FLAG false + +// default settings +static bool renderToWavFlag = DEFAULT_WAVRENDER_MODE_FLAG; +static bool interpolation = DEFAULT_INTRP_FLAG; +static bool volumeRamping = DEFAULT_VRAMP_FLAG; +static int32_t mixingAmp = DEFAULT_MIX_AMP; +static int32_t masterVolume = DEFAULT_MASTERVOL; +static int32_t mixingFrequency = DEFAULT_MIX_FREQ; +static int32_t mixingBufferSize = DEFAULT_MIX_BUFSIZE; + +// ---------------------------------------------------------- + +static volatile bool programRunning; +static char *filename, *WAVRenderFilename; + +static void showUsage(void); +static void handleArguments(int argc, char *argv[]); +static void readKeyboard(void); +static int32_t renderToWav(void); + +// yuck! +#ifdef _WIN32 +void wavRecordingThread(void *arg) +#else +void *wavRecordingThread(void *arg) +#endif +{ + (void)arg; + WAVDump_Record(WAVRenderFilename); +#ifndef _WIN32 + return NULL; +#endif +} + +#ifndef _WIN32 +static void sigtermFunc(int32_t signum) +{ + programRunning = false; // unstuck main loop + WAVDump_Flag = false; // unstuck WAV render loop + (void)signum; +} +#endif + +int main(int argc, char *argv[]) +{ + if (argc < 2 || (argc == 2 && (!strcmp(argv[1], "/?") || !strcmp(argv[1], "-h")))) + { + showUsage(); + return 1; + } + + handleArguments(argc, argv); + + // TODO: Return proper error codes to tell the user what went wrong... + if (!initMusic(mixingFrequency, mixingBufferSize, interpolation, volumeRamping)) + { + printf("Error: Out of memory while setting up replayer!\n"); + return 1; + } + + // TODO: Return proper error codes to tell the user what went wrong... + if (!loadMusic(filename)) + { + printf("Error: Couldn't load song!\n"); + return 1; + } + + // you only need to make a call to these if amp != 4 and/or mastervol != 256, which are the FT2 defaults + if (mixingAmp != 4) setAmp(mixingAmp); + if (masterVolume != 256) setMasterVol(masterVolume); + + // trap sigterm on Linux/macOS (since we need to properly revert the terminal) +#ifndef _WIN32 + struct sigaction action; + memset(&action, 0, sizeof (struct sigaction)); + action.sa_handler = sigtermFunc; + sigaction(SIGTERM, &action, NULL); +#endif + + if (renderToWavFlag) + return renderToWav(); + + if (!startMusic()) + { + printf("Error: Couldn't start the audio system!\n"); + freeMusic(); + return 1; + } + + startPlaying(); + + printf("Playing, press ESC to stop...\n"); + printf("\n"); + printf("Controls:\n"); + printf("Esc=Quit Space=Toggle Pause Plus = inc. song pos Minus = dec. song pos\n"); + printf("\n"); + printf("Mixing frequency: %dHz\n", realReplayRate); + printf("Linear interpolation: %s\n", interpolation ? "On" : "Off"); + printf("Volume ramping: %s\n", volumeRamping ? "On" : "Off"); + printf("Mixing amp: %d\n", boostLevel); + printf("Mixing volume: %d\n", masterVol); + printf("\n"); + printf("Name: %s\n", song.name); + printf("Channels: %d\n", song.antChn); + printf("Instruments: %d\n", song.antInstrs); + printf("Song length: %d (restart pos: %d)\n", song.len, song.repS); + printf("\n"); + + printf("- STATUS -\n"); + +#ifndef _WIN32 + modifyTerminal(); +#endif + + programRunning = true; + while (programRunning) + { + readKeyboard(); + + printf(" Pos: %03d/%03d - Pattern: %03d - Row: %03d/%03d - Active voices: %02d/%02d %s\r", + song.songPos, song.len, song.pattNr, song.pattPos, song.pattLen, + getNumActiveVoices(), song.antChn, musicPaused ? "(PAUSED)" : " "); + fflush(stdout); + + Sleep(100); + } + +#ifndef _WIN32 + revertTerminal(); +#endif + + printf("\n"); + + stopMusic(); + freeMusic(); + + printf("Playback stopped.\n"); + return 0; +} + +static void showUsage(void) +{ + printf("Usage:\n"); + printf(" ft2play input_module [-f hz] [-b buffersize] [-a amp] [-m mastervol]\n"); + printf(" ft2play input_module [--no-intrp] [--no-vramp] [--render-to-wav]\n"); + printf("\n"); + printf(" Options:\n"); + printf(" input_module Specifies the module file to load (.XM/.MOD/.FT supported)\n"); + printf(" -f hz Specifies the mixing frequency (8000..96000)\n"); + printf(" -b buffersize Specifies the mixing buffer size (256..8192)\n"); + printf(" -a amp Specifies the mixing amplitude (1..32)\n"); + printf(" -m mastervol Specifies the mixing master volume (0..256). This setting\n"); + printf(" is ignored when rendering to WAV (always set to 256).\n"); + printf(" --no-intrp Disables linear interpolation\n"); + printf(" --no-vramp Disables volume ramping\n"); + printf(" --render-to-wav Renders song to WAV instead of playing it. The output\n"); + printf(" filename will be the input filename with .WAV added to the\n"); + printf(" end.\n"); + printf("\n"); + printf("Default settings:\n"); + printf(" - Mixing frequency: %d\n", DEFAULT_MIX_FREQ); + printf(" - Mixing buffer size: %d\n", DEFAULT_MIX_BUFSIZE); + printf(" - Amp: %d\n", DEFAULT_MIX_AMP); + printf(" - Master volume: %d\n", DEFAULT_MASTERVOL); + printf(" - Linear interpolation: %s\n", DEFAULT_INTRP_FLAG ? "On" : "Off"); + printf(" - Volume ramping: %s\n", DEFAULT_VRAMP_FLAG ? "On" : "Off"); + printf(" - WAV render mode: %s\n", DEFAULT_WAVRENDER_MODE_FLAG ? "On" : "Off"); + printf("\n"); +} + +static void handleArguments(int argc, char *argv[]) +{ + filename = argv[1]; + if (argc > 2) // parse arguments + { + for (int32_t i = 1; i < argc; i++) + { + if (!_stricmp(argv[i], "-f") && i+1 < argc) + { + const int32_t num = atoi(argv[i+1]); + mixingFrequency = CLAMP(num, 8000, 96000); + } + else if (!_stricmp(argv[i], "-b") && i+1 < argc) + { + const int32_t num = atoi(argv[i+1]); + mixingBufferSize = CLAMP(num, 256, 8192); + } + else if (!_stricmp(argv[i], "-a") && i+1 < argc) + { + const int32_t num = atoi(argv[i+1]); + mixingAmp = CLAMP(num, 1, 32); + } + else if (!_stricmp(argv[i], "-m") && i+1 < argc) + { + const int32_t num = atoi(argv[i+1]); + masterVolume = CLAMP(num, 0, 256); + } + else if (!_stricmp(argv[i], "--no-intrp")) + { + interpolation = false; + } + else if (!_stricmp(argv[i], "--no-vramp")) + { + volumeRamping = false; + } + else if (!_stricmp(argv[i], "--render-to-wav")) + { + renderToWavFlag = true; + } + } + } +} + +static void readKeyboard(void) +{ + if (_kbhit()) + { + const int32_t key = _getch(); + switch (key) + { + case 0x1B: // esc + programRunning = false; + break; + + case 0x20: // space + toggleMusic(); + break; + + case 0x2B: // numpad + + song.globVol = 64; + stopVoices(); // prevent stuck notes + setPos(song.songPos + 1, 0); + break; + + case 0x2D: // numpad - + song.globVol = 64; + stopVoices(); // prevent stuck notes + setPos(song.songPos - 1, 0); + break; + + default: break; + } + } +} + +static int32_t renderToWav(void) +{ + const size_t filenameLen = strlen(filename); + WAVRenderFilename = (char *)malloc(filenameLen+5); + + if (WAVRenderFilename == NULL) + { + printf("Error: Out of memory!\n"); + freeMusic(); + return 1; + } + + strcpy(WAVRenderFilename, filename); + strcat(WAVRenderFilename, ".wav"); + + /* The WAV render loop also sets/listens/clears "WAVDump_Flag", but let's set it now + ** since we're doing the render in a separate thread (to be able to force-abort it if + ** the user is pressing a key). + ** + ** If you don't want to create a thread for the render, you don't have to + ** set this flag, and you just call WAVDump_Record("output.wav") directly. + ** Though, some songs will render forever (if they Bxx-jump to a previous order), + ** thus having this in a thread is recommended so that you can force-abort it, if stuck. + */ + WAVDump_Flag = true; + if (!createSingleThread(wavRecordingThread)) + { + printf("Error: Couldn't create WAV rendering thread!\n"); + free(WAVRenderFilename); + freeMusic(); + return 1; + } + + printf("Rendering to WAV. If stuck forever, press any key to stop rendering...\n"); + +#ifndef _WIN32 + modifyTerminal(); +#endif + while (WAVDump_Flag) + { + Sleep(200); + if ( _kbhit()) + WAVDump_Flag = false; + } +#ifndef _WIN32 + revertTerminal(); +#endif + + closeSingleThread(); + + free(WAVRenderFilename); + freeMusic(); + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/src/posix.c Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,147 @@ +/* Hackish attempt at making certain routines portable. +** +** Warning: Do not use these for other projects! They are not safe for general use. +*/ + +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +static HANDLE hThread; + +bool createSingleThread(void (*threadFunc)(void *arg)) +{ + DWORD dwThreadId; + + if (hThread != NULL) + return false; + + hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadFunc, NULL, 0, &dwThreadId); + if (hThread == NULL) + return false; + + return true; +} + +void closeSingleThread(void) +{ + if (hThread != NULL) + { + WaitForSingleObject(hThread, INFINITE); + + CloseHandle(hThread); + hThread = NULL; + + } +} + +#else + +#include <unistd.h> +#include <termios.h> +#include <sys/ioctl.h> +#include <pthread.h> + +static bool threadOpen; +static pthread_t threadId; +static int32_t lastChar = -1; +static struct termios tOld, tNew; + +void Sleep(uint32_t ms) +{ + usleep(ms * 1000); +} + +bool createSingleThread(void *(*threadFunc)(void *arg)) +{ + if (threadOpen) + return false; + + const int32_t err = pthread_create(&threadId, NULL, threadFunc, NULL); + if (err) + { + threadOpen = false; + return false; + } + + threadOpen = true; + return true; +} + +void closeSingleThread(void) +{ + if (threadOpen) + { + pthread_join(threadId, NULL); + threadOpen = false; + } +} + +// the following routines were found on google, and were modified + +void modifyTerminal(void) +{ + tcgetattr(0, &tOld); + + tNew = tOld; + tNew.c_lflag &= ~ICANON; + tNew.c_lflag &= ~ECHO; + tNew.c_lflag &= ~ISIG; + tNew.c_cc[VMIN] = 1; + tNew.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &tNew); + + lastChar = -1; +} + +void revertTerminal(void) +{ + tcsetattr(0, TCSANOW, &tOld); +} + +bool _kbhit(void) +{ + uint8_t ch; + + if (lastChar != -1) + return true; + + tNew.c_cc[VMIN] = 0; + tcsetattr(0, TCSANOW, &tNew); + + const int32_t nread = read(0, &ch, 1); + tNew.c_cc[VMIN] = 1; + tcsetattr(0, TCSANOW, &tNew); + + if (nread == 1) + { + lastChar = ch; + return true; + } + + return false; +} + +int32_t _getch(void) +{ + char ch; + + if (lastChar != -1) + { + ch = lastChar; + lastChar = -1; + } + else + { + read(0, &ch, 1); + } + + return ch; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/src/posix.h Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,35 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +void closeSingleThread(void); + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> // Sleep() +#include <conio.h> // _kbhit(), _getch() + +bool createSingleThread(void (*threadFunc)(void *arg)); + +#else + +#define _stricmp strcasecmp +#define _strnicmp strncasecmp + +bool createSingleThread(void *(*threadFunc)(void *arg)); + +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <pthread.h> + +void Sleep(uint32_t ms); +void modifyTerminal(void); +void revertTerminal(void); +bool _kbhit(void); +int32_t _getch(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/vs2019_project/ft2play.sln Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30523.141 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ft2play", "ft2play.vcxproj", "{91B645FD-82AF-44D4-9D0A-CD74BC0CAC3C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {91B645FD-82AF-44D4-9D0A-CD74BC0CAC3C}.Debug|Win32.ActiveCfg = Debug|Win32 + {91B645FD-82AF-44D4-9D0A-CD74BC0CAC3C}.Debug|Win32.Build.0 = Debug|Win32 + {91B645FD-82AF-44D4-9D0A-CD74BC0CAC3C}.Release|Win32.ActiveCfg = Release|Win32 + {91B645FD-82AF-44D4-9D0A-CD74BC0CAC3C}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0302C100-588F-45AD-8EBB-D1D343CEC87B} + EndGlobalSection +EndGlobal
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/vs2019_project/ft2play.vcxproj Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\audiodrivers\winmm\winmm.c" /> + <ClCompile Include="..\..\pmplay.c" /> + <ClCompile Include="..\..\pmp_main.c" /> + <ClCompile Include="..\..\pmp_mix.c" /> + <ClCompile Include="..\..\snd_masm.c" /> + <ClCompile Include="..\..\tables.c" /> + <ClCompile Include="..\src\ft2play.c" /> + <ClCompile Include="..\src\posix.c" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\audiodrivers\winmm\winmm.h" /> + <ClInclude Include="..\..\pmplay.h" /> + <ClInclude Include="..\..\pmp_main.h" /> + <ClInclude Include="..\..\pmp_mix.h" /> + <ClInclude Include="..\..\snd_masm.h" /> + <ClInclude Include="..\..\tables.h" /> + <ClInclude Include="..\src\posix.h" /> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{91B645FD-82AF-44D4-9D0A-CD74BC0CAC3C}</ProjectGuid> + <ProjectName>ft2play</ProjectName> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <CharacterSet>Unicode</CharacterSet> + <WholeProgramOptimization>true</WholeProgramOptimization> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v142</PlatformToolset> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion> + <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> + <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> + <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> + <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> + <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> + <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> + <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> + <CustomBuildAfterTargets Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + </CustomBuildAfterTargets> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <IncludePath>sdl\include;$(VCInstallDir)include;$(IncludePath)</IncludePath> + <LibraryPath>sdl\lib;$(VCInstallDir)lib;$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <OutDir>..\release\win32\</OutDir> + <TargetName>ft2play</TargetName> + <IncludePath>sdl\include;$(VCInstallDir)include;$(IncludePath)</IncludePath> + <LibraryPath>sdl\lib;$(VCInstallDir)lib;$(LibraryPath)</LibraryPath> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Midl> + <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MkTypLibCompatible>true</MkTypLibCompatible> + <SuppressStartupBanner>true</SuppressStartupBanner> + <TargetEnvironment>Win32</TargetEnvironment> + <HeaderFileName> + </HeaderFileName> + </Midl> + <ClCompile> + <PreprocessorDefinitions>DEBUG;_DEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;AUDIODRIVER_WINMM;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <WarningLevel>Level4</WarningLevel> + <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet> + <OmitFramePointers /> + <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType> + </ClCompile> + <ResourceCompile> + <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <Culture>0x0409</Culture> + </ResourceCompile> + <Link> + <AdditionalDependencies>winmm.lib;%(AdditionalDependencies)</AdditionalDependencies> + <GenerateDebugInformation>true</GenerateDebugInformation> + <SubSystem>Console</SubSystem> + <LargeAddressAware>true</LargeAddressAware> + <ImageHasSafeExceptionHandlers /> + <MinimumRequiredVersion>5.1</MinimumRequiredVersion> + <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors> + </Link> + <Bscmake /> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Midl> + <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MkTypLibCompatible>true</MkTypLibCompatible> + <SuppressStartupBanner>true</SuppressStartupBanner> + <TargetEnvironment>Win32</TargetEnvironment> + <HeaderFileName> + </HeaderFileName> + </Midl> + <ClCompile> + <Optimization>MaxSpeed</Optimization> + <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion> + <IntrinsicFunctions>true</IntrinsicFunctions> + <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> + <PreprocessorDefinitions>NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;AUDIODRIVER_WINMM;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <BufferSecurityCheck>false</BufferSecurityCheck> + <FunctionLevelLinking>true</FunctionLevelLinking> + <WarningLevel>Level4</WarningLevel> + <CompileAsManaged> + </CompileAsManaged> + <DebugInformationFormat>None</DebugInformationFormat> + <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeTypeInfo>false</RuntimeTypeInfo> + <OmitFramePointers>true</OmitFramePointers> + <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType> + </ClCompile> + <ResourceCompile> + <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <Culture>0x0409</Culture> + </ResourceCompile> + <Link> + <AdditionalDependencies>winmm.lib;libcmt.lib;libvcruntime.lib;libucrt.lib;%(AdditionalDependencies)</AdditionalDependencies> + <MapFileName> + </MapFileName> + <SubSystem>Console</SubSystem> + <EntryPointSymbol> + </EntryPointSymbol> + <BaseAddress> + </BaseAddress> + <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors> + <LargeAddressAware>true</LargeAddressAware> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <MinimumRequiredVersion>5.1</MinimumRequiredVersion> + <GenerateDebugInformation>false</GenerateDebugInformation> + <SetChecksum>true</SetChecksum> + <FixedBaseAddress>false</FixedBaseAddress> + </Link> + <Bscmake /> + <CustomBuildStep> + <Command> + </Command> + </CustomBuildStep> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + <ProjectReference /> + <Manifest> + <OutputManifestFile> + </OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" /> + </ImportGroup> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/vs2019_project/ft2play.vcxproj.filters Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="replayer"> + <UniqueIdentifier>{2cfd8b0c-481e-4730-981b-6b8213c6bfd4}</UniqueIdentifier> + </Filter> + <Filter Include="replayer\audiodrivers"> + <UniqueIdentifier>{8b2b35ad-3eac-48ea-b6ce-20cc06e98a9c}</UniqueIdentifier> + </Filter> + <Filter Include="replayer\audiodrivers\winmm"> + <UniqueIdentifier>{431a929b-85e5-4c94-9647-b6217c5ed9d4}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\pmp_main.c"> + <Filter>replayer</Filter> + </ClCompile> + <ClCompile Include="..\..\pmp_mix.c"> + <Filter>replayer</Filter> + </ClCompile> + <ClCompile Include="..\..\pmplay.c"> + <Filter>replayer</Filter> + </ClCompile> + <ClCompile Include="..\..\snd_masm.c"> + <Filter>replayer</Filter> + </ClCompile> + <ClCompile Include="..\..\tables.c"> + <Filter>replayer</Filter> + </ClCompile> + <ClCompile Include="..\src\ft2play.c" /> + <ClCompile Include="..\..\audiodrivers\winmm\winmm.c"> + <Filter>replayer\audiodrivers\winmm</Filter> + </ClCompile> + <ClCompile Include="..\src\posix.c" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\pmp_main.h"> + <Filter>replayer</Filter> + </ClInclude> + <ClInclude Include="..\..\pmp_mix.h"> + <Filter>replayer</Filter> + </ClInclude> + <ClInclude Include="..\..\pmplay.h"> + <Filter>replayer</Filter> + </ClInclude> + <ClInclude Include="..\..\snd_masm.h"> + <Filter>replayer</Filter> + </ClInclude> + <ClInclude Include="..\..\tables.h"> + <Filter>replayer</Filter> + </ClInclude> + <ClInclude Include="..\..\audiodrivers\winmm\winmm.h"> + <Filter>replayer\audiodrivers\winmm</Filter> + </ClInclude> + <ClInclude Include="..\src\posix.h" /> + </ItemGroup> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/ft2play/vs2019_project/ft2play.vcxproj.user Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup /> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/pmp_main.c Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,1853 @@ +/* - main XM replayer - +** +** NOTE: Effect handling is slightly different because +** I've removed the channel muting logic. +** Muted channels would only process *some* effects, but +** since we can't mute channels, we don't care about this. +** +** In FT2, the only way to mute a channel is through the +** tracker itself, so this is not really needed in a replayer. +*/ + +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include "pmplay.h" +#include "pmp_mix.h" +#include "snd_masm.h" +#include "tables.h" + +#define MAX_FRQ 32000 +#define MAX_NOTES (10*12*16+16) + +static tonTyp nilPatternLine[32]; // 8bb: used for non-allocated (empty) patterns + +typedef void (*volKolEfxRoutine)(stmTyp *ch); +typedef void (*volKolEfxRoutine2)(stmTyp *ch, uint8_t *volKol); +typedef void (*efxRoutine)(stmTyp *ch, uint8_t param); + +static void retrigVolume(stmTyp *ch) +{ + ch->realVol = ch->oldVol; + ch->outVol = ch->oldVol; + ch->outPan = ch->oldPan; + ch->status |= IS_Vol + IS_Pan + IS_QuickVol; +} + +static void retrigEnvelopeVibrato(stmTyp *ch) +{ + // 8bb: reset vibrato position + if (!(ch->waveCtrl & 0x04)) + ch->vibPos = 0; + + /* + ** 8bb: + ** In FT2.00 .. FT2.09, if the sixth bit of "ch->waveCtrl" is set + ** (from effect E7x where x is $4..$7 or $C..$F) and you trigger a note, + ** the replayer interrupt will freeze / lock up. This is because of a + ** label bug in the original code, causing it to jump back to itself + ** indefinitely. + */ + + // 8bb: safely reset tremolo position + if (!(ch->waveCtrl & 0x40)) + ch->tremPos = 0; + + ch->retrigCnt = 0; + ch->tremorPos = 0; + + ch->envSustainActive = true; + + instrTyp *ins = ch->instrSeg; + + if (ins->envVTyp & ENV_ENABLED) + { + ch->envVCnt = 65535; + ch->envVPos = 0; + } + + if (ins->envPTyp & ENV_ENABLED) + { + ch->envPCnt = 65535; + ch->envPPos = 0; + } + + ch->fadeOutSpeed = ins->fadeOut; // 8bb: ranges 0..4095 (FT2 doesn't check if it's higher than 4095!) + + // 8bb: final fadeout range is in fact 0..32768, and not 0..65536 like the XM format doc says + ch->fadeOutAmp = 32768; + + if (ins->vibDepth > 0) + { + ch->eVibPos = 0; + + if (ins->vibSweep > 0) + { + ch->eVibAmp = 0; + ch->eVibSweep = (ins->vibDepth << 8) / ins->vibSweep; + } + else + { + ch->eVibAmp = ins->vibDepth << 8; + ch->eVibSweep = 0; + } + } +} + +static void keyOff(stmTyp *ch) +{ + instrTyp *ins = ch->instrSeg; + + if (!(ins->envPTyp & ENV_ENABLED)) // 8bb: probably an FT2 bug + { + if (ch->envPCnt >= (uint16_t)ins->envPP[ch->envPPos][0]) + ch->envPCnt = ins->envPP[ch->envPPos][0]-1; + } + + if (ins->envVTyp & ENV_ENABLED) + { + if (ch->envVCnt >= (uint16_t)ins->envVP[ch->envVPos][0]) + ch->envVCnt = ins->envVP[ch->envVPos][0]-1; + } + else + { + ch->realVol = 0; + ch->outVol = 0; + ch->status |= IS_Vol + IS_QuickVol; + } + + ch->envSustainActive = false; +} + +uint32_t getFrequenceValue(uint16_t period) // 8bb: converts period to 16.16fp resampling delta +{ + uint32_t delta; + + if (period == 0) + return 0; + + if (linearFrqTab) + { + const uint16_t invPeriod = (12 * 192 * 4) - period; // 8bb: this intentionally underflows uint16_t to be accurate to FT2 + + const uint32_t quotient = invPeriod / 768; + const uint32_t remainder = invPeriod % 768; + + const int32_t octShift = 14 - quotient; + + delta = (uint32_t)(((int64_t)logTab[remainder] * (int32_t)frequenceMulFactor) >> 24); + delta >>= (octShift & 31); // 8bb: added needed 32-bit bitshift mask + } + else + { + delta = frequenceDivFactor / (uint32_t)period; + } + + return delta; +} + +static void startTone(uint8_t ton, uint8_t effTyp, uint8_t eff, stmTyp *ch) +{ + if (ton == NOTE_KEYOFF) + { + keyOff(ch); + return; + } + + // 8bb: if we came from Rxy (retrig), we didn't check note (Ton) yet + if (ton == 0) + { + ton = ch->tonNr; + if (ton == 0) + return; // 8bb: if still no note, return + } + + ch->tonNr = ton; + + instrTyp *ins = instr[ch->instrNr]; + if (ins == NULL) + ins = instr[0]; + + ch->instrSeg = ins; + ch->mute = ins->mute; + + uint8_t smp = ins->ta[ton-1] & 0xF; // 8bb: added for safety + ch->sampleNr = smp; + + sampleTyp *s = &ins->samp[smp]; + ch->relTonNr = s->relTon; + + ton += ch->relTonNr; + if (ton >= 10*12) + return; + + ch->oldVol = s->vol; + ch->oldPan = s->pan; + + if (effTyp == 0x0E && (eff & 0xF0) == 0x50) // 8bb: EFx - Set Finetune + ch->fineTune = ((eff & 0x0F) << 4) - 128; + else + ch->fineTune = s->fine; + + if (ton != 0) + { + const uint16_t tmpTon = ((ton-1) << 4) + (((int8_t)ch->fineTune >> 3) + 16); // 8bb: 0..1935 + if (tmpTon < MAX_NOTES) // 8bb: tmpTon is *always* below MAX_NOTES here, this check is not needed + ch->outPeriod = ch->realPeriod = note2Period[tmpTon]; + } + + ch->status |= IS_Period + IS_Vol + IS_Pan + IS_NyTon + IS_QuickVol; + + if (effTyp == 9) // 8bb: 9xx - Set Sample Offset + { + if (eff) + ch->smpOffset = ch->eff; + + ch->smpStartPos = ch->smpOffset << 8; + } + else + { + ch->smpStartPos = 0; + } + + P_StartTone(s, ch->smpStartPos); +} + +static void volume(stmTyp *ch, uint8_t param); // 8bb: volume slide +static void vibrato2(stmTyp *ch); +static void tonePorta(stmTyp *ch, uint8_t param); + +static void dummy(stmTyp *ch, uint8_t param) +{ + return; + + (void)ch; + (void)param; +} + +static void finePortaUp(stmTyp *ch, uint8_t param) +{ + if (param == 0) + param = ch->fPortaUpSpeed; + + ch->fPortaUpSpeed = param; + + ch->realPeriod -= param << 2; + if ((int16_t)ch->realPeriod < 1) + ch->realPeriod = 1; + + ch->outPeriod = ch->realPeriod; + ch->status |= IS_Period; +} + +static void finePortaDown(stmTyp *ch, uint8_t param) +{ + if (param == 0) + param = ch->fPortaDownSpeed; + + ch->fPortaDownSpeed = param; + + ch->realPeriod += param << 2; + if ((int16_t)ch->realPeriod > MAX_FRQ-1) // 8bb: FT2 bug, should've been unsigned comparison! + ch->realPeriod = MAX_FRQ-1; + + ch->outPeriod = ch->realPeriod; + ch->status |= IS_Period; +} + +static void setGlissCtrl(stmTyp *ch, uint8_t param) +{ + ch->glissFunk = param; +} + +static void setVibratoCtrl(stmTyp *ch, uint8_t param) +{ + ch->waveCtrl = (ch->waveCtrl & 0xF0) | param; +} + +static void jumpLoop(stmTyp *ch, uint8_t param) +{ + if (param == 0) + { + ch->pattPos = song.pattPos & 0xFF; + } + else if (ch->loopCnt == 0) + { + ch->loopCnt = param; + + song.pBreakPos = ch->pattPos; + song.pBreakFlag = true; + } + else if (--ch->loopCnt > 0) + { + song.pBreakPos = ch->pattPos; + song.pBreakFlag = true; + } +} + +static void setTremoloCtrl(stmTyp *ch, uint8_t param) +{ + ch->waveCtrl = (param << 4) | (ch->waveCtrl & 0x0F); +} + +static void volFineUp(stmTyp *ch, uint8_t param) +{ + if (param == 0) + param = ch->fVolSlideUpSpeed; + + ch->fVolSlideUpSpeed = param; + + ch->realVol += param; + if (ch->realVol > 64) + ch->realVol = 64; + + ch->outVol = ch->realVol; + ch->status |= IS_Vol; +} + +static void volFineDown(stmTyp *ch, uint8_t param) +{ + if (param == 0) + param = ch->fVolSlideDownSpeed; + + ch->fVolSlideDownSpeed = param; + + ch->realVol -= param; + if ((int8_t)ch->realVol < 0) + ch->realVol = 0; + + ch->outVol = ch->realVol; + ch->status |= IS_Vol; +} + +static void noteCut0(stmTyp *ch, uint8_t param) +{ + if (param == 0) // 8bb: only a parameter of zero is handled here + { + ch->realVol = 0; + ch->outVol = 0; + ch->status |= IS_Vol + IS_QuickVol; + } +} + +static void pattDelay(stmTyp *ch, uint8_t param) +{ + if (song.pattDelTime2 == 0) + song.pattDelTime = param + 1; + + (void)ch; +} + +static const efxRoutine EJumpTab_TickZero[16] = +{ + dummy, // 0 + finePortaUp, // 1 + finePortaDown, // 2 + setGlissCtrl, // 3 + setVibratoCtrl, // 4 + dummy, // 5 + jumpLoop, // 6 + setTremoloCtrl, // 7 + dummy, // 8 + dummy, // 9 + volFineUp, // A + volFineDown, // B + noteCut0, // C + dummy, // D + pattDelay, // E + dummy // F +}; + +static void E_Effects_TickZero(stmTyp *ch, uint8_t param) +{ + EJumpTab_TickZero[param >> 4](ch, param & 0x0F); +} + +static void posJump(stmTyp *ch, uint8_t param) +{ + song.songPos = (int16_t)param - 1; + song.pBreakPos = 0; + song.posJumpFlag = true; + + (void)ch; +} + +static void pattBreak(stmTyp *ch, uint8_t param) +{ + song.posJumpFlag = true; + + param = ((param >> 4) * 10) + (param & 0x0F); + if (param <= 63) + song.pBreakPos = param; + else + song.pBreakPos = 0; + + (void)ch; +} + +static void setSpeed(stmTyp *ch, uint8_t param) +{ + if (param >= 32) + { + song.speed = param; + P_SetSpeed(song.speed); + } + else + { + song.timer = song.tempo = param; + } + + (void)ch; +} + +static void setGlobaVol(stmTyp *ch, uint8_t param) +{ + if (param > 64) + param = 64; + + song.globVol = param; + + stmTyp *c = stm; + for (int32_t i = 0; i < song.antChn; i++, c++) // 8bb: this updates the volume for all voices + c->status |= IS_Vol; + + (void)ch; +} + +static void setEnvelopePos(stmTyp *ch, uint8_t param) +{ + int8_t envPos; + bool envUpdate; + int16_t newEnvPos; + + instrTyp *ins = ch->instrSeg; + + // *** VOLUME ENVELOPE *** + if (ins->envVTyp & ENV_ENABLED) + { + ch->envVCnt = param - 1; + + envPos = 0; + envUpdate = true; + newEnvPos = param; + + if (ins->envVPAnt > 1) + { + envPos++; + for (int32_t i = 0; i < ins->envVPAnt-1; i++) + { + if (newEnvPos < ins->envVP[envPos][0]) + { + envPos--; + + newEnvPos -= ins->envVP[envPos][0]; + if (newEnvPos == 0) + { + envUpdate = false; + break; + } + + if (ins->envVP[envPos+1][0] <= ins->envVP[envPos][0]) + { + envUpdate = true; + break; + } + + ch->envVIPValue = ((ins->envVP[envPos+1][1] - ins->envVP[envPos][1]) & 0xFF) << 8; + ch->envVIPValue /= (ins->envVP[envPos+1][0] - ins->envVP[envPos][0]); + + ch->envVAmp = (ch->envVIPValue * (newEnvPos - 1)) + ((ins->envVP[envPos][1] & 0xFF) << 8); + + envPos++; + + envUpdate = false; + break; + } + + envPos++; + } + + if (envUpdate) + envPos--; + } + + if (envUpdate) + { + ch->envVIPValue = 0; + ch->envVAmp = (ins->envVP[envPos][1] & 0xFF) << 8; + } + + if (envPos >= ins->envVPAnt) + { + envPos = ins->envVPAnt - 1; + if (envPos < 0) + envPos = 0; + } + + ch->envVPos = envPos; + } + + // *** PANNING ENVELOPE *** + if (ins->envVTyp & ENV_SUSTAIN) // 8bb: FT2 bug? (should probably have been "ins->envPTyp & ENV_ENABLED") + { + ch->envPCnt = param - 1; + + envPos = 0; + envUpdate = true; + newEnvPos = param; + + if (ins->envPPAnt > 1) + { + envPos++; + for (int32_t i = 0; i < ins->envPPAnt-1; i++) + { + if (newEnvPos < ins->envPP[envPos][0]) + { + envPos--; + + newEnvPos -= ins->envPP[envPos][0]; + if (newEnvPos == 0) + { + envUpdate = false; + break; + } + + if (ins->envPP[envPos + 1][0] <= ins->envPP[envPos][0]) + { + envUpdate = true; + break; + } + + ch->envPIPValue = ((ins->envPP[envPos+1][1] - ins->envPP[envPos][1]) & 0xFF) << 8; + ch->envPIPValue /= (ins->envPP[envPos+1][0] - ins->envPP[envPos][0]); + + ch->envPAmp = (ch->envPIPValue * (newEnvPos - 1)) + ((ins->envPP[envPos][1] & 0xFF) << 8); + + envPos++; + + envUpdate = false; + break; + } + + envPos++; + } + + if (envUpdate) + envPos--; + } + + if (envUpdate) + { + ch->envPIPValue = 0; + ch->envPAmp = (ins->envPP[envPos][1] & 0xFF) << 8; + } + + if (envPos >= ins->envPPAnt) + { + envPos = ins->envPPAnt - 1; + if (envPos < 0) + envPos = 0; + } + + ch->envPPos = envPos; + } +} + +/* -- tick-zero volume column effects -- +** 2nd parameter is used for a volume column quirk with the Rxy command (multiretrig) +*/ + +static void v_SetVibSpeed(stmTyp *ch, uint8_t *volKol) +{ + *volKol = (ch->volKolVol & 0x0F) << 2; + if (*volKol != 0) + ch->vibSpeed = *volKol; +} + +static void v_Volume(stmTyp *ch, uint8_t *volKol) +{ + *volKol -= 16; + if (*volKol > 64) // 8bb: no idea why FT2 has this check, this can't happen... + *volKol = 64; + + ch->outVol = ch->realVol = *volKol; + ch->status |= IS_Vol + IS_QuickVol; +} + +static void v_FineSlideDown(stmTyp *ch, uint8_t *volKol) +{ + *volKol = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->realVol; + if ((int8_t)*volKol < 0) + *volKol = 0; + + ch->outVol = ch->realVol = *volKol; + ch->status |= IS_Vol; +} + +static void v_FineSlideUp(stmTyp *ch, uint8_t *volKol) +{ + *volKol = (ch->volKolVol & 0x0F) + ch->realVol; + if (*volKol > 64) + *volKol = 64; + + ch->outVol = ch->realVol = *volKol; + ch->status |= IS_Vol; +} + +static void v_SetPan(stmTyp *ch, uint8_t *volKol) +{ + *volKol <<= 4; + + ch->outPan = *volKol; + ch->status |= IS_Pan; +} + +// -- non-tick-zero volume column effects -- + +static void v_SlideDown(stmTyp *ch) +{ + uint8_t newVol = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->realVol; + if ((int8_t)newVol < 0) + newVol = 0; + + ch->outVol = ch->realVol = newVol; + ch->status |= IS_Vol; +} + +static void v_SlideUp(stmTyp *ch) +{ + uint8_t newVol = (ch->volKolVol & 0x0F) + ch->realVol; + if (newVol > 64) + newVol = 64; + + ch->outVol = ch->realVol = newVol; + ch->status |= IS_Vol; +} + +static void v_Vibrato(stmTyp *ch) +{ + const uint8_t param = ch->volKolVol & 0xF; + if (param > 0) + ch->vibDepth = param; + + vibrato2(ch); +} + +static void v_PanSlideLeft(stmTyp *ch) +{ + uint16_t tmp16 = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->outPan; + if (tmp16 < 256) // 8bb: includes an FT2 bug: pan-slide-left of 0 = set pan to 0 + tmp16 = 0; + + ch->outPan = (uint8_t)tmp16; + ch->status |= IS_Pan; +} + +static void v_PanSlideRight(stmTyp *ch) +{ + uint16_t tmp16 = (ch->volKolVol & 0x0F) + ch->outPan; + if (tmp16 > 255) + tmp16 = 255; + + ch->outPan = (uint8_t)tmp16; + ch->status |= IS_Pan; +} + +static void v_TonePorta(stmTyp *ch) +{ + tonePorta(ch, 0); // 8bb: the last parameter is actually not used in tonePorta() +} + +static void v_dummy(stmTyp *ch) +{ + (void)ch; + return; +} + +static void v_dummy2(stmTyp *ch, uint8_t *volKol) +{ + (void)ch; + (void)volKol; + return; +} + +static const volKolEfxRoutine VJumpTab_TickNonZero[16] = +{ + v_dummy, v_dummy, v_dummy, v_dummy, + v_dummy, v_dummy, v_SlideDown, v_SlideUp, + v_dummy, v_dummy, v_dummy, v_Vibrato, + v_dummy, v_PanSlideLeft, v_PanSlideRight, v_TonePorta +}; + +static const volKolEfxRoutine2 VJumpTab_TickZero[16] = +{ + v_dummy2, v_Volume, v_Volume, v_Volume, + v_Volume, v_Volume, v_dummy2, v_dummy2, + v_FineSlideDown, v_FineSlideUp, v_SetVibSpeed, v_dummy2, + v_SetPan, v_dummy2, v_dummy2, v_dummy2 +}; + +static void setPan(stmTyp *ch, uint8_t param) +{ + ch->outPan = param; + ch->status |= IS_Pan; +} + +static void setVol(stmTyp *ch, uint8_t param) +{ + if (param > 64) + param = 64; + + ch->outVol = ch->realVol = param; + ch->status |= IS_Vol + IS_QuickVol; +} + +static void xFinePorta(stmTyp *ch, uint8_t param) +{ + const uint8_t type = param >> 4; + param &= 0x0F; + + if (type == 0x1) // extra fine porta up + { + if (param == 0) + param = ch->ePortaUpSpeed; + + ch->ePortaUpSpeed = param; + + uint16_t newPeriod = ch->realPeriod; + + newPeriod -= param; + if ((int16_t)newPeriod < 1) + newPeriod = 1; + + ch->outPeriod = ch->realPeriod = newPeriod; + ch->status |= IS_Period; + } + else if (type == 0x2) // extra fine porta down + { + if (param == 0) + param = ch->ePortaDownSpeed; + + ch->ePortaDownSpeed = param; + + uint16_t newPeriod = ch->realPeriod; + + newPeriod += param; + if ((int16_t)newPeriod > MAX_FRQ-1) // 8bb: FT2 bug, should've been unsigned comparison! + newPeriod = MAX_FRQ-1; + + ch->outPeriod = ch->realPeriod = newPeriod; + ch->status |= IS_Period; + } +} + +static void doMultiRetrig(stmTyp *ch, uint8_t param) // 8bb: "param" is never used (needed for efx jumptable structure) +{ + uint8_t cnt = ch->retrigCnt + 1; + if (cnt < ch->retrigSpeed) + { + ch->retrigCnt = cnt; + return; + } + + ch->retrigCnt = 0; + + int16_t vol = ch->realVol; + switch (ch->retrigVol) + { + case 0x1: vol -= 1; break; + case 0x2: vol -= 2; break; + case 0x3: vol -= 4; break; + case 0x4: vol -= 8; break; + case 0x5: vol -= 16; break; + case 0x6: vol = (vol >> 1) + (vol >> 3) + (vol >> 4); break; + case 0x7: vol >>= 1; break; + case 0x8: break; // 8bb: does not change the volume + case 0x9: vol += 1; break; + case 0xA: vol += 2; break; + case 0xB: vol += 4; break; + case 0xC: vol += 8; break; + case 0xD: vol += 16; break; + case 0xE: vol = (vol >> 1) + vol; break; + case 0xF: vol += vol; break; + default: break; + } + vol = CLAMP(vol, 0, 64); + + ch->realVol = (uint8_t)vol; + ch->outVol = ch->realVol; + + if (ch->volKolVol >= 0x10 && ch->volKolVol <= 0x50) // 8bb: Set Volume (volume column) + { + ch->outVol = ch->volKolVol - 0x10; + ch->realVol = ch->outVol; + } + else if (ch->volKolVol >= 0xC0 && ch->volKolVol <= 0xCF) // 8bb: Set Panning (volume column) + { + ch->outPan = (ch->volKolVol & 0x0F) << 4; + } + + startTone(0, 0, 0, ch); + + (void)param; +} + +static void multiRetrig(stmTyp *ch, uint8_t param, uint8_t volumeColumnData) +{ + uint8_t tmpParam; + + tmpParam = param & 0x0F; + if (tmpParam == 0) + tmpParam = ch->retrigSpeed; + + ch->retrigSpeed = tmpParam; + + tmpParam = param >> 4; + if (tmpParam == 0) + tmpParam = ch->retrigVol; + + ch->retrigVol = tmpParam; + + if (volumeColumnData == 0) + doMultiRetrig(ch, 0); // 8bb: the second parameter is never used (needed for efx jumptable structure) +} + +static const efxRoutine JumpTab_TickZero[36] = +{ + dummy, // 0 + dummy, // 1 + dummy, // 2 + dummy, // 3 + dummy, // 4 + dummy, // 5 + dummy, // 6 + dummy, // 7 + setPan, // 8 + dummy, // 9 + dummy, // A + posJump, // B + setVol, // C + pattBreak, // D + E_Effects_TickZero, // E + setSpeed, // F + setGlobaVol, // G + dummy, // H + dummy, // I + dummy, // J + dummy, // K + setEnvelopePos, // L + dummy, // M + dummy, // N + dummy, // O + dummy, // P + dummy, // Q + dummy, // R + dummy, // S + dummy, // T + dummy, // U + dummy, // V + dummy, // W + xFinePorta, // X + dummy, // Y + dummy // Z +}; + +static void checkEffects(stmTyp *ch) // tick0 effect handling +{ + // volume column effects + uint8_t newVolKol = ch->volKolVol; // 8bb: manipulated by vol. column effects, then used for multiretrig check (FT2 quirk) + VJumpTab_TickZero[ch->volKolVol >> 4](ch, &newVolKol); + + // normal effects + const uint8_t param = ch->eff; + + if ((ch->effTyp == 0 && param == 0) || ch->effTyp > 35) + return; + + // 8bb: this one has to be done here instead of in the jumptable, as it needs the "newVolKol" parameter (FT2 quirk) + if (ch->effTyp == 27) // 8bb: Rxy - Multi Retrig + { + multiRetrig(ch, param, newVolKol); + return; + } + + JumpTab_TickZero[ch->effTyp](ch, ch->eff); +} + +static void fixTonePorta(stmTyp *ch, const tonTyp *p, uint8_t inst) +{ + if (p->ton > 0) + { + if (p->ton == NOTE_KEYOFF) + { + keyOff(ch); + } + else + { + const uint16_t portaTmp = (((p->ton-1) + ch->relTonNr) << 4) + (((int8_t)ch->fineTune >> 3) + 16); + if (portaTmp < MAX_NOTES) + { + ch->wantPeriod = note2Period[portaTmp]; + + if (ch->wantPeriod == ch->realPeriod) + ch->portaDir = 0; + else if (ch->wantPeriod > ch->realPeriod) + ch->portaDir = 1; + else + ch->portaDir = 2; + } + } + } + + if (inst > 0) + { + retrigVolume(ch); + + if (p->ton != NOTE_KEYOFF) + retrigEnvelopeVibrato(ch); + } +} + +static void getNewNote(stmTyp *ch, const tonTyp *p) +{ + ch->volKolVol = p->vol; + + if (ch->effTyp == 0) + { + if (ch->eff != 0) // 8bb: we have an arpeggio (0xy) running, set period back + { + ch->outPeriod = ch->realPeriod; + ch->status |= IS_Period; + } + } + else + { + // 8bb: if we have a vibrato (4xy/6xy) on previous row (ch) that ends at current row (p), set period back + if ((ch->effTyp == 4 || ch->effTyp == 6) && (p->effTyp != 4 && p->effTyp != 6)) + { + ch->outPeriod = ch->realPeriod; + ch->status |= IS_Period; + } + } + + ch->effTyp = p->effTyp; + ch->eff = p->eff; + ch->tonTyp = (p->instr << 8) | p->ton; + + // 8bb: 'inst' var is used for later if-checks + uint8_t inst = p->instr; + if (inst > 0) + { + if (inst <= 128) + ch->instrNr = inst; + else + inst = 0; + } + + bool checkEfx = true; + if (p->effTyp == 0x0E) // 8bb: check for EDx (Note Delay) and E90 (Retrigger Note) + { + if (p->eff >= 0xD1 && p->eff <= 0xDF) // 8bb: ED1..EDF (Note Delay) + return; + else if (p->eff == 0x90) // 8bb: E90 (Retrigger Note) + checkEfx = false; + } + + if (checkEfx) + { + if ((ch->volKolVol & 0xF0) == 0xF0) // 8bb: Portamento (volume column) + { + const uint8_t volKolParam = ch->volKolVol & 0x0F; + if (volKolParam > 0) + ch->portaSpeed = volKolParam << 6; + + fixTonePorta(ch, p, inst); + checkEffects(ch); + return; + } + + if (p->effTyp == 3 || p->effTyp == 5) // 8bb: Portamento (3xx/5xx) + { + if (p->effTyp != 5 && p->eff != 0) + ch->portaSpeed = p->eff << 2; + + fixTonePorta(ch, p, inst); + checkEffects(ch); + return; + } + + if (p->effTyp == 0x14 && p->eff == 0) // 8bb: K00 (Key Off - only handle tick 0 here) + { + keyOff(ch); + + if (inst) + retrigVolume(ch); + + checkEffects(ch); + return; + } + + if (p->ton == 0) + { + if (inst > 0) + { + retrigVolume(ch); + retrigEnvelopeVibrato(ch); + } + + checkEffects(ch); + return; + } + } + + if (p->ton == NOTE_KEYOFF) + keyOff(ch); + else + startTone(p->ton, p->effTyp, p->eff, ch); + + if (inst > 0) + { + retrigVolume(ch); + if (p->ton != NOTE_KEYOFF) + retrigEnvelopeVibrato(ch); + } + + checkEffects(ch); +} + +static void fixaEnvelopeVibrato(stmTyp *ch) +{ + bool envInterpolateFlag, envDidInterpolate; + uint8_t envPos; + int16_t autoVibVal; + uint16_t autoVibAmp, envVal; + uint32_t vol; + + instrTyp *ins = ch->instrSeg; + + // *** FADEOUT *** + if (!ch->envSustainActive) + { + ch->status |= IS_Vol; + + if (ch->fadeOutAmp >= ch->fadeOutSpeed) + { + ch->fadeOutAmp -= ch->fadeOutSpeed; + } + else + { + ch->fadeOutAmp = 0; + ch->fadeOutSpeed = 0; + } + } + + if (!ch->mute) + { + // *** VOLUME ENVELOPE *** + envVal = 0; + if (ins->envVTyp & ENV_ENABLED) + { + envDidInterpolate = false; + envPos = ch->envVPos; + + if (++ch->envVCnt == ins->envVP[envPos][0]) + { + ch->envVAmp = ins->envVP[envPos][1] << 8; + + envPos++; + if (ins->envVTyp & ENV_LOOP) + { + envPos--; + + if (envPos == ins->envVRepE) + { + if (!(ins->envVTyp & ENV_SUSTAIN) || envPos != ins->envVSust || ch->envSustainActive) + { + envPos = ins->envVRepS; + + ch->envVCnt = ins->envVP[envPos][0]; + ch->envVAmp = ins->envVP[envPos][1] << 8; + } + } + + envPos++; + } + + if (envPos < ins->envVPAnt) + { + envInterpolateFlag = true; + if ((ins->envVTyp & ENV_SUSTAIN) && ch->envSustainActive) + { + if (envPos-1 == ins->envVSust) + { + envPos--; + ch->envVIPValue = 0; + envInterpolateFlag = false; + } + } + + if (envInterpolateFlag) + { + ch->envVPos = envPos; + + ch->envVIPValue = 0; + if (ins->envVP[envPos][0] > ins->envVP[envPos-1][0]) + { + ch->envVIPValue = (ins->envVP[envPos][1] - ins->envVP[envPos-1][1]) << 8; + ch->envVIPValue /= (ins->envVP[envPos][0] - ins->envVP[envPos-1][0]); + + envVal = ch->envVAmp; + envDidInterpolate = true; + } + } + } + else + { + ch->envVIPValue = 0; + } + } + + if (!envDidInterpolate) + { + ch->envVAmp += ch->envVIPValue; + + envVal = ch->envVAmp; + if (envVal > 64*256) + { + if (envVal > 128*256) + envVal = 64*256; + else + envVal = 0; + + ch->envVIPValue = 0; + } + } + + envVal >>= 8; + + vol = (envVal * ch->outVol * ch->fadeOutAmp) >> (16+2); + vol = (vol * song.globVol) >> 7; + + ch->status |= IS_Vol; // 8bb: this updates vol on every tick (because vol envelope is enabled) + } + else + { + vol = ((ch->outVol << 4) * ch->fadeOutAmp) >> 16; + vol = (vol * song.globVol) >> 7; + } + + ch->finalVol = (uint16_t)vol; // 0..256 + } + else + { + ch->finalVol = 0; + } + + // *** PANNING ENVELOPE *** + + envVal = 0; + if (ins->envPTyp & ENV_ENABLED) + { + envDidInterpolate = false; + envPos = ch->envPPos; + + if (++ch->envPCnt == ins->envPP[envPos][0]) + { + ch->envPAmp = ins->envPP[envPos][1] << 8; + + envPos++; + if (ins->envPTyp & ENV_LOOP) + { + envPos--; + + if (envPos == ins->envPRepE) + { + if (!(ins->envPTyp & ENV_SUSTAIN) || envPos != ins->envPSust || ch->envSustainActive) + { + envPos = ins->envPRepS; + + ch->envPCnt = ins->envPP[envPos][0]; + ch->envPAmp = ins->envPP[envPos][1] << 8; + } + } + + envPos++; + } + + if (envPos < ins->envPPAnt) + { + envInterpolateFlag = true; + if ((ins->envPTyp & ENV_SUSTAIN) && ch->envSustainActive) + { + if (envPos-1 == ins->envPSust) + { + envPos--; + ch->envPIPValue = 0; + envInterpolateFlag = false; + } + } + + if (envInterpolateFlag) + { + ch->envPPos = envPos; + + ch->envPIPValue = 0; + if (ins->envPP[envPos][0] > ins->envPP[envPos-1][0]) + { + ch->envPIPValue = (ins->envPP[envPos][1] - ins->envPP[envPos-1][1]) << 8; + ch->envPIPValue /= (ins->envPP[envPos][0] - ins->envPP[envPos-1][0]); + + envVal = ch->envPAmp; + envDidInterpolate = true; + } + } + } + else + { + ch->envPIPValue = 0; + } + } + + if (!envDidInterpolate) + { + ch->envPAmp += ch->envPIPValue; + + envVal = ch->envPAmp; + if (envVal > 64*256) + { + if (envVal > 128*256) + envVal = 64*256; + else + envVal = 0; + + ch->envPIPValue = 0; + } + } + + int16_t panTmp = ch->outPan - 128; + if (panTmp > 0) + panTmp = 0 - panTmp; + panTmp += 128; + + panTmp <<= 3; + envVal -= 32*256; + + ch->finalPan = ch->outPan + (uint8_t)(((int16_t)envVal * panTmp) >> 16); + ch->status |= IS_Pan; + } + else + { + ch->finalPan = ch->outPan; + } + + // *** AUTO VIBRATO *** + if (ins->vibDepth > 0) + { + if (ch->eVibSweep > 0) + { + autoVibAmp = ch->eVibSweep; + if (ch->envSustainActive) + { + autoVibAmp += ch->eVibAmp; + if ((autoVibAmp >> 8) > ins->vibDepth) + { + autoVibAmp = ins->vibDepth << 8; + ch->eVibSweep = 0; + } + + ch->eVibAmp = autoVibAmp; + } + } + else + { + autoVibAmp = ch->eVibAmp; + } + + ch->eVibPos += ins->vibRate; + + if (ins->vibTyp == 1) autoVibVal = (ch->eVibPos > 127) ? 64 : -64; // square + else if (ins->vibTyp == 2) autoVibVal = (((ch->eVibPos >> 1) + 64) & 127) - 64; // ramp up + else if (ins->vibTyp == 3) autoVibVal = ((-(ch->eVibPos >> 1) + 64) & 127) - 64; // ramp down + else autoVibVal = vibSineTab[ch->eVibPos]; // sine + + autoVibVal <<= 2; + uint16_t tmpPeriod = (autoVibVal * (int16_t)autoVibAmp) >> 16; + + tmpPeriod += ch->outPeriod; + if (tmpPeriod >= MAX_FRQ) + tmpPeriod = 0; // 8bb: yes, FT2 does this (!) + + ch->finalPeriod = tmpPeriod; + ch->status |= IS_Period; + } + else + { + ch->finalPeriod = ch->outPeriod; + } +} + +// 8bb: converts period to note number, for arpeggio and portamento (in semitone-slide mode) +static uint16_t relocateTon(uint16_t period, uint8_t arpNote, stmTyp *ch) +{ + int32_t tmpPeriod; + + const int32_t fineTune = ((int8_t)ch->fineTune >> 3) + 16; + + /* 8bb: FT2 bug, should've been 10*12*16. Notes above B-7 (95) will have issues. + ** You can only achieve such high notes by having a high relative note value + ** in the sample. + */ + int32_t hiPeriod = 8*12*16; + + int32_t loPeriod = 0; + + for (int32_t i = 0; i < 8; i++) + { + tmpPeriod = (((loPeriod + hiPeriod) >> 1) & ~15) + fineTune; + + int32_t lookUp = tmpPeriod - 8; + if (lookUp < 0) + lookUp = 0; // 8bb: safety fix (C-0 w/ ftune <= -65). This buggy read seems to return 0 in FT2 (TODO: verify) + + if (period >= note2Period[lookUp]) + hiPeriod = (tmpPeriod - fineTune) & ~15; + else + loPeriod = (tmpPeriod - fineTune) & ~15; + } + + tmpPeriod = loPeriod + fineTune + (arpNote << 4); + if (tmpPeriod >= (8*12*16+15)-1) // 8bb: FT2 bug, should've been 10*12*16+16 (also notice the +2 difference) + tmpPeriod = (8*12*16+16)-1; + + return note2Period[tmpPeriod]; +} + +static void vibrato2(stmTyp *ch) +{ + uint8_t tmpVib = (ch->vibPos >> 2) & 0x1F; + + switch (ch->waveCtrl & 3) + { + // 0: sine + case 0: tmpVib = vibTab[tmpVib]; break; + + // 1: ramp + case 1: + { + tmpVib <<= 3; + if ((int8_t)ch->vibPos < 0) + tmpVib = ~tmpVib; + } + break; + + // 2/3: square + default: tmpVib = 255; break; + } + + tmpVib = (tmpVib * ch->vibDepth) >> 5; + + if ((int8_t)ch->vibPos < 0) + ch->outPeriod = ch->realPeriod - tmpVib; + else + ch->outPeriod = ch->realPeriod + tmpVib; + + ch->status |= IS_Period; + ch->vibPos += ch->vibSpeed; +} + +static void arp(stmTyp *ch, uint8_t param) +{ + /* 8bb: The original arpTab table only supports 16 ticks, so it can and will overflow. + ** I have added overflown values to the table so that we can handle up to 256 ticks. + ** The added overflow entries are accurate to the overflow-read in FT2.08/FT2.09. + */ + const uint8_t tick = arpTab[song.timer & 0xFF]; + + if (tick == 0) + { + ch->outPeriod = ch->realPeriod; + } + else + { + const uint8_t note = (tick == 1) ? (param >> 4) : (param & 0x0F); + ch->outPeriod = relocateTon(ch->realPeriod, note, ch); + } + + ch->status |= IS_Period; +} + +static void portaUp(stmTyp *ch, uint8_t param) +{ + if (param == 0) + param = ch->portaUpSpeed; + + ch->portaUpSpeed = param; + + ch->realPeriod -= param << 2; + if ((int16_t)ch->realPeriod < 1) + ch->realPeriod = 1; + + ch->outPeriod = ch->realPeriod; + ch->status |= IS_Period; +} + +static void portaDown(stmTyp *ch, uint8_t param) +{ + if (param == 0) + param = ch->portaDownSpeed; + + ch->portaDownSpeed = param; + + ch->realPeriod += param << 2; + if ((int16_t)ch->realPeriod > MAX_FRQ-1) // 8bb: FT2 bug, should've been unsigned comparison! + ch->realPeriod = MAX_FRQ-1; + + ch->outPeriod = ch->realPeriod; + ch->status |= IS_Period; +} + +static void tonePorta(stmTyp *ch, uint8_t param) // 8bb: param is a placeholder (not used) +{ + if (ch->portaDir == 0) + return; + + if (ch->portaDir > 1) + { + ch->realPeriod -= ch->portaSpeed; + if ((int16_t)ch->realPeriod <= (int16_t)ch->wantPeriod) + { + ch->portaDir = 1; + ch->realPeriod = ch->wantPeriod; + } + } + else + { + ch->realPeriod += ch->portaSpeed; + if (ch->realPeriod >= ch->wantPeriod) + { + ch->portaDir = 1; + ch->realPeriod = ch->wantPeriod; + } + } + + if (ch->glissFunk) // 8bb: semitone-slide flag + ch->outPeriod = relocateTon(ch->realPeriod, 0, ch); + else + ch->outPeriod = ch->realPeriod; + + ch->status |= IS_Period; + + (void)param; +} + +static void vibrato(stmTyp *ch, uint8_t param) +{ + uint8_t tmp8; + + if (ch->eff > 0) + { + tmp8 = param & 0x0F; + if (tmp8 > 0) + ch->vibDepth = tmp8; + + tmp8 = (param & 0xF0) >> 2; + if (tmp8 > 0) + ch->vibSpeed = tmp8; + } + + vibrato2(ch); +} + +static void tonePlusVol(stmTyp *ch, uint8_t param) +{ + tonePorta(ch, 0); // 8bb: the last parameter is not used in tonePorta() + volume(ch, param); + + (void)param; +} + +static void vibratoPlusVol(stmTyp *ch, uint8_t param) +{ + vibrato2(ch); + volume(ch, param); + + (void)param; +} + +static void tremolo(stmTyp *ch, uint8_t param) +{ + uint8_t tmp8; + int16_t tremVol; + + const uint8_t tmpEff = param; + if (tmpEff > 0) + { + tmp8 = tmpEff & 0x0F; + if (tmp8 > 0) + ch->tremDepth = tmp8; + + tmp8 = (tmpEff & 0xF0) >> 2; + if (tmp8 > 0) + ch->tremSpeed = tmp8; + } + + uint8_t tmpTrem = (ch->tremPos >> 2) & 0x1F; + switch ((ch->waveCtrl >> 4) & 3) + { + // 0: sine + case 0: tmpTrem = vibTab[tmpTrem]; break; + + // 1: ramp + case 1: + { + tmpTrem <<= 3; + if ((int8_t)ch->vibPos < 0) // 8bb: FT2 bug, should've been ch->tremPos + tmpTrem = ~tmpTrem; + } + break; + + // 2/3: square + default: tmpTrem = 255; break; + } + tmpTrem = (tmpTrem * ch->tremDepth) >> 6; + + if ((int8_t)ch->tremPos < 0) + { + tremVol = ch->realVol - tmpTrem; + if (tremVol < 0) + tremVol = 0; + } + else + { + tremVol = ch->realVol + tmpTrem; + if (tremVol > 64) + tremVol = 64; + } + + ch->outVol = (uint8_t)tremVol; + ch->status |= IS_Vol; + ch->tremPos += ch->tremSpeed; +} + +static void volume(stmTyp *ch, uint8_t param) // 8bb: volume slide +{ + if (param == 0) + param = ch->volSlideSpeed; + + ch->volSlideSpeed = param; + + uint8_t newVol = ch->realVol; + if ((param & 0xF0) == 0) + { + newVol -= param; + if ((int8_t)newVol < 0) + newVol = 0; + } + else + { + param >>= 4; + + newVol += param; + if (newVol > 64) + newVol = 64; + } + + ch->outVol = ch->realVol = newVol; + ch->status |= IS_Vol; +} + +static void globalVolSlide(stmTyp *ch, uint8_t param) +{ + if (param == 0) + param = ch->globVolSlideSpeed; + + ch->globVolSlideSpeed = param; + + uint8_t newVol = (uint8_t)song.globVol; + if ((param & 0xF0) == 0) + { + newVol -= param; + if ((int8_t)newVol < 0) + newVol = 0; + } + else + { + param >>= 4; + + newVol += param; + if (newVol > 64) + newVol = 64; + } + + song.globVol = newVol; + + stmTyp *c = stm; + for (int32_t i = 0; i < song.antChn; i++, c++) // 8bb: this updates the volume for all voices + c->status |= IS_Vol; +} + +static void keyOffCmd(stmTyp *ch, uint8_t param) +{ + if ((uint8_t)(song.tempo-song.timer) == (param & 31)) + keyOff(ch); +} + +static void panningSlide(stmTyp *ch, uint8_t param) +{ + if (param == 0) + param = ch->panningSlideSpeed; + + ch->panningSlideSpeed = param; + + int16_t newPan = (int16_t)ch->outPan; + if ((param & 0xF0) == 0) + { + newPan -= param; + if (newPan < 0) + newPan = 0; + } + else + { + param >>= 4; + + newPan += param; + if (newPan > 255) + newPan = 255; + } + + ch->outPan = (uint8_t)newPan; + ch->status |= IS_Pan; +} + +static void tremor(stmTyp *ch, uint8_t param) +{ + if (param == 0) + param = ch->tremorSave; + + ch->tremorSave = param; + + uint8_t tremorSign = ch->tremorPos & 0x80; + uint8_t tremorData = ch->tremorPos & 0x7F; + + tremorData--; + if ((int8_t)tremorData < 0) + { + if (tremorSign == 0x80) + { + tremorSign = 0x00; + tremorData = param & 0x0F; + } + else + { + tremorSign = 0x80; + tremorData = param >> 4; + } + } + + ch->tremorPos = tremorSign | tremorData; + ch->outVol = (tremorSign == 0x80) ? ch->realVol : 0; + ch->status |= IS_Vol + IS_QuickVol; +} + +static void retrigNote(stmTyp *ch, uint8_t param) +{ + if (param == 0) // 8bb: E9x with a param of zero is handled in getNewNote() + return; + + if ((song.tempo-song.timer) % param == 0) + { + startTone(0, 0, 0, ch); + retrigEnvelopeVibrato(ch); + } +} + +static void noteCut(stmTyp *ch, uint8_t param) +{ + if ((uint8_t)(song.tempo-song.timer) == param) + { + ch->outVol = ch->realVol = 0; + ch->status |= IS_Vol + IS_QuickVol; + } +} + +static void noteDelay(stmTyp *ch, uint8_t param) +{ + if ((uint8_t)(song.tempo-song.timer) == param) + { + startTone(ch->tonTyp & 0xFF, 0, 0, ch); + + if ((ch->tonTyp & 0xFF00) > 0) // 8bb: do we have an instrument number? + retrigVolume(ch); + + retrigEnvelopeVibrato(ch); + + if (ch->volKolVol >= 0x10 && ch->volKolVol <= 0x50) // 8bb: Set Volume (volume column) + { + ch->outVol = ch->volKolVol - 16; + ch->realVol = ch->outVol; + } + else if (ch->volKolVol >= 0xC0 && ch->volKolVol <= 0xCF) // 8bb: Set Panning (volume column) + { + ch->outPan = (ch->volKolVol & 0x0F) << 4; + } + } +} + +static const efxRoutine EJumpTab_TickNonZero[16] = +{ + dummy, // 0 + dummy, // 1 + dummy, // 2 + dummy, // 3 + dummy, // 4 + dummy, // 5 + dummy, // 6 + dummy, // 7 + dummy, // 8 + retrigNote, // 9 + dummy, // A + dummy, // B + noteCut, // C + noteDelay, // D + dummy, // E + dummy // F +}; + +static void E_Effects_TickNonZero(stmTyp *ch, uint8_t param) +{ + EJumpTab_TickNonZero[param >> 4](ch, param & 0xF); +} + +static const efxRoutine JumpTab_TickNonZero[36] = +{ + arp, // 0 + portaUp, // 1 + portaDown, // 2 + tonePorta, // 3 + vibrato, // 4 + tonePlusVol, // 5 + vibratoPlusVol, // 6 + tremolo, // 7 + dummy, // 8 + dummy, // 9 + volume, // A + dummy, // B + dummy, // C + dummy, // D + E_Effects_TickNonZero, // E + dummy, // F + dummy, // G + globalVolSlide, // H + dummy, // I + dummy, // J + keyOffCmd, // K + dummy, // L + dummy, // M + dummy, // N + dummy, // O + panningSlide, // P + dummy, // Q + doMultiRetrig, // R + dummy, // S + tremor, // T + dummy, // U + dummy, // V + dummy, // W + dummy, // X + dummy, // Y + dummy // Z +}; + +static void doEffects(stmTyp *ch) // tick>0 effect handling +{ + const uint8_t volKolEfx = ch->volKolVol >> 4; + if (volKolEfx > 0) + VJumpTab_TickNonZero[volKolEfx](ch); + + if ((ch->eff == 0 && ch->effTyp == 0) || ch->effTyp > 35) + return; + + JumpTab_TickNonZero[ch->effTyp](ch, ch->eff); +} + +static void getNextPos(void) +{ + song.pattPos++; + + if (song.pattDelTime > 0) + { + song.pattDelTime2 = song.pattDelTime; + song.pattDelTime = 0; + } + + if (song.pattDelTime2 > 0) + { + song.pattDelTime2--; + if (song.pattDelTime2 > 0) + song.pattPos--; + } + + if (song.pBreakFlag) + { + song.pBreakFlag = false; + song.pattPos = song.pBreakPos; + } + + if (song.pattPos >= song.pattLen || song.posJumpFlag) + { + song.pattPos = song.pBreakPos; + song.pBreakPos = 0; + song.posJumpFlag = false; + + song.songPos++; + if (song.songPos >= song.len) + song.songPos = song.repS; + + song.pattNr = song.songTab[(uint8_t)song.songPos]; + song.pattLen = pattLens[(uint8_t)song.pattNr]; + } +} + +void mainPlayer(void) +{ + if (musicPaused) + return; + + bool tickZero = false; + + song.timer--; + if (song.timer == 0) + { + song.timer = song.tempo; + tickZero = true; + } + + const bool readNewNote = tickZero && (song.pattDelTime2 == 0); + if (readNewNote) + { + const tonTyp *pattPtr = nilPatternLine; + if (patt[song.pattNr] != NULL) + pattPtr = &patt[song.pattNr][song.pattPos * song.antChn]; + + stmTyp *c = stm; + for (uint8_t i = 0; i < song.antChn; i++, c++, pattPtr++) + { + PMPTmpActiveChannel = i; // 8bb: for P_StartTone() + getNewNote(c, pattPtr); + fixaEnvelopeVibrato(c); + } + } + else + { + stmTyp *c = stm; + for (uint8_t i = 0; i < song.antChn; i++, c++) + { + PMPTmpActiveChannel = i; // 8bb: for P_StartTone() + doEffects(c); + fixaEnvelopeVibrato(c); + } + } + + if (song.timer == 1) + getNextPos(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/pmp_main.h Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,7 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +void mainPlayer(void); +uint32_t getFrequenceValue(uint16_t period);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/pmp_mix.c Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,381 @@ +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include "pmplay.h" +#include "pmp_main.h" +#include "snd_masm.h" +#include "tables.h" + +// fast 32-bit -> 16-bit clamp +#define CLAMP16(i) if ((int16_t)(i) != i) i = 0x7FFF ^ (i >> 31) + +static bool dump_Flag; +static int32_t oldReplayRate; + +// globalized +int16_t chnReloc[32]; +int32_t *CDA_MixBuffer = NULL; +CIType CI[32 * 2]; +// ------------ + +static void mix_UpdateChannel(int32_t nr, WaveChannelInfoType *WCI); + +void P_SetSpeed(uint16_t bpm) +{ + // 8bb: added this + if (bpm == 0) + bpm = 125; + + speedVal = ((realReplayRate + realReplayRate) + (realReplayRate >> 1)) / bpm; // 8bb: same as doing "((realReplayRate * 5) / 2) / bpm" +} + +void P_StartTone(sampleTyp *s, int32_t smpStartPos) +{ + WaveChannelInfoType WCI; + + WCI.SStartPos = smpStartPos; + WCI.SBase = s->pek; + WCI.SLen = s->len; + WCI.SRepS = s->repS; + WCI.SRepL = s->repL; + WCI.SType = s->typ; + WCI.Status = Status_StartTone+Status_StopTone; + + mix_UpdateChannel(PMPTmpActiveChannel, &WCI); +} + +// 8bb: added these two +bool mix_Init(int32_t audioBufferSize) +{ + CDA_MixBuffer = (int32_t *)malloc(audioBufferSize * 2 * sizeof (int32_t)); + if (CDA_MixBuffer == NULL) + return false; + + PMPLeft = 0; + return true; +} + +void mix_Free(void) +{ + if (CDA_MixBuffer != NULL) + { + free(CDA_MixBuffer); + CDA_MixBuffer = NULL; + } +} +// -------------------- + +static void updateVolume(CIType *v, int32_t volIPLen) +{ + const uint32_t vol = v->SVol * CDA_Amp; + + v->SLVol1 = (vol * panningTab[256-v->SPan]) >> (32-28); + v->SRVol1 = (vol * panningTab[ v->SPan]) >> (32-28); + + if (volumeRampingFlag) + { + v->SLVolIP = (v->SLVol1 - v->SLVol2) / volIPLen; + v->SRVolIP = (v->SRVol1 - v->SRVol2) / volIPLen; + v->SVolIPLen = volIPLen; + } +} + +static void mix_UpdateChannel(int32_t nr, WaveChannelInfoType *WCI) +{ + CIType *v = &CI[chnReloc[nr]]; + const uint8_t status = WCI->Status; + + if (status & Status_StopTone) + { + if (volumeRampingFlag) + { + // 8bb: fade out current voice + v->SType |= SType_Fadeout; + v->SVol = 0; + updateVolume(v, quickVolSizeVal); + + // 8bb: swap current voice with neighbor + chnReloc[nr] ^= 1; + v = &CI[chnReloc[nr]]; + } + + v->SType = SType_Off; + } + + if (status & Status_SetPan) + v->SPan = (uint8_t)WCI->SPan; + + if (status & Status_SetVol) + { + uint16_t vol = WCI->SVol; + if (vol > 0) vol--; // 8bb: 0..256 -> 0..255 ( FT2 does this to prevent mul overflow in updateVolume() ) + v->SVol = (uint8_t)vol; + } + + if (status & (Status_SetVol+Status_SetPan)) + updateVolume(v, (status & Status_QuickVol) ? quickVolSizeVal : speedVal); + + if (status & Status_SetFrq) + v->SFrq = WCI->SFrq; + + if (status & Status_StartTone) + { + int32_t len; + + uint8_t type = WCI->SType; + const bool sample16Bit = (type >> 4) & 1; + + if (type & (SType_Fwd+SType_Rev)) + { + int32_t repL = WCI->SRepL; + int32_t repS = WCI->SRepS; + + if (sample16Bit) + { + repL >>= 1; + repS >>= 1; + + v->SRevBase = (int16_t *)WCI->SBase + (repS+repS+repL); + } + else + { + v->SRevBase = (int8_t *)WCI->SBase + (repS+repS+repL); + } + + v->SRepL = repL; + v->SRepS = repS; + + len = repS + repL; + } + else + { + type &= ~(SType_Fwd+SType_Rev); // 8bb: keep loop flags only + + len = WCI->SLen; + if (sample16Bit) + len >>= 1; + + if (len == 0) + return; + } + + // 8bb: overflown 9xx (set sample offset), cut voice (voice got ended earlier in "if (status & Status_StopTone)") + if (WCI->SStartPos >= len) + return; + + v->SLen = len; + v->SPos = WCI->SStartPos; + v->SPosDec = 0; + v->SBase = WCI->SBase; + v->SMixType = (sample16Bit * 4) + (volumeRampingFlag * 2) + interpolationFlag; + v->SType = type; + } +} + +static void mix_UpdateChannelVolPanFrq(void) +{ + WaveChannelInfoType WCI; + + stmTyp *ch = stm; + for (int32_t i = 0; i < song.antChn; i++, ch++) + { + uint8_t newStatus = 0; + + const uint8_t status = ch->status; + ch->status = 0; + + if (status == 0) + continue; + + if (status & IS_Vol) + { + WCI.SVol = ch->finalVol; + newStatus |= Status_SetVol; + } + + if (status & IS_QuickVol) + newStatus |= Status_QuickVol; + + if (status & IS_Pan) + { + WCI.SPan = ch->finalPan; + newStatus |= Status_SetPan; + } + + if (status & IS_Period) + { + WCI.SFrq = getFrequenceValue(ch->finalPeriod); + newStatus |= Status_SetFrq; + } + + WCI.Status = newStatus; + mix_UpdateChannel(i, &WCI); + } +} + +void mix_ClearChannels(void) // 8bb: rewritten to handle all voices instead of song.antChn +{ + lockMixer(); + + memset(CI, 0, sizeof (CI)); + + CIType *v = CI; + for (int16_t i = 0; i < 32*2; i++, v++) + { + v->SPan = 128; + v->SType = SType_Off; + } + + for (int16_t i = 0; i < 32; i++) + chnReloc[i] = i+i; + + unlockMixer(); +} + +static void mix_SaveIPVolumes(void) +{ + CIType *v = CI; + for (int32_t i = 0; i < song.antChn*2; i++, v++) + { + // 8bb: this cuts any active fade-out voices (volume ramping) + if (v->SType & SType_Fadeout) + v->SType = SType_Off; + + v->SLVol2 = v->SLVol1; + v->SRVol2 = v->SRVol1; + v->SVolIPLen = 0; + } +} + +void mix_UpdateBuffer(int16_t *buffer, int32_t numSamples) +{ + if (numSamples <= 0) + return; + + if (musicPaused || WAVDump_Flag) // silence output + { + memset(buffer, 0, numSamples * (2 * sizeof (int16_t))); + return; + } + + memset(CDA_MixBuffer, 0, numSamples * (2 * sizeof (int32_t))); + + int32_t c = 0; + int32_t a = numSamples; + + while (a > 0) + { + if (PMPLeft == 0) + { + mix_SaveIPVolumes(); + mainPlayer(); + mix_UpdateChannelVolPanFrq(); + PMPLeft = speedVal; + } + + int32_t b = a; + if (b > PMPLeft) + b = PMPLeft; + + CIType *v = CI; + for (int32_t i = 0; i < song.antChn*2; i++, v++) + PMPMix32Proc(v, b, c); + + c += b; + a -= b; + PMPLeft -= b; + } + + numSamples *= 2; // 8bb: stereo + + /* 8bb: Done a bit differently since we don't use a + ** Sound Blaster with its master volume setting. + ** Instead we change the amplitude here. + */ + + if (masterVol == 256) // 8bb: max master volume, no need to change amp + { + for (int32_t i = 0; i < numSamples; i++) + { + int32_t out32 = CDA_MixBuffer[i] >> 8; + CLAMP16(out32); + buffer[i] = (int16_t)out32; + } + } + else + { + for (int32_t i = 0; i < numSamples; i++) + { + int32_t out32 = CDA_MixBuffer[i] >> 8; + CLAMP16(out32); + out32 = (out32 * masterVol) >> 8; + buffer[i] = (int16_t)out32; + } + } +} + +bool dump_Init(int32_t frq, int32_t amp, int16_t songPos) +{ + setPos(songPos, 0); + + oldReplayRate = realReplayRate; + + realReplayRate = frq; + updateReplayRate(); + CDA_Amp = 8 * amp; + + mix_ClearChannels(); + stopVoices(); + song.globVol = 64; + speedVal = (frq*5 / 2) / song.speed; + quickVolSizeVal = frq / 200; + + dump_Flag = false; + return true; +} + +void dump_Close(void) +{ + stopVoices(); + realReplayRate = oldReplayRate; + updateReplayRate(); +} + +bool dump_EndOfTune(int32_t endSongPos) +{ + bool returnValue = (dump_Flag && song.pattPos == 0 && song.timer == 1) || (song.tempo == 0); + + // 8bb: FT2 bugfix for EEx (pattern delay) on first row of a pattern + if (song.pattDelTime2 > 0) + returnValue = false; + + if (song.songPos == endSongPos && song.pattPos == 0 && song.timer == 1) + dump_Flag = true; + + return returnValue; +} + +int32_t dump_GetFrame(int16_t *p) // 8bb: returns bytes mixed to 16-bit stereo buffer +{ + mix_SaveIPVolumes(); + mainPlayer(); + mix_UpdateChannelVolPanFrq(); + + memset(CDA_MixBuffer, 0, speedVal * (2 * sizeof (int32_t))); + + CIType *v = CI; + for (int32_t i = 0; i < song.antChn*2; i++, v++) + PMPMix32Proc(v, speedVal, 0); + + const int32_t numSamples = speedVal * 2; // 8bb: *2 for stereo + for (int32_t i = 0; i < numSamples; i++) + { + int32_t out32 = CDA_MixBuffer[i] >> 8; + CLAMP16(out32); + p[i] = (int16_t)out32; + } + + return speedVal * (2 * sizeof (int16_t)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/pmp_mix.h Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,59 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "pmplay.h" + +enum +{ + Status_SetVol = 1, + Status_SetPan = 2, + Status_SetFrq = 4, + Status_StartTone = 8, + Status_StopTone = 16, + Status_QuickVol = 32, + + SType_Fwd = 1, + SType_Rev = 2, + SType_RevDir = 4, + SType_Off = 8, + SType_16 = 16, + SType_Fadeout = 32 +}; + +typedef struct +{ + const void *SBase, *SRevBase; + uint8_t SType, SPan, SVol; + int32_t SLVol1, SRVol1, SLVol2, SRVol2, SLVolIP, SRVolIP, SVolIPLen; + int32_t SLen, SRepS, SRepL, SPos, SMixType; + uint32_t SPosDec, SFrq; +} CIType; + +typedef struct +{ + const void *SBase; + uint8_t Status, SType; + int16_t SVol, SPan; + int32_t SFrq, SLen, SRepS, SRepL, SStartPos; +} WaveChannelInfoType; + +extern int16_t chnReloc[32]; +extern int32_t *CDA_MixBuffer; +extern CIType CI[32 * 2]; + +void P_SetSpeed(uint16_t bpm); +void P_StartTone(sampleTyp *s, int32_t smpStartPos); + +// 8bb: added these two +bool mix_Init(int32_t audioBufferSize); +void mix_Free(void); +// ------------------- + +void mix_ClearChannels(void); +void mix_UpdateBuffer(int16_t *buffer, int32_t numSamples); + +bool dump_Init(int32_t frq, int32_t amp, int16_t songPos); +void dump_Close(void); +bool dump_EndOfTune(int32_t endSongPos); +int32_t dump_GetFrame(int16_t *p);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/pmplay.c Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,1414 @@ +/* +** - loaders and replayer handlers - +*/ + +#define DEFAULT_AMP 4 +#define DEFAULT_MASTER_VOL 256 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <stdbool.h> +#include <math.h> +#include <assert.h> +#include "pmplay.h" +#include "pmp_mix.h" +#include "snd_masm.h" +#include "tables.h" + +#define INSTR_HEADER_SIZE 263 + +#define SWAP16(value) \ +( \ + (((uint16_t)((value) & 0x00FF)) << 8) | \ + (((uint16_t)((value) & 0xFF00)) >> 8) \ +) + +#ifdef _MSC_VER +#pragma pack(push) +#pragma pack(1) +#endif +typedef struct songHeaderTyp_t +{ + char sig[17], name[21], progName[20]; + uint16_t ver; + int32_t headerSize; + uint16_t len, repS, antChn, antPtn, antInstrs, flags, defTempo, defSpeed; + uint8_t songTab[256]; +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +songHeaderTyp; + +typedef struct modSampleTyp +{ + char name[22]; + uint16_t len; + uint8_t fine, vol; + uint16_t repS, repL; +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +modSampleTyp; + +typedef struct songMOD31HeaderTyp +{ + char name[20]; + modSampleTyp sample[31]; + uint8_t len, repS, songTab[128]; + char Sig[4]; +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +songMOD31HeaderTyp; + +typedef struct songMOD15HeaderTyp +{ + char name[20]; + modSampleTyp sample[15]; + uint8_t len, repS, songTab[128]; +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +songMOD15HeaderTyp; + +typedef struct sampleHeaderTyp_t +{ + int32_t len, repS, repL; + uint8_t vol; + int8_t fine; + uint8_t typ, pan; + int8_t relTon; + uint8_t skrap; + char name[22]; +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +sampleHeaderTyp; + +typedef struct instrHeaderTyp_t +{ + int32_t instrSize; + char name[22]; + uint8_t typ; + uint16_t antSamp; + int32_t sampleSize; + uint8_t ta[96]; + int16_t envVP[12][2], envPP[12][2]; + uint8_t envVPAnt, envPPAnt, envVSust, envVRepS, envVRepE, envPSust, envPRepS; + uint8_t envPRepE, envVTyp, envPTyp, vibTyp, vibSweep, vibDepth, vibRate; + uint16_t fadeOut; + uint8_t midiOn, midiChannel; + int16_t midiProgram, midiBend; + int8_t mute; + uint8_t reserved[15]; + sampleHeaderTyp samp[32]; +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +instrHeaderTyp; + +typedef struct patternHeaderTyp_t +{ + int32_t patternHeaderSize; + uint8_t typ; + uint16_t pattLen, dataLen; +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +patternHeaderTyp; +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +static int32_t soundBufferSize; + +// globalized +volatile bool interpolationFlag, volumeRampingFlag, moduleLoaded, musicPaused, WAVDump_Flag; +bool linearFrqTab; +volatile const uint16_t *note2Period; +uint16_t pattLens[256]; +int16_t PMPTmpActiveChannel, boostLevel = DEFAULT_AMP; +int32_t masterVol = DEFAULT_MASTER_VOL, PMPLeft = 0; +int32_t realReplayRate, quickVolSizeVal, speedVal; +uint32_t frequenceDivFactor, frequenceMulFactor, CDA_Amp = 8*DEFAULT_AMP; +tonTyp *patt[256]; +instrTyp *instr[1+128]; +songTyp song; +stmTyp stm[32]; +// ------------------ + +// 8bb: added these for loader +typedef struct +{ + uint8_t *_ptr, *_base; + bool _eof; + size_t _cnt, _bufsiz; +} MEMFILE; + +static MEMFILE *mopen(const uint8_t *src, uint32_t length); +static void mclose(MEMFILE **buf); +static size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf); +static bool meof(MEMFILE *buf); +static void mseek(MEMFILE *buf, int32_t offset, int32_t whence); +static void mrewind(MEMFILE *buf); +// -------------------------- + +static void resetMusic(void); +static void freeAllPatterns(void); +static void setFrqTab(bool linear); + +static CIType *getVoice(int32_t ch) // 8bb: added this +{ + if (ch < 0 || ch > 31) + return NULL; + + return &CI[chnReloc[ch]]; +} + +/*************************************************************************** + * ROUTINES FOR SAMPLE HANDLING ETC. * + ***************************************************************************/ + +// 8bb: modifies wrapped sample after loop/end (for branchless mixer interpolation) +static void fixSample(sampleTyp *s) +{ + if (s->pek == NULL) + return; // empty sample + + const bool sample16Bit = !!(s->typ & SAMPLE_16BIT); + uint8_t loopType = s->typ & 3; + int16_t *ptr16 = (int16_t *)s->pek; + int32_t len = s->len; + int32_t loopStart = s->repS; + int32_t loopEnd = s->repS + s->repL; + + if (sample16Bit) + { + len >>= 1; + loopStart >>= 1; + loopEnd >>= 1; + } + + if (len < 1) + return; + + /* 8bb: + ** This is the exact bit test order of which FT2 handles + ** the sample tap fix. + ** + ** This order is important for rare cases where both the + ** "forward" and "pingpong" loop bits are set at once. + ** + ** This means that if both flags are set, the mixer will + ** play the sample with pingpong looping, but the sample fix + ** is handled as if it was a forward loop. This results in + ** the wrong interpolation tap sample being written after the + ** loop end point. + */ + + if (loopType & LOOP_FORWARD) + { + if (sample16Bit) + ptr16[loopEnd] = ptr16[loopStart]; + else + s->pek[loopEnd] = s->pek[loopStart]; + + return; + } + else if (loopType & LOOP_PINGPONG) + { + if (sample16Bit) + ptr16[loopEnd] = ptr16[loopEnd-1]; + else + s->pek[loopEnd] = s->pek[loopEnd-1]; + } + else // no loop + { + if (sample16Bit) + ptr16[len] = 0; + else + s->pek[len] = 0; + } +} + +static void checkSampleRepeat(int32_t nr, int32_t nr2) +{ + instrTyp *i = instr[nr]; + if (i == NULL) + return; + + sampleTyp *s = &i->samp[nr2]; + + if (s->repS < 0) s->repS = 0; + if (s->repL < 0) s->repL = 0; + if (s->repS > s->len) s->repS = s->len; + if (s->repS+s->repL > s->len) s->repL = s->len - s->repS; +} + +static void upDateInstrs(void) +{ + for (int32_t i = 0; i <= 128; i++) + { + instrTyp *ins = instr[i]; + if (ins == NULL) + continue; + + sampleTyp *s = ins->samp; + for (int32_t j = 0; j < 16; j++, s++) + { + checkSampleRepeat(i, j); + fixSample(s); + + if (s->pek == NULL) + { + s->len = 0; + s->repS = 0; + s->repL = 0; + } + } + } +} + +static bool patternEmpty(uint16_t nr) +{ + if (patt[nr] == NULL) + return true; + + const uint8_t *scanPtr = (const uint8_t *)patt[nr]; + const int32_t scanLen = pattLens[nr] * song.antChn * sizeof (tonTyp); + + for (int32_t i = 0; i < scanLen; i++) + { + if (scanPtr[i] != 0) + return false; + } + + return true; +} + +static bool allocateInstr(uint16_t i) +{ + if (instr[i] != NULL) + return true; + + instrTyp *p = (instrTyp *)calloc(1, sizeof (instrTyp)); + if (p == NULL) + return false; + + sampleTyp *s = p->samp; + for (int32_t j = 0; j < 16; j++, s++) + { + s->pan = 128; + s->vol = 64; + } + + instr[i] = p; + return true; +} + +static void freeInstr(uint16_t nr) +{ + if (nr > 128) + return; + + instrTyp *ins = instr[nr]; + if (ins == NULL) + return; + + sampleTyp *s = ins->samp; + for (uint8_t i = 0; i < 16; i++, s++) + { + if (s->pek != NULL) + free(s->pek); + } + + free(ins); + instr[nr] = NULL; +} + +static void freeAllInstr(void) +{ + for (uint16_t i = 0; i <= 128; i++) + freeInstr(i); +} + +static void freeAllPatterns(void) // 8bb: added this one, since it's handy +{ + for (int32_t i = 0; i < 256; i++) + { + if (patt[i] != NULL) + { + free(patt[i]); + patt[i] = NULL; + } + + pattLens[i] = 64; + } +} + +static void delta2Samp(int8_t *p, uint32_t len, bool sample16Bit) +{ + if (sample16Bit) + { + len >>= 1; + + int16_t *p16 = (int16_t *)p; + + int16_t olds16 = 0; + for (uint32_t i = 0; i < len; i++) + { + const int16_t news16 = p16[i] + olds16; + p16[i] = news16; + olds16 = news16; + } + } + else + { + int8_t *p8 = (int8_t *)p; + + int8_t olds8 = 0; + for (uint32_t i = 0; i < len; i++) + { + const int8_t news8 = p8[i] + olds8; + p8[i] = news8; + olds8 = news8; + } + } +} + +static void unpackPatt(uint8_t *dst, uint16_t inn, uint16_t len, uint8_t antChn) +{ + if (dst == NULL) + return; + + const uint8_t *src = dst + inn; + const int32_t srcEnd = len * (sizeof (tonTyp) * antChn); + + int32_t srcIdx = 0; + for (int32_t i = 0; i < len; i++) + { + for (int32_t j = 0; j < antChn; j++) + { + if (srcIdx >= srcEnd) + return; // error! + + const uint8_t note = *src++; + if (note & 0x80) + { + *dst++ = (note & 0x01) ? *src++ : 0; + *dst++ = (note & 0x02) ? *src++ : 0; + *dst++ = (note & 0x04) ? *src++ : 0; + *dst++ = (note & 0x08) ? *src++ : 0; + *dst++ = (note & 0x10) ? *src++ : 0; + } + else + { + *dst++ = note; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + } + + // 8bb: added this. If note >97, remove it (prevents out-of-range read in note->sample LUT) + if (*(dst-5) > 97) + *(dst-5) = 0; + + srcIdx += sizeof (tonTyp); + } + } +} + +void freeMusic(void) +{ + stopMusic(); + freeAllInstr(); + freeAllPatterns(); + + song.tempo = 6; + song.speed = 125; + song.timer = 1; + + setFrqTab(true); + resetMusic(); +} + +void stopVoices(void) +{ + lockMixer(); + + stmTyp *ch = stm; + for (uint8_t i = 0; i < 32; i++, ch++) + { + ch->tonTyp = 0; + ch->relTonNr = 0; + ch->instrNr = 0; + ch->instrSeg = instr[0]; // 8bb: placeholder instrument + ch->status = IS_Vol; + + ch->realVol = 0; + ch->outVol = 0; + ch->oldVol = 0; + ch->finalVol = 0; + ch->oldPan = 128; + ch->outPan = 128; + ch->finalPan = 128; + ch->vibDepth = 0; + } + + unlockMixer(); +} + +static void resetMusic(void) +{ + song.timer = 1; + stopVoices(); + setPos(0, 0); +} + +void setPos(int32_t pos, int32_t row) // -1 = don't change +{ + if (pos != -1) + { + song.songPos = (int16_t)pos; + if (song.len > 0 && song.songPos >= song.len) + song.songPos = song.len - 1; + + song.pattNr = song.songTab[song.songPos]; + song.pattLen = pattLens[song.pattNr]; + } + + if (row != -1) + { + song.pattPos = (int16_t)row; + if (song.pattPos >= song.pattLen) + song.pattPos = song.pattLen - 1; + } + + song.timer = 1; +} + +/*************************************************************************** + * MODULE LOADING ROUTINES * + ***************************************************************************/ + +static bool loadInstrHeader(MEMFILE *f, uint16_t i) +{ + instrHeaderTyp ih; + + memset(&ih, 0, INSTR_HEADER_SIZE); + mread(&ih.instrSize, 4, 1, f); + if (ih.instrSize > INSTR_HEADER_SIZE) ih.instrSize = INSTR_HEADER_SIZE; + + if (ih.instrSize < 4) // 8bb: added protection + return false; + + mread(ih.name, ih.instrSize-4, 1, f); + + if (ih.antSamp > 16) + return false; + + if (ih.antSamp > 0) + { + if (!allocateInstr(i)) + return false; + + instrTyp *ins = instr[i]; + + memcpy(ins->name, ih.name, 22); + ins->name[22] = '\0'; + + // 8bb: copy instrument header elements to our instrument struct + memcpy(ins->ta, ih.ta, 96); + memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t)); + memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t)); + ins->envVPAnt = ih.envVPAnt; + ins->envPPAnt = ih.envPPAnt; + ins->envVSust = ih.envVSust; + ins->envVRepS = ih.envVRepS; + ins->envVRepE = ih.envVRepE; + ins->envPSust = ih.envPSust; + ins->envPRepS = ih.envPRepS; + ins->envPRepE = ih.envPRepE; + ins->envVTyp = ih.envVTyp; + ins->envPTyp = ih.envPTyp; + ins->vibTyp = ih.vibTyp; + ins->vibSweep = ih.vibSweep; + ins->vibDepth = ih.vibDepth; + ins->vibRate = ih.vibRate; + ins->fadeOut = ih.fadeOut; + ins->mute = (ih.mute == 1) ? true : false; // 8bb: correct logic! + ins->antSamp = ih.antSamp; + + if (mread(ih.samp, ih.antSamp * sizeof (sampleHeaderTyp), 1, f) != 1) + return false; + + sampleTyp *s = instr[i]->samp; + sampleHeaderTyp *src = ih.samp; + for (int32_t j = 0; j < ih.antSamp; j++, s++, src++) + { + memcpy(s->name, src->name, 22); + s->name[22] = '\0'; + + s->len = src->len; + s->repS = src->repS; + s->repL = src->repL; + s->vol = src->vol; + s->fine = src->fine; + s->typ = src->typ; + s->pan = src->pan; + s->relTon = src->relTon; + } + } + + return true; +} + +static bool loadInstrSample(MEMFILE *f, uint16_t i) +{ + if (instr[i] == NULL) + return true; // empty instrument + + sampleTyp *s = instr[i]->samp; + for (uint16_t j = 0; j < instr[i]->antSamp; j++, s++) + { + if (s->len > 0) + { + bool sample16Bit = !!(s->typ & SAMPLE_16BIT); + + s->pek = (int8_t *)malloc(s->len+2); // 8bb: +2 for fixed interpolation tap sample + if (s->pek == NULL) + return false; + + mread(s->pek, 1, s->len, f); + delta2Samp(s->pek, s->len, sample16Bit); + } + + checkSampleRepeat(i, j); + } + + return true; +} + +static bool loadPatterns(MEMFILE *f, uint16_t antPtn) +{ + uint8_t tmpLen; + patternHeaderTyp ph; + + for (uint16_t i = 0; i < antPtn; i++) + { + mread(&ph.patternHeaderSize, 4, 1, f); + mread(&ph.typ, 1, 1, f); + + ph.pattLen = 0; + if (song.ver == 0x0102) + { + mread(&tmpLen, 1, 1, f); + mread(&ph.dataLen, 2, 1, f); + ph.pattLen = (uint16_t)tmpLen + 1; // 8bb: +1 in v1.02 + + if (ph.patternHeaderSize > 8) + mseek(f, ph.patternHeaderSize - 8, SEEK_CUR); + } + else + { + mread(&ph.pattLen, 2, 1, f); + mread(&ph.dataLen, 2, 1, f); + + if (ph.patternHeaderSize > 9) + mseek(f, ph.patternHeaderSize - 9, SEEK_CUR); + } + + if (meof(f)) + { + mclose(&f); + return false; + } + + pattLens[i] = ph.pattLen; + if (ph.dataLen) + { + const uint16_t a = ph.pattLen * song.antChn * sizeof (tonTyp); + + patt[i] = (tonTyp *)malloc(a); + if (patt[i] == NULL) + return false; + + uint8_t *pattPtr = (uint8_t *)patt[i]; + + memset(pattPtr, 0, a); + mread(&pattPtr[a - ph.dataLen], 1, ph.dataLen, f); + unpackPatt(pattPtr, a - ph.dataLen, ph.pattLen, song.antChn); + } + + if (patternEmpty(i)) + { + if (patt[i] != NULL) + { + free(patt[i]); + patt[i] = NULL; + } + + pattLens[i] = 64; + } + } + + return true; +} + +static bool loadMusicMOD(MEMFILE *f) +{ + uint8_t ha[sizeof (songMOD31HeaderTyp)]; + songMOD31HeaderTyp *h_MOD31 = (songMOD31HeaderTyp *)ha; + songMOD15HeaderTyp *h_MOD15 = (songMOD15HeaderTyp *)ha; + + mread(ha, sizeof (ha), 1, f); + if (meof(f)) + goto loadError2; + + memcpy(song.name, h_MOD31->name, 20); + song.name[20] = '\0'; + + uint8_t j = 0; + for (uint8_t i = 1; i <= 16; i++) + { + if (memcmp(h_MOD31->Sig, MODSig[i-1], 4) == 0) + j = i + i; + } + + if (memcmp(h_MOD31->Sig, "M!K!", 4) == 0 || memcmp(h_MOD31->Sig, "FLT4", 4) == 0) + j = 4; + + if (memcmp(h_MOD31->Sig, "OCTA", 4) == 0) + j = 8; + + uint8_t typ; + if (j > 0) + { + typ = 1; + song.antChn = j; + } + else + { + typ = 2; + song.antChn = 4; + } + + int16_t ai; + if (typ == 1) + { + mseek(f, sizeof (songMOD31HeaderTyp), SEEK_SET); + song.len = h_MOD31->len; + song.repS = h_MOD31->repS; + memcpy(song.songTab, h_MOD31->songTab, 128); + ai = 31; + } + else + { + mseek(f, sizeof (songMOD15HeaderTyp), SEEK_SET); + song.len = h_MOD15->len; + song.repS = h_MOD15->repS; + memcpy(song.songTab, h_MOD15->songTab, 128); + ai = 15; + } + + song.antInstrs = ai; // 8bb: added this + + if (meof(f)) + goto loadError2; + + int32_t b = 0; + for (int32_t a = 0; a < 128; a++) + { + if (song.songTab[a] > b) + b = song.songTab[a]; + } + + uint8_t pattBuf[32 * 4 * 64]; // 8bb: max pattern size (32 channels, 64 rows) + for (uint16_t a = 0; a <= b; a++) + { + patt[a] = (tonTyp *)calloc(song.antChn * 64, sizeof (tonTyp)); + if (patt[a] == NULL) + goto loadError; + + pattLens[a] = 64; + + mread(pattBuf, 1, song.antChn * 4 * 64, f); + if (meof(f)) + goto loadError; + + // convert pattern + uint8_t *bytes = pattBuf; + tonTyp *ton = patt[a]; + for (int32_t i = 0; i < 64 * song.antChn; i++, bytes += 4, ton++) + { + const uint16_t period = ((bytes[0] & 0x0F) << 8) | bytes[1]; + for (uint8_t k = 0; k < 96; k++) + { + if (period >= amigaPeriod[k]) + { + ton->ton = k+1; + break; + } + } + + ton->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4); + ton->effTyp = bytes[2] & 0x0F; + ton->eff = bytes[3]; + + switch (ton->effTyp) + { + case 0xC: + { + if (ton->eff > 64) + ton->eff = 64; + } + break; + + case 0x1: + case 0x2: + { + if (ton->eff == 0) + ton->effTyp = 0; + } + break; + + case 0x5: + { + if (ton->eff == 0) + ton->effTyp = 3; + } + break; + + case 0x6: + { + if (ton->eff == 0) + ton->effTyp = 4; + } + break; + + case 0xA: + { + if (ton->eff == 0) + ton->effTyp = 0; + } + break; + + case 0xE: + { + const uint8_t effTyp = ton->effTyp >> 4; + const uint8_t eff = ton->effTyp & 15; + + if (eff == 0 && (effTyp == 0x1 || effTyp == 0x2 || effTyp == 0xA || effTyp == 0xB)) + { + ton->eff = 0; + ton->effTyp = 0; + } + } + break; + + default: break; + } + } + + if (patternEmpty(a)) + { + free(patt[a]); + patt[a] = NULL; + pattLens[a] = 64; + } + } + + for (uint16_t a = 1; a <= ai; a++) + { + modSampleTyp *modSmp = &h_MOD31->sample[a-1]; + + uint32_t len = 2 * SWAP16(modSmp->len); + if (len == 0) + continue; + + if (!allocateInstr(a)) + goto loadError; + + sampleTyp *xmSmp = &instr[a]->samp[0]; + + memcpy(xmSmp->name, modSmp->name, 22); + xmSmp->name[22] = '\0'; + + uint32_t repS = 2 * SWAP16(modSmp->repS); + uint32_t repL = 2 * SWAP16(modSmp->repL); + + if (repL <= 2) + { + repS = 0; + repL = 0; + } + + if (repS+repL > len) + { + if (repS >= len) + { + repS = 0; + repL = 0; + } + else + { + repL = len-repS; + } + } + + xmSmp->typ = (repL > 2) ? 1 : 0; + xmSmp->len = len; + xmSmp->vol = (modSmp->vol <= 64) ? modSmp->vol : 64; + xmSmp->fine = 8 * ((2 * ((modSmp->fine & 15) ^ 8)) - 16); + xmSmp->repL = repL; + xmSmp->repS = repS; + + xmSmp->pek = (int8_t *)malloc(len + 2); + if (xmSmp->pek == NULL) + goto loadError; + + mread(xmSmp->pek, 1, len, f); + } + + mclose(&f); + + if (song.repS > song.len) + song.repS = 0; + + resetMusic(); + upDateInstrs(); + + moduleLoaded = true; + return true; +loadError: + freeAllInstr(); + freeAllPatterns(); +loadError2: + mclose(&f); + return false; +} + +bool loadMusicFromData(const uint8_t *data, uint32_t dataLength) // .XM/.MOD/.FT +{ + uint16_t i; + songHeaderTyp h; + + freeMusic(); + setFrqTab(false); + + moduleLoaded = false; + + MEMFILE *f = mopen(data, dataLength); + if (f == NULL) + return false; + + // 8bb: instr 0 is a placeholder for empty instruments + allocateInstr(0); + instr[0]->samp[0].vol = 0; + + mread(&h, sizeof (h), 1, f); + if (meof(f)) + goto loadError2; + + if (memcmp(h.sig, "Extended Module: ", 17) != 0) + { + mrewind(f); + return loadMusicMOD(f); + } + + if (h.ver < 0x0102 || h.ver > 0x104 || h.antChn < 2 || h.antChn > 32 || (h.antChn & 1) != 0 || + h.antPtn > 256 || h.antInstrs > 128) + { + goto loadError2; + } + + mseek(f, 60+h.headerSize, SEEK_SET); + if (meof(f)) + goto loadError2; + + memcpy(song.name, h.name, 20); + song.name[20] = '\0'; + + song.len = h.len; + song.repS = h.repS; + song.antChn = (uint8_t)h.antChn; + bool linearFrequencies = !!(h.flags & LINEAR_FREQUENCIES); + setFrqTab(linearFrequencies); + memcpy(song.songTab, h.songTab, 256); + + song.antInstrs = h.antInstrs; // 8bb: added this + if (h.defSpeed == 0) h.defSpeed = 125; // 8bb: (BPM) FT2 doesn't do this, but we do it for safety + song.speed = h.defSpeed; + song.tempo = h.defTempo; + song.ver = h.ver; + + // 8bb: bugfixes... + if (song.speed < 1) song.speed = 1; + if (song.tempo < 1) song.tempo = 1; + // ---------------- + + if (song.ver < 0x0104) // old FT2 XM format + { + for (i = 1; i <= h.antInstrs; i++) + { + if (!loadInstrHeader(f, i)) + goto loadError; + } + + if (!loadPatterns(f, h.antPtn)) + goto loadError; + + for (i = 1; i <= h.antInstrs; i++) + { + if (!loadInstrSample(f, i)) + goto loadError; + } + } + else // latest FT2 XM format + { + if (!loadPatterns(f, h.antPtn)) + goto loadError; + + for (i = 1; i <= h.antInstrs; i++) + { + if (!loadInstrHeader(f, i)) + goto loadError; + + if (!loadInstrSample(f, i)) + goto loadError; + } + } + + mclose(&f); + + if (song.repS > song.len) + song.repS = 0; + + resetMusic(); + upDateInstrs(); + + moduleLoaded = true; + return true; + +loadError: + freeAllInstr(); + freeAllPatterns(); +loadError2: + mclose(&f); + return false; +} + +bool loadMusic(const char *fileName) // .XM/.MOD/.FT +{ + FILE *f = fopen(fileName, "rb"); + if (f == NULL) + return false; + + fseek(f, 0, SEEK_END); + const uint32_t fileSize = (uint32_t)ftell(f); + rewind(f); + + uint8_t *fileBuffer = (uint8_t *)malloc(fileSize); + if (fileBuffer == NULL) + { + fclose(f); + return false; + } + + if (fread(fileBuffer, 1, fileSize, f) != fileSize) + { + free(fileBuffer); + fclose(f); + return false; + } + + fclose(f); + + if (!loadMusicFromData((const uint8_t *)fileBuffer, fileSize)) + { + free(fileBuffer); + return false; + } + + free(fileBuffer); + return true; +} + +/*************************************************************************** + * PROCESS HANDLING * + ***************************************************************************/ + +bool startMusic(void) +{ + if (!moduleLoaded || song.speed == 0) + return false; + + mix_ClearChannels(); + stopVoices(); + song.globVol = 64; + + speedVal = ((realReplayRate * 5) / 2) / song.speed; + quickVolSizeVal = realReplayRate / 200; + + if (!mix_Init(soundBufferSize)) + return false; + + if (openMixer(realReplayRate, soundBufferSize)) + { + musicPaused = false; + return true; + } + + return false; +} + +void stopMusic(void) +{ + pauseMusic(); + + closeMixer(); + mix_Free(); + song.globVol = 64; + + resumeMusic(); +} + +void startPlaying(void) +{ + stopMusic(); + song.pattDelTime = song.pattDelTime2 = 0; // 8bb: added these + setPos(0, 0); + startMusic(); +} + +void stopPlaying(void) +{ + stopMusic(); + stopVoices(); +} + +void pauseMusic(void) +{ + musicPaused = true; +} + +void resumeMusic(void) +{ + musicPaused = false; +} + +// 8bb: added these three, handy +void toggleMusic(void) +{ + musicPaused ^= 1; +} + +void setInterpolation(bool on) +{ + interpolationFlag = on; + mix_ClearChannels(); +} + +void setVolumeRamping(bool on) +{ + volumeRampingFlag = on; + mix_ClearChannels(); +} + +/*************************************************************************** + * CONFIGURATION ROUTINES * + ***************************************************************************/ + +void setMasterVol(int32_t v) // 0..256 +{ + masterVol = CLAMP(v, 0, 256); + + stmTyp *ch = stm; + for (int32_t i = 0; i < 32; i++, ch++) + ch->status |= IS_Vol; +} + +void setAmp(int32_t level) // 1..32 +{ + boostLevel = (int16_t)CLAMP(level, 1, 32); + CDA_Amp = boostLevel * 8; +} + +int32_t getMasterVol(void) // 8bb: added this +{ + return masterVol; +} + +int32_t getAmp(void) // 8bb: added this +{ + return boostLevel; +} + +uint8_t getNumActiveVoices(void) // 8bb: added this +{ + uint8_t activeVoices = 0; + for (int32_t i = 0; i < song.antChn; i++) + { + CIType *v = getVoice(i); + if (!(v->SType & SType_Off) && v->SVol > 0) + activeVoices++; + } + + return activeVoices; +} + +static void setFrqTab(bool linear) +{ + linearFrqTab = linear; + note2Period = linear ? linearPeriods : amigaPeriods; +} + +void updateReplayRate(void) +{ + lockMixer(); + + // 8bb: bit-exact to FT2 + frequenceDivFactor = (uint32_t)round(65536.0*1712.0/realReplayRate*8363.0); + frequenceMulFactor = (uint32_t)round(256.0*65536.0/realReplayRate*8363.0); + + unlockMixer(); +} + +/*************************************************************************** + * INITIALIZATION ROUTINES * + ***************************************************************************/ + +bool initMusic(int32_t audioFrequency, int32_t audioBufferSize, bool interpolation, bool volumeRamping) +{ + closeMixer(); + freeMusic(); + memset(stm, 0, sizeof (stm)); + + realReplayRate = CLAMP(audioFrequency, 8000, 96000); + updateReplayRate(); + + soundBufferSize = audioBufferSize; + interpolationFlag = interpolation; + volumeRampingFlag = volumeRamping; + + song.tempo = 6; + song.speed = 125; + setFrqTab(true); + resetMusic(); + + return true; +} + +/*************************************************************************** + * WAV DUMPING ROUTINES * + ***************************************************************************/ + +static void WAV_WriteHeader(FILE *f, int32_t frq) +{ + uint16_t w; + uint32_t l; + + // 12 bytes + + const uint32_t RIFF = 0x46464952; + fwrite(&RIFF, 4, 1, f); + fseek(f, 4, SEEK_CUR); + const uint32_t WAVE = 0x45564157; + fwrite(&WAVE, 4, 1, f); + + // 24 bytes + + const uint32_t fmt = 0x20746D66; + fwrite(&fmt, 4, 1, f); + l = 16; fwrite(&l, 4, 1, f); + w = 1; fwrite(&w, 2, 1, f); + w = 2; fwrite(&w, 2, 1, f); + l = frq; fwrite(&l, 4, 1, f); + l = frq*2*2; fwrite(&l, 4, 1, f); + w = 2*2; fwrite(&w, 2, 1, f); + w = 8*2; fwrite(&w, 2, 1, f); + + // 8 bytes + + const uint32_t DATA = 0x61746164; + fwrite(&DATA, 4, 1, f); + fseek(f, 4, SEEK_CUR); +} + +static void WAV_WriteEnd(FILE *f, uint32_t size) +{ + fseek(f, 4, SEEK_SET); + uint32_t l = size+4+24+8; + fwrite(&l, 4, 1, f); + fseek(f, 12+24+4, SEEK_SET); + fwrite(&size, 4, 1, f); +} + +void WAVDump_Abort(void) // 8bb: added this +{ + WAVDump_Flag = false; +} + +bool WAVDump_Record(const char *filenameOut) +{ + FILE *fil = fopen(filenameOut, "wb"); + if (fil == NULL) + { + WAVDump_Flag = false; + return false; + } + + const int32_t WDFrequency = realReplayRate; + const int32_t WDAmp = boostLevel; + + const uint32_t maxSamplesPerTick = (WDFrequency*5 / 2) / 1; // 8bb: added this (min. BPM = 1, through hex editing) + int16_t *pBlock = (int16_t *)malloc(maxSamplesPerTick * (2 * sizeof (int16_t))); + if (pBlock == NULL) + { + fclose(fil); + WAVDump_Flag = false; + return false; + } + + WAV_WriteHeader(fil, WDFrequency); + + stopMusic(); + mix_Init(maxSamplesPerTick); + + uint16_t WDStartPos = 0; + uint16_t WDStopPos = song.len-1; + + dump_Init(WDFrequency, WDAmp, WDStartPos); + + uint32_t totSize = 0; + + WAVDump_Flag = true; + while (!dump_EndOfTune(WDStopPos)) + { + if (!WAVDump_Flag) // extra check so that external threads can force-abort render + break; + + const uint32_t size = dump_GetFrame(pBlock); + fwrite(pBlock, 1, size, fil); + totSize += size; + } + WAVDump_Flag = false; + + mix_Free(); + + WAV_WriteEnd(fil, totSize); + dump_Close(); + + stopMusic(); + fclose(fil); + + free(pBlock); + + WAVDump_Flag = false; + return true; +} + +/*************************************************************************** + * MEMORY READ ROUTINES (8bb: added these) * + ***************************************************************************/ + +static MEMFILE *mopen(const uint8_t *src, uint32_t length) +{ + if (src == NULL || length == 0) + return NULL; + + MEMFILE *b = (MEMFILE *)malloc(sizeof (MEMFILE)); + if (b == NULL) + return NULL; + + b->_base = (uint8_t *)src; + b->_ptr = (uint8_t *)src; + b->_cnt = length; + b->_bufsiz = length; + b->_eof = false; + + return b; +} + +static void mclose(MEMFILE **buf) +{ + if (*buf != NULL) + { + free(*buf); + *buf = NULL; + } +} + +static size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf) +{ + if (buf == NULL || buf->_ptr == NULL) + return 0; + + size_t wrcnt = size * count; + if (size == 0 || buf->_eof) + return 0; + + int32_t pcnt = (buf->_cnt > wrcnt) ? (int32_t)wrcnt : (int32_t)buf->_cnt; + memcpy(buffer, buf->_ptr, pcnt); + + buf->_cnt -= pcnt; + buf->_ptr += pcnt; + + if (buf->_cnt <= 0) + { + buf->_ptr = buf->_base + buf->_bufsiz; + buf->_cnt = 0; + buf->_eof = true; + } + + return pcnt / size; +} + +static bool meof(MEMFILE *buf) +{ + if (buf == NULL) + return true; + + return buf->_eof; +} + +static void mseek(MEMFILE *buf, int32_t offset, int32_t whence) +{ + if (buf == NULL) + return; + + if (buf->_base) + { + switch (whence) + { + case SEEK_SET: buf->_ptr = buf->_base + offset; break; + case SEEK_CUR: buf->_ptr += offset; break; + case SEEK_END: buf->_ptr = buf->_base + buf->_bufsiz + offset; break; + default: break; + } + + buf->_eof = false; + if (buf->_ptr >= buf->_base+buf->_bufsiz) + { + buf->_ptr = buf->_base + buf->_bufsiz; + buf->_eof = true; + } + + buf->_cnt = (buf->_base + buf->_bufsiz) - buf->_ptr; + } +} + +static void mrewind(MEMFILE *buf) +{ + mseek(buf, 0, SEEK_SET); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/pmplay.h Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,166 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +// AUDIO DRIVERS +#if defined AUDIODRIVER_SDL +#include "audiodrivers/sdl/sdldriver.h" +#elif defined AUDIODRIVER_WINMM +#include "audiodrivers/winmm/winmm.h" +#else +// Read "audiodrivers/how_to_write_drivers.txt" +#endif + +enum // voice flags +{ + IS_Vol = 1, + IS_Period = 2, + IS_NyTon = 4, + IS_Pan = 8, + IS_QuickVol = 16 +}; + +enum // note +{ + NOTE_KEYOFF = 97 +}; + +enum // header flags +{ + LINEAR_FREQUENCIES = 1 +}; + +enum // sample flags +{ + LOOP_OFF = 0, + LOOP_FORWARD = 1, + LOOP_PINGPONG = 2, + SAMPLE_16BIT = 16 +}; + +enum // envelope flags +{ + ENV_ENABLED = 1, + ENV_SUSTAIN = 2, + ENV_LOOP = 4 +}; + +typedef struct songTyp_t +{ + char name[20+1]; + uint8_t antChn, pattDelTime, pattDelTime2, pBreakPos, songTab[256]; + bool pBreakFlag, posJumpFlag; + int16_t songPos, pattNr, pattPos, pattLen; + uint16_t len, repS, speed, tempo, globVol, timer, ver; + + uint16_t antInstrs; // 8bb: added this +} songTyp; + +typedef struct sampleTyp_t +{ + char name[22+1]; + int32_t len, repS, repL; + uint8_t vol; + int8_t fine; + uint8_t typ, pan; + int8_t relTon; + int8_t *pek; +} sampleTyp; + +typedef struct instrTyp_t +{ + char name[22+1]; + uint8_t ta[96]; + int16_t envVP[12][2], envPP[12][2]; + uint8_t envVPAnt, envPPAnt; + uint8_t envVSust, envVRepS, envVRepE; + uint8_t envPSust, envPRepS, envPRepE; + uint8_t envVTyp, envPTyp; + uint8_t vibTyp, vibSweep, vibDepth, vibRate; + uint16_t fadeOut; + uint8_t mute; + int16_t antSamp; + sampleTyp samp[16]; +} instrTyp; + +typedef struct stmTyp_t +{ + volatile uint8_t status; + int8_t relTonNr, fineTune; + uint8_t sampleNr, instrNr, effTyp, eff, smpOffset, tremorSave, tremorPos; + uint8_t globVolSlideSpeed, panningSlideSpeed, mute, waveCtrl, portaDir; + uint8_t glissFunk, vibPos, tremPos, vibSpeed, vibDepth, tremSpeed, tremDepth; + uint8_t pattPos, loopCnt, volSlideSpeed, fVolSlideUpSpeed, fVolSlideDownSpeed; + uint8_t fPortaUpSpeed, fPortaDownSpeed, ePortaUpSpeed, ePortaDownSpeed; + uint8_t portaUpSpeed, portaDownSpeed, retrigSpeed, retrigCnt, retrigVol; + uint8_t volKolVol, tonNr, envPPos, eVibPos, envVPos, realVol, oldVol, outVol; + uint8_t oldPan, outPan, finalPan; + bool envSustainActive; + int16_t envVIPValue, envPIPValue; + uint16_t outPeriod, realPeriod, finalPeriod, finalVol, tonTyp, wantPeriod, portaSpeed; + uint16_t envVCnt, envVAmp, envPCnt, envPAmp, eVibAmp, eVibSweep; + uint16_t fadeOutAmp, fadeOutSpeed; + int32_t smpStartPos; + instrTyp *instrSeg; +} stmTyp; + +#ifdef _MSC_VER +#pragma pack(push) +#pragma pack(1) +#endif +typedef struct tonTyp_t // this one must be packed on some systems +{ + uint8_t ton, instr, vol, effTyp, eff; +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +tonTyp; +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +// globalized +extern volatile bool interpolationFlag, volumeRampingFlag, moduleLoaded, musicPaused, WAVDump_Flag; +extern bool linearFrqTab; +extern volatile const uint16_t *note2Period; +extern uint16_t pattLens[256]; +extern int16_t PMPTmpActiveChannel, boostLevel; +extern int32_t masterVol, PMPLeft; +extern int32_t realReplayRate, quickVolSizeVal, speedVal; +extern uint32_t frequenceDivFactor, frequenceMulFactor; +extern uint32_t CDA_Amp; +extern tonTyp *patt[256]; +extern instrTyp *instr[1+128]; +extern songTyp song; +extern stmTyp stm[32]; + +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +bool initMusic(int32_t audioFrequency, int32_t audioBufferSize, bool interpolation, bool volumeRamping); +bool loadMusicFromData(const uint8_t *data, uint32_t dataLength); // .XM/.MOD/.FT +bool loadMusic(const char *filename); // .XM/.MOD/.FT +void freeMusic(void); +bool startMusic(void); +void stopMusic(); +void pauseMusic(void); +void resumeMusic(void); +void setMasterVol(int32_t v); // 0..256 +void setAmp(int32_t level); // 1..32 +void setPos(int32_t pos, int32_t row); // input of -1 = don't change +void stopVoices(void); +void updateReplayRate(void); +void startPlaying(void); +void stopPlaying(void); + +bool WAVDump_Record(const char *filenameOut); + +// 8bb: added these three, handy +void WAVDump_Abort(void); +int32_t getMasterVol(void); +int32_t getAmp(void); +uint8_t getNumActiveVoices(void); +void toggleMusic(void); +void setInterpolation(bool on); +void setVolumeRamping(bool on);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/snd_masm.c Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,676 @@ +#include <stdint.h> +#include <stdbool.h> +#include "snd_masm.h" +#include "pmplay.h" + +/* 8bb: This is done in a slightly different way, but the result +** is the same (bit-accurate to FT2.08/FT2.09 w/ SB16, and WAV-writer). +** +** Mixer macros are stored in snd_masm.h +*/ + +void PMPMix32Proc(CIType *v, int32_t numSamples, int32_t bufferPos) +{ + if (v->SType & SType_Off) + return; // voice is not active + + uint32_t volStatus = v->SLVol1 | v->SRVol1; + if (volumeRampingFlag) + volStatus |= v->SLVol2 | v->SRVol2; + + if (volStatus == 0) // silence mix + { + const uint64_t samplesToMix = (uint64_t)v->SFrq * (uint32_t)numSamples; // 16.16fp + + const int32_t samples = (int32_t)(samplesToMix >> 16); + const int32_t samplesFrac = (samplesToMix & 0xFFFF) + (v->SPosDec >> 16); + + int32_t realPos = v->SPos + samples + (samplesFrac >> 16); + int32_t posFrac = samplesFrac & 0xFFFF; + + if (realPos >= v->SLen) + { + uint8_t SType = v->SType; + if (SType & (SType_Fwd+SType_Rev)) + { + do + { + SType ^= SType_RevDir; + realPos -= v->SRepL; + } + while (realPos >= v->SLen); + v->SType = SType; + } + else + { + v->SType = SType_Off; + return; + } + } + + v->SPosDec = posFrac << 16; + v->SPos = realPos; + } + else // normal mixing + { + bool mixInCenter; + if (volumeRampingFlag) + mixInCenter = (v->SLVol2 == v->SRVol2) && (v->SLVolIP == v->SRVolIP); + else + mixInCenter = v->SLVol1 == v->SRVol1; + + mixRoutineTable[(mixInCenter * 8) + v->SMixType](v, numSamples, bufferPos); + } +} + +static void mix8b(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample; + + GET_VOL + GET_MIXER_VARS + SET_BASE8 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + CDA_BytesLeft -= samplesToMix; + + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_8BIT + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_8BIT + MIX_8BIT + MIX_8BIT + MIX_8BIT + } + HANDLE_POS_END + } + + SET_BACK_MIXER_POS +} + +static void mix8bIntrp(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample, sample2; + + GET_VOL + GET_MIXER_VARS + SET_BASE8 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + CDA_BytesLeft -= samplesToMix; + + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_8BIT_INTRP + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_8BIT_INTRP + MIX_8BIT_INTRP + MIX_8BIT_INTRP + MIX_8BIT_INTRP + } + HANDLE_POS_END + } + + SET_BACK_MIXER_POS +} + +static void mix8bRamp(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample; + + GET_MIXER_VARS + GET_RAMP_VARS + SET_BASE8 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + LIMIT_MIX_NUM_RAMP + CDA_BytesLeft -= samplesToMix; + + GET_VOL_RAMP + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_8BIT + VOL_RAMP + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_8BIT + VOL_RAMP + MIX_8BIT + VOL_RAMP + MIX_8BIT + VOL_RAMP + MIX_8BIT + VOL_RAMP + } + HANDLE_POS_END + SET_VOL_BACK + } + + SET_BACK_MIXER_POS +} + +static void mix8bRampIntrp(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample, sample2; + + GET_MIXER_VARS + GET_RAMP_VARS + SET_BASE8 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + LIMIT_MIX_NUM_RAMP + CDA_BytesLeft -= samplesToMix; + + GET_VOL_RAMP + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_8BIT_INTRP + VOL_RAMP + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_8BIT_INTRP + VOL_RAMP + MIX_8BIT_INTRP + VOL_RAMP + MIX_8BIT_INTRP + VOL_RAMP + MIX_8BIT_INTRP + VOL_RAMP + } + HANDLE_POS_END + SET_VOL_BACK + } + + SET_BACK_MIXER_POS +} + +static void mix16b(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample; + + GET_VOL + GET_MIXER_VARS + SET_BASE16 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + CDA_BytesLeft -= samplesToMix; + + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_16BIT + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_16BIT + MIX_16BIT + MIX_16BIT + MIX_16BIT + } + HANDLE_POS_END + } + + SET_BACK_MIXER_POS +} + +static void mix16bIntrp(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample, sample2; + + GET_VOL + GET_MIXER_VARS + SET_BASE16 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + CDA_BytesLeft -= samplesToMix; + + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_16BIT_INTRP + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_16BIT_INTRP + MIX_16BIT_INTRP + MIX_16BIT_INTRP + MIX_16BIT_INTRP + } + HANDLE_POS_END + } + + SET_BACK_MIXER_POS +} + +static void mix16bRamp(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample; + + GET_MIXER_VARS + GET_RAMP_VARS + SET_BASE16 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + LIMIT_MIX_NUM_RAMP + CDA_BytesLeft -= samplesToMix; + + GET_VOL_RAMP + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_16BIT + VOL_RAMP + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_16BIT + VOL_RAMP + MIX_16BIT + VOL_RAMP + MIX_16BIT + VOL_RAMP + MIX_16BIT + VOL_RAMP + } + HANDLE_POS_END + SET_VOL_BACK + } + + SET_BACK_MIXER_POS +} + +static void mix16bRampIntrp(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample, sample2; + + GET_MIXER_VARS + GET_RAMP_VARS + SET_BASE16 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + LIMIT_MIX_NUM_RAMP + CDA_BytesLeft -= samplesToMix; + + GET_VOL_RAMP + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_16BIT_INTRP + VOL_RAMP + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_16BIT_INTRP + VOL_RAMP + MIX_16BIT_INTRP + VOL_RAMP + MIX_16BIT_INTRP + VOL_RAMP + MIX_16BIT_INTRP + VOL_RAMP + } + HANDLE_POS_END + SET_VOL_BACK + } + + SET_BACK_MIXER_POS +} + +static void mix8bCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample; + + GET_VOL_CENTER + GET_MIXER_VARS + SET_BASE8 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + CDA_BytesLeft -= samplesToMix; + + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_8BIT_M + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_8BIT_M + MIX_8BIT_M + MIX_8BIT_M + MIX_8BIT_M + } + HANDLE_POS_END + } + + SET_BACK_MIXER_POS +} + +static void mix8bIntrpCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample, sample2; + + GET_VOL_CENTER + GET_MIXER_VARS + SET_BASE8 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + CDA_BytesLeft -= samplesToMix; + + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_8BIT_INTRP_M + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_8BIT_INTRP_M + MIX_8BIT_INTRP_M + MIX_8BIT_INTRP_M + MIX_8BIT_INTRP_M + } + HANDLE_POS_END + } + + SET_BACK_MIXER_POS +} + +static void mix8bRampCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample; + + GET_MIXER_VARS + GET_RAMP_VARS + SET_BASE8 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + LIMIT_MIX_NUM_RAMP + CDA_BytesLeft -= samplesToMix; + + GET_VOL_RAMP + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_8BIT_M + VOL_RAMP + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_8BIT_M + VOL_RAMP + MIX_8BIT_M + VOL_RAMP + MIX_8BIT_M + VOL_RAMP + MIX_8BIT_M + VOL_RAMP + } + HANDLE_POS_END + SET_VOL_BACK + } + + SET_BACK_MIXER_POS +} + +static void mix8bRampIntrpCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample, sample2; + + GET_MIXER_VARS + GET_RAMP_VARS + SET_BASE8 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + LIMIT_MIX_NUM_RAMP + CDA_BytesLeft -= samplesToMix; + + GET_VOL_RAMP + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_8BIT_INTRP_M + VOL_RAMP + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_8BIT_INTRP_M + VOL_RAMP + MIX_8BIT_INTRP_M + VOL_RAMP + MIX_8BIT_INTRP_M + VOL_RAMP + MIX_8BIT_INTRP_M + VOL_RAMP + } + HANDLE_POS_END + SET_VOL_BACK + } + + SET_BACK_MIXER_POS +} + +static void mix16bCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample; + + GET_VOL_CENTER + GET_MIXER_VARS + SET_BASE16 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + CDA_BytesLeft -= samplesToMix; + + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_16BIT_M + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_16BIT_M + MIX_16BIT_M + MIX_16BIT_M + MIX_16BIT_M + } + HANDLE_POS_END + } + + SET_BACK_MIXER_POS +} + +static void mix16bIntrpCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample, sample2; + + GET_VOL_CENTER + GET_MIXER_VARS + SET_BASE16 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + CDA_BytesLeft -= samplesToMix; + + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_16BIT_INTRP_M + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_16BIT_INTRP_M + MIX_16BIT_INTRP_M + MIX_16BIT_INTRP_M + MIX_16BIT_INTRP_M + } + HANDLE_POS_END + } + + SET_BACK_MIXER_POS +} + +static void mix16bRampCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample; + + GET_MIXER_VARS + GET_RAMP_VARS + SET_BASE16 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + LIMIT_MIX_NUM_RAMP + CDA_BytesLeft -= samplesToMix; + + GET_VOL_RAMP + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_16BIT_M + VOL_RAMP + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_16BIT_M + VOL_RAMP + MIX_16BIT_M + VOL_RAMP + MIX_16BIT_M + VOL_RAMP + MIX_16BIT_M + VOL_RAMP + } + HANDLE_POS_END + SET_VOL_BACK + } + + SET_BACK_MIXER_POS +} + +static void mix16bRampIntrpCenter(CIType *v, uint32_t numSamples, uint32_t bufferPos) +{ + int32_t sample, sample2; + + GET_MIXER_VARS + GET_RAMP_VARS + SET_BASE16 + + int32_t CDA_BytesLeft = numSamples; + while (CDA_BytesLeft > 0) + { + LIMIT_MIX_NUM + LIMIT_MIX_NUM_RAMP + CDA_BytesLeft -= samplesToMix; + + GET_VOL_RAMP + HANDLE_POS_START + for (i = 0; i < (samplesToMix & 3); i++) + { + MIX_16BIT_INTRP_M + VOL_RAMP + } + samplesToMix >>= 2; + for (i = 0; i < samplesToMix; i++) + { + MIX_16BIT_INTRP_M + VOL_RAMP + MIX_16BIT_INTRP_M + VOL_RAMP + MIX_16BIT_INTRP_M + VOL_RAMP + MIX_16BIT_INTRP_M + VOL_RAMP + } + HANDLE_POS_END + SET_VOL_BACK + } + + SET_BACK_MIXER_POS +} + +mixRoutine mixRoutineTable[16] = +{ + (mixRoutine)mix8b, + (mixRoutine)mix8bIntrp, + (mixRoutine)mix8bRamp, + (mixRoutine)mix8bRampIntrp, + (mixRoutine)mix16b, + (mixRoutine)mix16bIntrp, + (mixRoutine)mix16bRamp, + (mixRoutine)mix16bRampIntrp, + (mixRoutine)mix8bCenter, + (mixRoutine)mix8bIntrpCenter, + (mixRoutine)mix8bRampCenter, + (mixRoutine)mix8bRampIntrpCenter, + (mixRoutine)mix16bCenter, + (mixRoutine)mix16bIntrpCenter, + (mixRoutine)mix16bRampCenter, + (mixRoutine)mix16bRampIntrpCenter +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/snd_masm.h Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,227 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "pmp_mix.h" + +#define GET_VOL \ + const int32_t CDA_LVol = v->SLVol1; \ + const int32_t CDA_RVol = v->SRVol1; \ + +#define GET_VOL_CENTER \ + const int32_t CDA_LVol = v->SLVol1; \ + +#define GET_VOL_RAMP \ + int32_t CDA_LVol = v->SLVol2; \ + int32_t CDA_RVol = v->SRVol2; \ + +#define SET_VOL_BACK \ + v->SLVol2 = CDA_LVol; \ + v->SRVol2 = CDA_RVol; \ + +#define GET_MIXER_VARS \ + int32_t *audioMix = CDA_MixBuffer + (bufferPos << 1); \ + int32_t realPos = v->SPos; \ + uint32_t pos = v->SPosDec; \ + uint16_t CDA_MixBuffPos = (32768+96)-8; /* address of FT2 mix buffer minus mix sample size (used for quirky LERP) */ \ + +#define GET_RAMP_VARS \ + int32_t CDA_LVolIP = v->SLVolIP; \ + int32_t CDA_RVolIP = v->SRVolIP; \ + +#define SET_BASE8 \ + const int8_t *CDA_LinearAdr = (int8_t *)v->SBase; \ + const int8_t *CDA_LinAdrRev = (int8_t *)v->SRevBase; \ + const int8_t *smpPtr = CDA_LinearAdr + realPos; \ + +#define SET_BASE16 \ + const int16_t *CDA_LinearAdr = (int16_t *)v->SBase; \ + const int16_t *CDA_LinAdrRev = (int16_t *)v->SRevBase; \ + const int16_t *smpPtr = CDA_LinearAdr + realPos; \ + +#define INC_POS \ + smpPtr += CDA_IPValH; \ + smpPtr += (CDA_IPValL > (uint32_t)~pos); /* if pos would 32-bit overflow after CDA_IPValL add, add one to smpPtr (branchless) */ \ + pos += CDA_IPValL; \ + +#define SET_BACK_MIXER_POS \ + v->SPosDec = pos & 0xFFFF0000; \ + v->SPos = realPos; \ + +#define VOL_RAMP \ + CDA_LVol += CDA_LVolIP; \ + CDA_RVol += CDA_RVolIP; \ + +// stereo mixing without interpolation + +#define MIX_8BIT \ + sample = (*smpPtr) << (28-8); \ + *audioMix++ += ((int64_t)sample * (int32_t)CDA_LVol) >> 32; \ + *audioMix++ += ((int64_t)sample * (int32_t)CDA_RVol) >> 32; \ + INC_POS \ + +#define MIX_16BIT \ + sample = (*smpPtr) << (28-16); \ + *audioMix++ += ((int64_t)sample * (int32_t)CDA_LVol) >> 32; \ + *audioMix++ += ((int64_t)sample * (int32_t)CDA_RVol) >> 32; \ + INC_POS \ + +// center mixing without interpolation + +#define MIX_8BIT_M \ + sample = (*smpPtr) << (28-8); \ + sample = ((int64_t)sample * (int32_t)CDA_LVol) >> 32; \ + *audioMix++ += sample; \ + *audioMix++ += sample; \ + INC_POS \ + +#define MIX_16BIT_M \ + sample = (*smpPtr) << (28-16); \ + sample = ((int64_t)sample * (int32_t)CDA_LVol) >> 32; \ + *audioMix++ += sample; \ + *audioMix++ += sample; \ + INC_POS \ + +// linear interpolation with bit-accurate results to FT2.08/FT2.09 +#define LERP(s1, s2, f) \ +{ \ + s2 -= s1; \ + f >>= 1; \ + s2 = ((int64_t)s2 * (int32_t)f) >> 32; \ + f += f; \ + s2 += s2; \ + s2 += s1; \ +} \ + +// stereo mixing w/ linear interpolation + +#define MIX_8BIT_INTRP \ + sample = smpPtr[0] << 8; \ + sample2 = smpPtr[1] << 8; \ + LERP(sample, sample2, pos) \ + sample2 <<= (28-16); \ + *audioMix++ += ((int64_t)sample2 * (int32_t)CDA_LVol) >> 32; \ + *audioMix++ += ((int64_t)sample2 * (int32_t)CDA_RVol) >> 32; \ + INC_POS \ + +#define MIX_16BIT_INTRP \ + sample = smpPtr[0]; \ + sample2 = smpPtr[1]; \ + LERP(sample, sample2, pos) \ + sample2 <<= (28-16); \ + *audioMix++ += ((int64_t)sample2 * (int32_t)CDA_LVol) >> 32; \ + *audioMix++ += ((int64_t)sample2 * (int32_t)CDA_RVol) >> 32; \ + INC_POS \ + +// center mixing w/ linear interpolation + +#define MIX_8BIT_INTRP_M \ + sample = smpPtr[0] << 8; \ + sample2 = smpPtr[1] << 8; \ + LERP(sample, sample2, pos) \ + sample2 <<= (28-16); \ + sample = ((int64_t)sample2 * (int32_t)CDA_LVol) >> 32; \ + *audioMix++ += sample; \ + *audioMix++ += sample; \ + INC_POS \ + +#define MIX_16BIT_INTRP_M \ + sample = smpPtr[0]; \ + sample2 = smpPtr[1]; \ + LERP(sample, sample2, pos) \ + sample2 <<= (28-16); \ + sample = ((int64_t)sample2 * (int32_t)CDA_LVol) >> 32; \ + *audioMix++ += sample; \ + *audioMix++ += sample; \ + INC_POS \ + +// ------------------------ + +#define LIMIT_MIX_NUM \ + int32_t samplesToMix; \ + int32_t SFrq = v->SFrq; \ + int32_t i = (v->SLen-1) - realPos; \ + if (i > UINT16_MAX) i = UINT16_MAX; /* 8bb: added this to prevent 64-bit div (still bit-accurate mixing results) */ \ + if (SFrq != 0) \ + { \ + const uint32_t tmp32 = (i << 16) | ((0xFFFF0000 - pos) >> 16); \ + samplesToMix = (tmp32 / (uint32_t)SFrq) + 1; \ + } \ + else \ + { \ + samplesToMix = 65535; \ + } \ + \ + if (samplesToMix > CDA_BytesLeft) \ + samplesToMix = CDA_BytesLeft; \ + +#define LIMIT_MIX_NUM_RAMP \ + if (v->SVolIPLen == 0) \ + { \ + CDA_LVolIP = 0; \ + CDA_RVolIP = 0; \ + } \ + else \ + { \ + if (samplesToMix > v->SVolIPLen) \ + samplesToMix = v->SVolIPLen; \ + \ + v->SVolIPLen -= samplesToMix; \ + } \ + +#define HANDLE_POS_START \ + const bool backwards = (v->SType & (SType_Rev+SType_RevDir)) == SType_Rev+SType_RevDir; \ + if (backwards) \ + { \ + SFrq = 0 - SFrq; \ + realPos = ~realPos; \ + smpPtr = CDA_LinAdrRev + realPos; \ + pos ^= 0xFFFF0000; \ + } \ + else \ + { \ + smpPtr = CDA_LinearAdr + realPos; \ + } \ + \ + pos += CDA_MixBuffPos; \ + const int32_t CDA_IPValH = (int32_t)SFrq >> 16; \ + const uint32_t CDA_IPValL = ((uint32_t)(SFrq & 0xFFFF) << 16) + 8; /* 8 = mixer buffer increase (for LERP to be bit-accurate to FT2) */ \ + +#define HANDLE_POS_END \ + if (backwards) \ + { \ + pos ^= 0xFFFF0000; \ + realPos = ~(int32_t)(smpPtr - CDA_LinAdrRev); \ + } \ + else \ + { \ + realPos = (int32_t)(smpPtr - CDA_LinearAdr); \ + } \ + CDA_MixBuffPos = pos & 0xFFFF; \ + pos &= 0xFFFF0000; \ + \ + if (realPos >= v->SLen) \ + { \ + uint8_t SType = v->SType; \ + if (SType & (SType_Fwd+SType_Rev)) \ + { \ + do \ + { \ + realPos -= v->SRepL; \ + SType ^= SType_RevDir; \ + } \ + while (realPos >= v->SLen); \ + v->SType = SType; \ + } \ + else \ + { \ + v->SType = SType_Off; \ + return; \ + } \ + } \ + +typedef void (*mixRoutine)(void *, int32_t, int32_t); + +extern mixRoutine mixRoutineTable[16]; + +void PMPMix32Proc(CIType *v, int32_t numSamples, int32_t bufferPos);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/tables.c Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,453 @@ +#include <stdint.h> + +// 8bb: bit-accurate FT2 tables (FT2.08/FT2.09) + +/* +** for (int32_t i = 0; i < 257; i++) +** panningTab[i] = (int32_t)round(65536.0 * sqrt(i / 256.0)); +*/ +const uint32_t panningTab[257] = +{ + 0, 4096, 5793, 7094, 8192, 9159,10033,10837,11585,12288,12953,13585,14189,14768,15326,15864, + 16384,16888,17378,17854,18318,18770,19212,19644,20066,20480,20886,21283,21674,22058,22435,22806, + 23170,23530,23884,24232,24576,24915,25249,25580,25905,26227,26545,26859,27170,27477,27780,28081, + 28378,28672,28963,29251,29537,29819,30099,30377,30652,30924,31194,31462,31727,31991,32252,32511, + 32768,33023,33276,33527,33776,34024,34270,34514,34756,34996,35235,35472,35708,35942,36175,36406, + 36636,36864,37091,37316,37540,37763,37985,38205,38424,38642,38858,39073,39287,39500,39712,39923, + 40132,40341,40548,40755,40960,41164,41368,41570,41771,41972,42171,42369,42567,42763,42959,43154, + 43348,43541,43733,43925,44115,44305,44494,44682,44869,45056,45242,45427,45611,45795,45977,46160, + 46341,46522,46702,46881,47059,47237,47415,47591,47767,47942,48117,48291,48465,48637,48809,48981, + 49152,49322,49492,49661,49830,49998,50166,50332,50499,50665,50830,50995,51159,51323,51486,51649, + 51811,51972,52134,52294,52454,52614,52773,52932,53090,53248,53405,53562,53719,53874,54030,54185, + 54340,54494,54647,54801,54954,55106,55258,55410,55561,55712,55862,56012,56162,56311,56459,56608, + 56756,56903,57051,57198,57344,57490,57636,57781,57926,58071,58215,58359,58503,58646,58789,58931, + 59073,59215,59357,59498,59639,59779,59919,60059,60199,60338,60477,60615,60753,60891,61029,61166, + 61303,61440,61576,61712,61848,61984,62119,62254,62388,62523,62657,62790,62924,63057,63190,63323, + 63455,63587,63719,63850,63982,64113,64243,64374,64504,64634,64763,64893,65022,65151,65279,65408, + 65536 +}; + +// 8bb: the last 17 values are off (but identical to FT2.08/FT2.09) because of a bug in how it calculates this table +const uint16_t amigaPeriods[1936] = +{ + 29024,28912,28800,28704,28608,28496,28384,28288,28192,28096,28000,27888,27776,27680,27584,27488, + 27392,27296,27200,27104,27008,26912,26816,26720,26624,26528,26432,26336,26240,26144,26048,25952, + 25856,25760,25664,25568,25472,25392,25312,25216,25120,25024,24928,24848,24768,24672,24576,24480, + 24384,24304,24224,24144,24064,23968,23872,23792,23712,23632,23552,23456,23360,23280,23200,23120, + 23040,22960,22880,22784,22688,22608,22528,22448,22368,22288,22208,22128,22048,21968,21888,21792, + 21696,21648,21600,21520,21440,21360,21280,21200,21120,21040,20960,20896,20832,20752,20672,20576, + 20480,20416,20352,20288,20224,20160,20096,20016,19936,19872,19808,19728,19648,19584,19520,19424, + 19328,19280,19232,19168,19104,19024,18944,18880,18816,18752,18688,18624,18560,18480,18400,18320, + 18240,18192,18144,18080,18016,17952,17888,17824,17760,17696,17632,17568,17504,17440,17376,17296, + 17216,17168,17120,17072,17024,16960,16896,16832,16768,16704,16640,16576,16512,16464,16416,16336, + 16256,16208,16160,16112,16064,16000,15936,15872,15808,15760,15712,15648,15584,15536,15488,15424, + 15360,15312,15264,15216,15168,15104,15040,14992,14944,14880,14816,14768,14720,14672,14624,14568, + 14512,14456,14400,14352,14304,14248,14192,14144,14096,14048,14000,13944,13888,13840,13792,13744, + 13696,13648,13600,13552,13504,13456,13408,13360,13312,13264,13216,13168,13120,13072,13024,12976, + 12928,12880,12832,12784,12736,12696,12656,12608,12560,12512,12464,12424,12384,12336,12288,12240, + 12192,12152,12112,12072,12032,11984,11936,11896,11856,11816,11776,11728,11680,11640,11600,11560, + 11520,11480,11440,11392,11344,11304,11264,11224,11184,11144,11104,11064,11024,10984,10944,10896, + 10848,10824,10800,10760,10720,10680,10640,10600,10560,10520,10480,10448,10416,10376,10336,10288, + 10240,10208,10176,10144,10112,10080,10048,10008, 9968, 9936, 9904, 9864, 9824, 9792, 9760, 9712, + 9664, 9640, 9616, 9584, 9552, 9512, 9472, 9440, 9408, 9376, 9344, 9312, 9280, 9240, 9200, 9160, + 9120, 9096, 9072, 9040, 9008, 8976, 8944, 8912, 8880, 8848, 8816, 8784, 8752, 8720, 8688, 8648, + 8608, 8584, 8560, 8536, 8512, 8480, 8448, 8416, 8384, 8352, 8320, 8288, 8256, 8232, 8208, 8168, + 8128, 8104, 8080, 8056, 8032, 8000, 7968, 7936, 7904, 7880, 7856, 7824, 7792, 7768, 7744, 7712, + 7680, 7656, 7632, 7608, 7584, 7552, 7520, 7496, 7472, 7440, 7408, 7384, 7360, 7336, 7312, 7284, + 7256, 7228, 7200, 7176, 7152, 7124, 7096, 7072, 7048, 7024, 7000, 6972, 6944, 6920, 6896, 6872, + 6848, 6824, 6800, 6776, 6752, 6728, 6704, 6680, 6656, 6632, 6608, 6584, 6560, 6536, 6512, 6488, + 6464, 6440, 6416, 6392, 6368, 6348, 6328, 6304, 6280, 6256, 6232, 6212, 6192, 6168, 6144, 6120, + 6096, 6076, 6056, 6036, 6016, 5992, 5968, 5948, 5928, 5908, 5888, 5864, 5840, 5820, 5800, 5780, + 5760, 5740, 5720, 5696, 5672, 5652, 5632, 5612, 5592, 5572, 5552, 5532, 5512, 5492, 5472, 5448, + 5424, 5412, 5400, 5380, 5360, 5340, 5320, 5300, 5280, 5260, 5240, 5224, 5208, 5188, 5168, 5144, + 5120, 5104, 5088, 5072, 5056, 5040, 5024, 5004, 4984, 4968, 4952, 4932, 4912, 4896, 4880, 4856, + 4832, 4820, 4808, 4792, 4776, 4756, 4736, 4720, 4704, 4688, 4672, 4656, 4640, 4620, 4600, 4580, + 4560, 4548, 4536, 4520, 4504, 4488, 4472, 4456, 4440, 4424, 4408, 4392, 4376, 4360, 4344, 4324, + 4304, 4292, 4280, 4268, 4256, 4240, 4224, 4208, 4192, 4176, 4160, 4144, 4128, 4116, 4104, 4084, + 4064, 4052, 4040, 4028, 4016, 4000, 3984, 3968, 3952, 3940, 3928, 3912, 3896, 3884, 3872, 3856, + 3840, 3828, 3816, 3804, 3792, 3776, 3760, 3748, 3736, 3720, 3704, 3692, 3680, 3668, 3656, 3642, + 3628, 3614, 3600, 3588, 3576, 3562, 3548, 3536, 3524, 3512, 3500, 3486, 3472, 3460, 3448, 3436, + 3424, 3412, 3400, 3388, 3376, 3364, 3352, 3340, 3328, 3316, 3304, 3292, 3280, 3268, 3256, 3244, + 3232, 3220, 3208, 3196, 3184, 3174, 3164, 3152, 3140, 3128, 3116, 3106, 3096, 3084, 3072, 3060, + 3048, 3038, 3028, 3018, 3008, 2996, 2984, 2974, 2964, 2954, 2944, 2932, 2920, 2910, 2900, 2890, + 2880, 2870, 2860, 2848, 2836, 2826, 2816, 2806, 2796, 2786, 2776, 2766, 2756, 2746, 2736, 2724, + 2712, 2706, 2700, 2690, 2680, 2670, 2660, 2650, 2640, 2630, 2620, 2612, 2604, 2594, 2584, 2572, + 2560, 2552, 2544, 2536, 2528, 2520, 2512, 2502, 2492, 2484, 2476, 2466, 2456, 2448, 2440, 2428, + 2416, 2410, 2404, 2396, 2388, 2378, 2368, 2360, 2352, 2344, 2336, 2328, 2320, 2310, 2300, 2290, + 2280, 2274, 2268, 2260, 2252, 2244, 2236, 2228, 2220, 2212, 2204, 2196, 2188, 2180, 2172, 2162, + 2152, 2146, 2140, 2134, 2128, 2120, 2112, 2104, 2096, 2088, 2080, 2072, 2064, 2058, 2052, 2042, + 2032, 2026, 2020, 2014, 2008, 2000, 1992, 1984, 1976, 1970, 1964, 1956, 1948, 1942, 1936, 1928, + 1920, 1914, 1908, 1902, 1896, 1888, 1880, 1874, 1868, 1860, 1852, 1846, 1840, 1834, 1828, 1821, + 1814, 1807, 1800, 1794, 1788, 1781, 1774, 1768, 1762, 1756, 1750, 1743, 1736, 1730, 1724, 1718, + 1712, 1706, 1700, 1694, 1688, 1682, 1676, 1670, 1664, 1658, 1652, 1646, 1640, 1634, 1628, 1622, + 1616, 1610, 1604, 1598, 1592, 1587, 1582, 1576, 1570, 1564, 1558, 1553, 1548, 1542, 1536, 1530, + 1524, 1519, 1514, 1509, 1504, 1498, 1492, 1487, 1482, 1477, 1472, 1466, 1460, 1455, 1450, 1445, + 1440, 1435, 1430, 1424, 1418, 1413, 1408, 1403, 1398, 1393, 1388, 1383, 1378, 1373, 1368, 1362, + 1356, 1353, 1350, 1345, 1340, 1335, 1330, 1325, 1320, 1315, 1310, 1306, 1302, 1297, 1292, 1286, + 1280, 1276, 1272, 1268, 1264, 1260, 1256, 1251, 1246, 1242, 1238, 1233, 1228, 1224, 1220, 1214, + 1208, 1205, 1202, 1198, 1194, 1189, 1184, 1180, 1176, 1172, 1168, 1164, 1160, 1155, 1150, 1145, + 1140, 1137, 1134, 1130, 1126, 1122, 1118, 1114, 1110, 1106, 1102, 1098, 1094, 1090, 1086, 1081, + 1076, 1073, 1070, 1067, 1064, 1060, 1056, 1052, 1048, 1044, 1040, 1036, 1032, 1029, 1026, 1021, + 1016, 1013, 1010, 1007, 1004, 1000, 996, 992, 988, 985, 982, 978, 974, 971, 968, 964, + 960, 957, 954, 951, 948, 944, 940, 937, 934, 930, 926, 923, 920, 917, 914, 910, + 907, 903, 900, 897, 894, 890, 887, 884, 881, 878, 875, 871, 868, 865, 862, 859, + 856, 853, 850, 847, 844, 841, 838, 835, 832, 829, 826, 823, 820, 817, 814, 811, + 808, 805, 802, 799, 796, 793, 791, 788, 785, 782, 779, 776, 774, 771, 768, 765, + 762, 759, 757, 754, 752, 749, 746, 743, 741, 738, 736, 733, 730, 727, 725, 722, + 720, 717, 715, 712, 709, 706, 704, 701, 699, 696, 694, 691, 689, 686, 684, 681, + 678, 676, 675, 672, 670, 667, 665, 662, 660, 657, 655, 653, 651, 648, 646, 643, + 640, 638, 636, 634, 632, 630, 628, 625, 623, 621, 619, 616, 614, 612, 610, 607, + 604, 602, 601, 599, 597, 594, 592, 590, 588, 586, 584, 582, 580, 577, 575, 572, + 570, 568, 567, 565, 563, 561, 559, 557, 555, 553, 551, 549, 547, 545, 543, 540, + 538, 536, 535, 533, 532, 530, 528, 526, 524, 522, 520, 518, 516, 514, 513, 510, + 508, 506, 505, 503, 502, 500, 498, 496, 494, 492, 491, 489, 487, 485, 484, 482, + 480, 478, 477, 475, 474, 472, 470, 468, 467, 465, 463, 461, 460, 458, 457, 455, + 453, 451, 450, 448, 447, 445, 443, 441, 440, 438, 437, 435, 434, 432, 431, 429, + 428, 426, 425, 423, 422, 420, 419, 417, 416, 414, 413, 411, 410, 408, 407, 405, + 404, 402, 401, 399, 398, 396, 395, 393, 392, 390, 389, 388, 387, 385, 384, 382, + 381, 379, 378, 377, 376, 374, 373, 371, 370, 369, 368, 366, 365, 363, 362, 361, + 360, 358, 357, 355, 354, 353, 352, 350, 349, 348, 347, 345, 344, 343, 342, 340, + 339, 338, 337, 336, 335, 333, 332, 331, 330, 328, 327, 326, 325, 324, 323, 321, + 320, 319, 318, 317, 316, 315, 314, 312, 311, 310, 309, 308, 307, 306, 305, 303, + 302, 301, 300, 299, 298, 297, 296, 295, 294, 293, 292, 291, 290, 288, 287, 286, + 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, + 269, 268, 267, 266, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256, 255, + 254, 253, 252, 251, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 242, 241, + 240, 239, 238, 237, 237, 236, 235, 234, 233, 232, 231, 230, 230, 229, 228, 227, + 227, 226, 225, 224, 223, 222, 222, 221, 220, 219, 219, 218, 217, 216, 215, 214, + 214, 213, 212, 211, 211, 210, 209, 208, 208, 207, 206, 205, 205, 204, 203, 202, + 202, 201, 200, 199, 199, 198, 198, 197, 196, 195, 195, 194, 193, 192, 192, 191, + 190, 189, 189, 188, 188, 187, 186, 185, 185, 184, 184, 183, 182, 181, 181, 180, + 180, 179, 179, 178, 177, 176, 176, 175, 175, 174, 173, 172, 172, 171, 171, 170, + 169, 169, 169, 168, 167, 166, 166, 165, 165, 164, 164, 163, 163, 162, 161, 160, + 160, 159, 159, 158, 158, 157, 157, 156, 156, 155, 155, 154, 153, 152, 152, 151, + 151, 150, 150, 149, 149, 148, 148, 147, 147, 146, 146, 145, 145, 144, 144, 143, + 142, 142, 142, 141, 141, 140, 140, 139, 139, 138, 138, 137, 137, 136, 136, 135, + 134, 134, 134, 133, 133, 132, 132, 131, 131, 130, 130, 129, 129, 128, 128, 127, + 127, 126, 126, 125, 125, 124, 124, 123, 123, 123, 123, 122, 122, 121, 121, 120, + 120, 119, 119, 118, 118, 117, 117, 117, 117, 116, 116, 115, 115, 114, 114, 113, + 113, 112, 112, 112, 112, 111, 111, 110, 110, 109, 109, 108, 108, 108, 108, 107, + 107, 106, 106, 105, 105, 105, 105, 104, 104, 103, 103, 102, 102, 102, 102, 101, + 101, 100, 100, 99, 99, 99, 99, 98, 98, 97, 97, 97, 97, 96, 96, 95, + 95, 95, 95, 94, 94, 93, 93, 93, 93, 92, 92, 91, 91, 91, 91, 90, + 90, 89, 89, 89, 89, 88, 88, 87, 87, 87, 87, 86, 86, 85, 85, 85, + 85, 84, 84, 84, 84, 83, 83, 82, 82, 82, 82, 81, 81, 81, 81, 80, + 80, 79, 79, 79, 79, 78, 78, 78, 78, 77, 77, 77, 77, 76, 76, 75, + 75, 75, 75, 75, 75, 74, 74, 73, 73, 73, 73, 72, 72, 72, 72, 71, + 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, + 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 64, 64, 64, 64, 63, + 63, 63, 63, 63, 63, 62, 62, 62, 62, 61, 61, 61, 61, 60, 60, 60, + 60, 60, 60, 59, 59, 59, 59, 58, 58, 58, 58, 57, 57, 57, 57, 57, + 57, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 53, + 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 50, + 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, + 48, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 45, 45, 45, + 45, 45, 45, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 42, + 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 40, 40, 40, + 40, 40, 40, 39, 39, 39, 39, 39, 39, 39, 39, 38, 38, 38, 38, 38, + 38, 38, 38, 37, 37, 37, 37, 37, 37, 36, 36, 36, 36, 36, 36, 36, + 36, 35, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 34, 34, 34, + 34, 33, 33, 33, 33, 33, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 22, + 16, 8, 0, 16, 32, 24, 16, 8, 0, 16, 32, 24, 16, 8, 0, 0 +}; + +const uint16_t linearPeriods[1936] = +{ + 7744,7740,7736,7732,7728,7724,7720,7716,7712,7708,7704,7700,7696,7692,7688,7684, + 7680,7676,7672,7668,7664,7660,7656,7652,7648,7644,7640,7636,7632,7628,7624,7620, + 7616,7612,7608,7604,7600,7596,7592,7588,7584,7580,7576,7572,7568,7564,7560,7556, + 7552,7548,7544,7540,7536,7532,7528,7524,7520,7516,7512,7508,7504,7500,7496,7492, + 7488,7484,7480,7476,7472,7468,7464,7460,7456,7452,7448,7444,7440,7436,7432,7428, + 7424,7420,7416,7412,7408,7404,7400,7396,7392,7388,7384,7380,7376,7372,7368,7364, + 7360,7356,7352,7348,7344,7340,7336,7332,7328,7324,7320,7316,7312,7308,7304,7300, + 7296,7292,7288,7284,7280,7276,7272,7268,7264,7260,7256,7252,7248,7244,7240,7236, + 7232,7228,7224,7220,7216,7212,7208,7204,7200,7196,7192,7188,7184,7180,7176,7172, + 7168,7164,7160,7156,7152,7148,7144,7140,7136,7132,7128,7124,7120,7116,7112,7108, + 7104,7100,7096,7092,7088,7084,7080,7076,7072,7068,7064,7060,7056,7052,7048,7044, + 7040,7036,7032,7028,7024,7020,7016,7012,7008,7004,7000,6996,6992,6988,6984,6980, + 6976,6972,6968,6964,6960,6956,6952,6948,6944,6940,6936,6932,6928,6924,6920,6916, + 6912,6908,6904,6900,6896,6892,6888,6884,6880,6876,6872,6868,6864,6860,6856,6852, + 6848,6844,6840,6836,6832,6828,6824,6820,6816,6812,6808,6804,6800,6796,6792,6788, + 6784,6780,6776,6772,6768,6764,6760,6756,6752,6748,6744,6740,6736,6732,6728,6724, + 6720,6716,6712,6708,6704,6700,6696,6692,6688,6684,6680,6676,6672,6668,6664,6660, + 6656,6652,6648,6644,6640,6636,6632,6628,6624,6620,6616,6612,6608,6604,6600,6596, + 6592,6588,6584,6580,6576,6572,6568,6564,6560,6556,6552,6548,6544,6540,6536,6532, + 6528,6524,6520,6516,6512,6508,6504,6500,6496,6492,6488,6484,6480,6476,6472,6468, + 6464,6460,6456,6452,6448,6444,6440,6436,6432,6428,6424,6420,6416,6412,6408,6404, + 6400,6396,6392,6388,6384,6380,6376,6372,6368,6364,6360,6356,6352,6348,6344,6340, + 6336,6332,6328,6324,6320,6316,6312,6308,6304,6300,6296,6292,6288,6284,6280,6276, + 6272,6268,6264,6260,6256,6252,6248,6244,6240,6236,6232,6228,6224,6220,6216,6212, + 6208,6204,6200,6196,6192,6188,6184,6180,6176,6172,6168,6164,6160,6156,6152,6148, + 6144,6140,6136,6132,6128,6124,6120,6116,6112,6108,6104,6100,6096,6092,6088,6084, + 6080,6076,6072,6068,6064,6060,6056,6052,6048,6044,6040,6036,6032,6028,6024,6020, + 6016,6012,6008,6004,6000,5996,5992,5988,5984,5980,5976,5972,5968,5964,5960,5956, + 5952,5948,5944,5940,5936,5932,5928,5924,5920,5916,5912,5908,5904,5900,5896,5892, + 5888,5884,5880,5876,5872,5868,5864,5860,5856,5852,5848,5844,5840,5836,5832,5828, + 5824,5820,5816,5812,5808,5804,5800,5796,5792,5788,5784,5780,5776,5772,5768,5764, + 5760,5756,5752,5748,5744,5740,5736,5732,5728,5724,5720,5716,5712,5708,5704,5700, + 5696,5692,5688,5684,5680,5676,5672,5668,5664,5660,5656,5652,5648,5644,5640,5636, + 5632,5628,5624,5620,5616,5612,5608,5604,5600,5596,5592,5588,5584,5580,5576,5572, + 5568,5564,5560,5556,5552,5548,5544,5540,5536,5532,5528,5524,5520,5516,5512,5508, + 5504,5500,5496,5492,5488,5484,5480,5476,5472,5468,5464,5460,5456,5452,5448,5444, + 5440,5436,5432,5428,5424,5420,5416,5412,5408,5404,5400,5396,5392,5388,5384,5380, + 5376,5372,5368,5364,5360,5356,5352,5348,5344,5340,5336,5332,5328,5324,5320,5316, + 5312,5308,5304,5300,5296,5292,5288,5284,5280,5276,5272,5268,5264,5260,5256,5252, + 5248,5244,5240,5236,5232,5228,5224,5220,5216,5212,5208,5204,5200,5196,5192,5188, + 5184,5180,5176,5172,5168,5164,5160,5156,5152,5148,5144,5140,5136,5132,5128,5124, + 5120,5116,5112,5108,5104,5100,5096,5092,5088,5084,5080,5076,5072,5068,5064,5060, + 5056,5052,5048,5044,5040,5036,5032,5028,5024,5020,5016,5012,5008,5004,5000,4996, + 4992,4988,4984,4980,4976,4972,4968,4964,4960,4956,4952,4948,4944,4940,4936,4932, + 4928,4924,4920,4916,4912,4908,4904,4900,4896,4892,4888,4884,4880,4876,4872,4868, + 4864,4860,4856,4852,4848,4844,4840,4836,4832,4828,4824,4820,4816,4812,4808,4804, + 4800,4796,4792,4788,4784,4780,4776,4772,4768,4764,4760,4756,4752,4748,4744,4740, + 4736,4732,4728,4724,4720,4716,4712,4708,4704,4700,4696,4692,4688,4684,4680,4676, + 4672,4668,4664,4660,4656,4652,4648,4644,4640,4636,4632,4628,4624,4620,4616,4612, + 4608,4604,4600,4596,4592,4588,4584,4580,4576,4572,4568,4564,4560,4556,4552,4548, + 4544,4540,4536,4532,4528,4524,4520,4516,4512,4508,4504,4500,4496,4492,4488,4484, + 4480,4476,4472,4468,4464,4460,4456,4452,4448,4444,4440,4436,4432,4428,4424,4420, + 4416,4412,4408,4404,4400,4396,4392,4388,4384,4380,4376,4372,4368,4364,4360,4356, + 4352,4348,4344,4340,4336,4332,4328,4324,4320,4316,4312,4308,4304,4300,4296,4292, + 4288,4284,4280,4276,4272,4268,4264,4260,4256,4252,4248,4244,4240,4236,4232,4228, + 4224,4220,4216,4212,4208,4204,4200,4196,4192,4188,4184,4180,4176,4172,4168,4164, + 4160,4156,4152,4148,4144,4140,4136,4132,4128,4124,4120,4116,4112,4108,4104,4100, + 4096,4092,4088,4084,4080,4076,4072,4068,4064,4060,4056,4052,4048,4044,4040,4036, + 4032,4028,4024,4020,4016,4012,4008,4004,4000,3996,3992,3988,3984,3980,3976,3972, + 3968,3964,3960,3956,3952,3948,3944,3940,3936,3932,3928,3924,3920,3916,3912,3908, + 3904,3900,3896,3892,3888,3884,3880,3876,3872,3868,3864,3860,3856,3852,3848,3844, + 3840,3836,3832,3828,3824,3820,3816,3812,3808,3804,3800,3796,3792,3788,3784,3780, + 3776,3772,3768,3764,3760,3756,3752,3748,3744,3740,3736,3732,3728,3724,3720,3716, + 3712,3708,3704,3700,3696,3692,3688,3684,3680,3676,3672,3668,3664,3660,3656,3652, + 3648,3644,3640,3636,3632,3628,3624,3620,3616,3612,3608,3604,3600,3596,3592,3588, + 3584,3580,3576,3572,3568,3564,3560,3556,3552,3548,3544,3540,3536,3532,3528,3524, + 3520,3516,3512,3508,3504,3500,3496,3492,3488,3484,3480,3476,3472,3468,3464,3460, + 3456,3452,3448,3444,3440,3436,3432,3428,3424,3420,3416,3412,3408,3404,3400,3396, + 3392,3388,3384,3380,3376,3372,3368,3364,3360,3356,3352,3348,3344,3340,3336,3332, + 3328,3324,3320,3316,3312,3308,3304,3300,3296,3292,3288,3284,3280,3276,3272,3268, + 3264,3260,3256,3252,3248,3244,3240,3236,3232,3228,3224,3220,3216,3212,3208,3204, + 3200,3196,3192,3188,3184,3180,3176,3172,3168,3164,3160,3156,3152,3148,3144,3140, + 3136,3132,3128,3124,3120,3116,3112,3108,3104,3100,3096,3092,3088,3084,3080,3076, + 3072,3068,3064,3060,3056,3052,3048,3044,3040,3036,3032,3028,3024,3020,3016,3012, + 3008,3004,3000,2996,2992,2988,2984,2980,2976,2972,2968,2964,2960,2956,2952,2948, + 2944,2940,2936,2932,2928,2924,2920,2916,2912,2908,2904,2900,2896,2892,2888,2884, + 2880,2876,2872,2868,2864,2860,2856,2852,2848,2844,2840,2836,2832,2828,2824,2820, + 2816,2812,2808,2804,2800,2796,2792,2788,2784,2780,2776,2772,2768,2764,2760,2756, + 2752,2748,2744,2740,2736,2732,2728,2724,2720,2716,2712,2708,2704,2700,2696,2692, + 2688,2684,2680,2676,2672,2668,2664,2660,2656,2652,2648,2644,2640,2636,2632,2628, + 2624,2620,2616,2612,2608,2604,2600,2596,2592,2588,2584,2580,2576,2572,2568,2564, + 2560,2556,2552,2548,2544,2540,2536,2532,2528,2524,2520,2516,2512,2508,2504,2500, + 2496,2492,2488,2484,2480,2476,2472,2468,2464,2460,2456,2452,2448,2444,2440,2436, + 2432,2428,2424,2420,2416,2412,2408,2404,2400,2396,2392,2388,2384,2380,2376,2372, + 2368,2364,2360,2356,2352,2348,2344,2340,2336,2332,2328,2324,2320,2316,2312,2308, + 2304,2300,2296,2292,2288,2284,2280,2276,2272,2268,2264,2260,2256,2252,2248,2244, + 2240,2236,2232,2228,2224,2220,2216,2212,2208,2204,2200,2196,2192,2188,2184,2180, + 2176,2172,2168,2164,2160,2156,2152,2148,2144,2140,2136,2132,2128,2124,2120,2116, + 2112,2108,2104,2100,2096,2092,2088,2084,2080,2076,2072,2068,2064,2060,2056,2052, + 2048,2044,2040,2036,2032,2028,2024,2020,2016,2012,2008,2004,2000,1996,1992,1988, + 1984,1980,1976,1972,1968,1964,1960,1956,1952,1948,1944,1940,1936,1932,1928,1924, + 1920,1916,1912,1908,1904,1900,1896,1892,1888,1884,1880,1876,1872,1868,1864,1860, + 1856,1852,1848,1844,1840,1836,1832,1828,1824,1820,1816,1812,1808,1804,1800,1796, + 1792,1788,1784,1780,1776,1772,1768,1764,1760,1756,1752,1748,1744,1740,1736,1732, + 1728,1724,1720,1716,1712,1708,1704,1700,1696,1692,1688,1684,1680,1676,1672,1668, + 1664,1660,1656,1652,1648,1644,1640,1636,1632,1628,1624,1620,1616,1612,1608,1604, + 1600,1596,1592,1588,1584,1580,1576,1572,1568,1564,1560,1556,1552,1548,1544,1540, + 1536,1532,1528,1524,1520,1516,1512,1508,1504,1500,1496,1492,1488,1484,1480,1476, + 1472,1468,1464,1460,1456,1452,1448,1444,1440,1436,1432,1428,1424,1420,1416,1412, + 1408,1404,1400,1396,1392,1388,1384,1380,1376,1372,1368,1364,1360,1356,1352,1348, + 1344,1340,1336,1332,1328,1324,1320,1316,1312,1308,1304,1300,1296,1292,1288,1284, + 1280,1276,1272,1268,1264,1260,1256,1252,1248,1244,1240,1236,1232,1228,1224,1220, + 1216,1212,1208,1204,1200,1196,1192,1188,1184,1180,1176,1172,1168,1164,1160,1156, + 1152,1148,1144,1140,1136,1132,1128,1124,1120,1116,1112,1108,1104,1100,1096,1092, + 1088,1084,1080,1076,1072,1068,1064,1060,1056,1052,1048,1044,1040,1036,1032,1028, + 1024,1020,1016,1012,1008,1004,1000, 996, 992, 988, 984, 980, 976, 972, 968, 964, + 960, 956, 952, 948, 944, 940, 936, 932, 928, 924, 920, 916, 912, 908, 904, 900, + 896, 892, 888, 884, 880, 876, 872, 868, 864, 860, 856, 852, 848, 844, 840, 836, + 832, 828, 824, 820, 816, 812, 808, 804, 800, 796, 792, 788, 784, 780, 776, 772, + 768, 764, 760, 756, 752, 748, 744, 740, 736, 732, 728, 724, 720, 716, 712, 708, + 704, 700, 696, 692, 688, 684, 680, 676, 672, 668, 664, 660, 656, 652, 648, 644, + 640, 636, 632, 628, 624, 620, 616, 612, 608, 604, 600, 596, 592, 588, 584, 580, + 576, 572, 568, 564, 560, 556, 552, 548, 544, 540, 536, 532, 528, 524, 520, 516, + 512, 508, 504, 500, 496, 492, 488, 484, 480, 476, 472, 468, 464, 460, 456, 452, + 448, 444, 440, 436, 432, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, + 384, 380, 376, 372, 368, 364, 360, 356, 352, 348, 344, 340, 336, 332, 328, 324, + 320, 316, 312, 308, 304, 300, 296, 292, 288, 284, 280, 276, 272, 268, 264, 260, + 256, 252, 248, 244, 240, 236, 232, 228, 224, 220, 216, 212, 208, 204, 200, 196, + 192, 188, 184, 180, 176, 172, 168, 164, 160, 156, 152, 148, 144, 140, 136, 132, + 128, 124, 120, 116, 112, 108, 104, 100, 96, 92, 88, 84, 80, 76, 72, 68, + 64, 60, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4 +}; + +/* +** for (int32_t i = 0; i < 768; i++) +** logTab[i] = (int32_t)round(16777216.0 * exp2(i / 768.0)); +*/ +const int32_t logTab[768] = +{ + 16777216,16792365,16807527,16822704,16837894,16853097,16868315,16883546, + 16898791,16914049,16929322,16944608,16959908,16975222,16990549,17005891, + 17021246,17036615,17051999,17067396,17082806,17098231,17113670,17129123, + 17144589,17160070,17175564,17191073,17206595,17222132,17237683,17253247, + 17268826,17284419,17300026,17315646,17331282,17346931,17362594,17378271, + 17393963,17409669,17425389,17441123,17456871,17472634,17488410,17504202, + 17520007,17535826,17551660,17567508,17583371,17599248,17615139,17631044, + 17646964,17662898,17678847,17694810,17710787,17726779,17742785,17758806, + 17774841,17790891,17806955,17823034,17839127,17855235,17871357,17887494, + 17903645,17919811,17935992,17952187,17968397,17984621,18000860,18017114, + 18033382,18049665,18065963,18082276,18098603,18114945,18131302,18147673, + 18164060,18180461,18196877,18213307,18229753,18246213,18262689,18279179, + 18295684,18312204,18328739,18345288,18361853,18378433,18395028,18411637, + 18428262,18444902,18461556,18478226,18494911,18511611,18528325,18545056, + 18561801,18578561,18595336,18612127,18628932,18645753,18662589,18679441, + 18696307,18713189,18730086,18746998,18763925,18780868,18797826,18814800, + 18831788,18848792,18865812,18882846,18899897,18916962,18934043,18951139, + 18968251,18985378,19002521,19019679,19036853,19054042,19071247,19088467, + 19105703,19122954,19140221,19157504,19174802,19192116,19209445,19226790, + 19244151,19261527,19278919,19296327,19313750,19331190,19348645,19366115, + 19383602,19401104,19418622,19436156,19453706,19471271,19488853,19506450, + 19524063,19541692,19559337,19576998,19594675,19612368,19630077,19647802, + 19665543,19683300,19701072,19718861,19736666,19754488,19772325,19790178, + 19808047,19825933,19843835,19861752,19879686,19897637,19915603,19933586, + 19951585,19969600,19987631,20005679,20023743,20041823,20059920,20078033, + 20096162,20114308,20132470,20150648,20168843,20187054,20205282,20223526, + 20241787,20260064,20278358,20296668,20314995,20333338,20351698,20370074, + 20388467,20406877,20425303,20443746,20462206,20480682,20499175,20517684, + 20536211,20554754,20573313,20591890,20610483,20629093,20647720,20666364, + 20685025,20703702,20722396,20741107,20759835,20778580,20797342,20816121, + 20834917,20853729,20872559,20891406,20910270,20929150,20948048,20966963, + 20985895,21004844,21023810,21042794,21061794,21080812,21099846,21118898, + 21137968,21157054,21176158,21195278,21214417,21233572,21252745,21271935, + 21291142,21310367,21329609,21348868,21368145,21387439,21406751,21426080, + 21445426,21464790,21484172,21503571,21522987,21542421,21561873,21581342, + 21600829,21620333,21639855,21659395,21678952,21698527,21718119,21737729, + 21757357,21777003,21796666,21816348,21836046,21855763,21875498,21895250, + 21915020,21934808,21954614,21974438,21994279,22014139,22034016,22053912, + 22073825,22093757,22113706,22133674,22153659,22173663,22193684,22213724, + 22233781,22253857,22273951,22294063,22314194,22334342,22354509,22374693, + 22394897,22415118,22435357,22455615,22475891,22496186,22516499,22536830, + 22557179,22577547,22597933,22618338,22638761,22659202,22679662,22700141, + 22720638,22741153,22761687,22782240,22802811,22823400,22844009,22864635, + 22885281,22905945,22926628,22947329,22968049,22988788,23009546,23030322, + 23051117,23071931,23092764,23113615,23134485,23155374,23176282,23197209, + 23218155,23239120,23260103,23281106,23302127,23323168,23344227,23365306, + 23386403,23407520,23428656,23449810,23470984,23492177,23513389,23534620, + 23555871,23577140,23598429,23619737,23641065,23662411,23683777,23705162, + 23726566,23747990,23769433,23790896,23812377,23833879,23855399,23876939, + 23898499,23920078,23941676,23963294,23984932,24006589,24028265,24049962, + 24071677,24093413,24115168,24136942,24158736,24180550,24202384,24224237, + 24246111,24268003,24289916,24311848,24333801,24355773,24377765,24399776, + 24421808,24443859,24465931,24488022,24510133,24532265,24554416,24576587, + 24598778,24620990,24643221,24665472,24687744,24710036,24732347,24754679, + 24777031,24799403,24821796,24844209,24866641,24889095,24911568,24934062, + 24956576,24979110,25001665,25024240,25046835,25069451,25092088,25114744, + 25137421,25160119,25182837,25205576,25228335,25251115,25273915,25296736, + 25319578,25342440,25365322,25388226,25411150,25434095,25457060,25480047, + 25503054,25526081,25549130,25572199,25595290,25618401,25641533,25664686, + 25687859,25711054,25734270,25757506,25780764,25804042,25827342,25850662, + 25874004,25897367,25920751,25944156,25967582,25991029,26014497,26037987, + 26061498,26085030,26108583,26132158,26155754,26179371,26203009,26226669, + 26250350,26274053,26297777,26321522,26345289,26369077,26392887,26416718, + 26440571,26464445,26488341,26512259,26536198,26560158,26584141,26608145, + 26632170,26656218,26680287,26704377,26728490,26752624,26776780,26800958, + 26825158,26849380,26873623,26897888,26922176,26946485,26970816,26995169, + 27019544,27043941,27068360,27092802,27117265,27141750,27166258,27190787, + 27215339,27239913,27264509,27289127,27313768,27338430,27363116,27387823, + 27412552,27437304,27462079,27486875,27511695,27536536,27561400,27586286, + 27611195,27636126,27661080,27686057,27711056,27736077,27761121,27786188, + 27811277,27836389,27861524,27886681,27911861,27937064,27962290,27987538, + 28012809,28038103,28063420,28088760,28114122,28139508,28164916,28190347, + 28215802,28241279,28266779,28292302,28317849,28343418,28369011,28394626, + 28420265,28445927,28471612,28497320,28523052,28548806,28574584,28600385, + 28626210,28652058,28677929,28703823,28729741,28755683,28781647,28807636, + 28833647,28859682,28885741,28911823,28937929,28964058,28990211,29016388, + 29042588,29068811,29095059,29121330,29147625,29173944,29200286,29226652, + 29253042,29279456,29305894,29332355,29358841,29385350,29411883,29438441, + 29465022,29491627,29518256,29544910,29571587,29598288,29625014,29651764, + 29678538,29705336,29732158,29759004,29785875,29812770,29839689,29866633, + 29893600,29920593,29947609,29974650,30001716,30028805,30055920,30083059, + 30110222,30137410,30164622,30191859,30219120,30246407,30273717,30301053, + 30328413,30355798,30383207,30410642,30438101,30465584,30493093,30520627, + 30548185,30575768,30603377,30631010,30658668,30686351,30714059,30741792, + 30769550,30797333,30825141,30852975,30880833,30908717,30936625,30964559, + 30992519,31020503,31048513,31076548,31104608,31132694,31160805,31188941, + 31217103,31245290,31273503,31301741,31330005,31358294,31386609,31414949, + 31443315,31471707,31500124,31528567,31557035,31585529,31614049,31642595, + 31671166,31699764,31728387,31757036,31785710,31814411,31843138,31871890, + 31900669,31929473,31958304,31987160,32016043,32044951,32073886,32102847, + 32131834,32160847,32189887,32218952,32248044,32277162,32306307,32335478, + 32364675,32393898,32423148,32452424,32481727,32511056,32540412,32569794, + 32599202,32628638,32658099,32687588,32717103,32746645,32776213,32805808, + 32835430,32865078,32894754,32924456,32954184,32983940,33013723,33043532, + 33073369,33103232,33133122,33163040,33192984,33222955,33252954,33282979, + 33313032,33343112,33373219,33403353,33433514,33463703,33493919,33524162 +}; + +const char *MODSig[16] = +{ + "2CHN","M.K.","6CHN","8CHN","10CH","12CH","14CH","16CH", + "18CH","20CH","22CH","24CH","26CH","28CH","30CH","32CH" +}; + +const uint16_t amigaPeriod[96] = +{ + 6848,6464,6096,5760,5424,5120,4832,4560,4304,4064,3840,3624, + 3424,3232,3048,2880,2712,2560,2416,2280,2152,2032,1920,1812, + 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016, 960, 906, + 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, + 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, + 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113, + 107, 101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56, + 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28, +}; + +const uint8_t vibTab[32] = +{ + 0, 24, 49, 74, 97,120,141,161, + 180,197,212,224,235,244,250,253, + 255,253,250,244,235,224,212,197, + 180,161,141,120, 97, 74, 49, 24 +}; + +const int8_t vibSineTab[256] = +{ + 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 +}; + +const uint8_t arpTab[256] = +{ + 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0, + + /* 8bb: the following are overflown bytes from FT2's binary, read by the buggy + ** arpeggio routine. (values confirmed to be the same on FT2.08 and FT2.09) + */ + 0x00,0x18,0x31,0x4A,0x61,0x78,0x8D,0xA1,0xB4,0xC5,0xD4,0xE0,0xEB,0xF4,0xFA,0xFD, + 0xFF,0xFD,0xFA,0xF4,0xEB,0xE0,0xD4,0xC5,0xB4,0xA1,0x8D,0x78,0x61,0x4A,0x31,0x18, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x00,0x02,0x00,0x04,0x00,0x00, + 0x00,0x05,0x06,0x00,0x00,0x07,0x00,0x01,0x00,0x02,0x00,0x03,0x04,0x05,0x00,0x00, + 0x0B,0x00,0x0A,0x02,0x01,0x03,0x04,0x07,0x00,0x05,0x06,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x79,0x02,0x00,0x00,0x8F,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x4F,0x52,0x4D,0x49,0x4C,0x42,0x4D,0x42,0x4D +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win95kggui/dep/ft2play/tables.h Sun Oct 01 03:48:43 2023 -0400 @@ -0,0 +1,13 @@ +#pragma once + +#include <stdint.h> + +extern const uint32_t panningTab[257]; +extern const uint16_t amigaPeriods[1936]; +extern const uint16_t linearPeriods[1936]; +extern const int32_t logTab[768]; +extern const char *MODSig[16]; +extern const uint16_t amigaPeriod[96]; +extern const uint8_t vibTab[32]; +extern const int8_t vibSineTab[256]; +extern const uint8_t arpTab[256];