From 2f766553e8d26128dbe4609c45acab92439406a9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 13 Feb 2022 17:02:49 -0500 Subject: [PATCH] add SAASound as an alternate SAA1099 core DOES NOT WORK YET --- CMakeLists.txt | 11 + extern/SAASound/.gitignore | 15 + extern/SAASound/CMakeLists.txt | 102 +++ extern/SAASound/INSTALL | 21 + extern/SAASound/LICENCE | 31 + extern/SAASound/MODIFIED.md | 5 + extern/SAASound/README | 7 + extern/SAASound/README.MD | 14 + extern/SAASound/VS17/.gitignore | 16 + extern/SAASound/VS17/SAASound.sln | 43 + .../Profiles/x86/Release-SSE/SAASound!1.pgc | Bin 0 -> 13280 bytes .../Profiles/x86/Release-SSE/SAASound.pgd | Bin 0 -> 176128 bytes .../x86/ReleaseWithSymbols/SAASound!1.pgc | Bin 0 -> 13328 bytes .../x86/ReleaseWithSymbols/SAASound.pgd | Bin 0 -> 208896 bytes extern/SAASound/VS17/SAASound/SAASound.rc | Bin 0 -> 4996 bytes .../SAASound/VS17/SAASound/SAASound.vcxproj | 665 ++++++++++++++ .../VS17/SAASound/SAASound.vcxproj.filters | 141 +++ .../VS17/SAASound/SAASound.vcxproj.user | 8 + extern/SAASound/VS17/SAASound/resource.h | 14 + extern/SAASound/resources/SAASound.cfg | 42 + extern/SAASound/resources/SAASound.def | 52 ++ extern/SAASound/src/SAAAmp.cpp | 203 +++++ extern/SAASound/src/SAAAmp.h | 44 + extern/SAASound/src/SAAConfig.cpp | 113 +++ extern/SAASound/src/SAAConfig.h | 41 + extern/SAASound/src/SAADevice.cpp | 391 ++++++++ extern/SAASound/src/SAADevice.h | 68 ++ extern/SAASound/src/SAAEnv.cpp | 380 ++++++++ extern/SAASound/src/SAAEnv.h | 51 ++ extern/SAASound/src/SAAFreq.cpp | 280 ++++++ extern/SAASound/src/SAAFreq.dat | 141 +++ extern/SAASound/src/SAAFreq.h | 72 ++ extern/SAASound/src/SAAImpl.cpp | 484 ++++++++++ extern/SAASound/src/SAAImpl.h | 75 ++ extern/SAASound/src/SAANoise.cpp | 180 ++++ extern/SAASound/src/SAANoise.h | 54 ++ extern/SAASound/src/SAASndC.cpp | 95 ++ extern/SAASound/src/SAASndC.h | 102 +++ extern/SAASound/src/SAASound.cpp | 13 + extern/SAASound/src/SAASound.h | 129 +++ extern/SAASound/src/config.h.in | 14 + extern/SAASound/src/defns.h | 59 ++ extern/SAASound/src/minIni/minGlue-FatFs.h | 45 + extern/SAASound/src/minIni/minGlue-Linux.h | 51 ++ extern/SAASound/src/minIni/minGlue-ccs.h | 64 ++ extern/SAASound/src/minIni/minGlue-efsl.h | 63 ++ extern/SAASound/src/minIni/minGlue-ffs.h | 27 + extern/SAASound/src/minIni/minGlue-mdd.h | 58 ++ extern/SAASound/src/minIni/minGlue-stdio.h | 31 + extern/SAASound/src/minIni/minGlue.h | 34 + .../SAASound/src/minIni/minGlue_stdio_tchar.h | 31 + extern/SAASound/src/minIni/minIni.c | 862 ++++++++++++++++++ extern/SAASound/src/minIni/minIni.h | 228 +++++ extern/SAASound/src/resource.h | 15 + extern/SAASound/src/types.h | 34 + .../tests/SimCoupe dsk/testcases.dsk.gz | Bin 0 -> 20721 bytes extern/SAASound/tools/freqdat.py | 21 + extern/SAASound/tools/levels.py | 65 ++ src/engine/platform/saa.h | 10 + 59 files changed, 5815 insertions(+) create mode 100644 extern/SAASound/.gitignore create mode 100644 extern/SAASound/CMakeLists.txt create mode 100755 extern/SAASound/INSTALL create mode 100755 extern/SAASound/LICENCE create mode 100644 extern/SAASound/MODIFIED.md create mode 100755 extern/SAASound/README create mode 100755 extern/SAASound/README.MD create mode 100644 extern/SAASound/VS17/.gitignore create mode 100644 extern/SAASound/VS17/SAASound.sln create mode 100644 extern/SAASound/VS17/SAASound/Profiles/x86/Release-SSE/SAASound!1.pgc create mode 100644 extern/SAASound/VS17/SAASound/Profiles/x86/Release-SSE/SAASound.pgd create mode 100644 extern/SAASound/VS17/SAASound/Profiles/x86/ReleaseWithSymbols/SAASound!1.pgc create mode 100644 extern/SAASound/VS17/SAASound/Profiles/x86/ReleaseWithSymbols/SAASound.pgd create mode 100644 extern/SAASound/VS17/SAASound/SAASound.rc create mode 100644 extern/SAASound/VS17/SAASound/SAASound.vcxproj create mode 100644 extern/SAASound/VS17/SAASound/SAASound.vcxproj.filters create mode 100644 extern/SAASound/VS17/SAASound/SAASound.vcxproj.user create mode 100644 extern/SAASound/VS17/SAASound/resource.h create mode 100644 extern/SAASound/resources/SAASound.cfg create mode 100644 extern/SAASound/resources/SAASound.def create mode 100755 extern/SAASound/src/SAAAmp.cpp create mode 100755 extern/SAASound/src/SAAAmp.h create mode 100644 extern/SAASound/src/SAAConfig.cpp create mode 100644 extern/SAASound/src/SAAConfig.h create mode 100644 extern/SAASound/src/SAADevice.cpp create mode 100644 extern/SAASound/src/SAADevice.h create mode 100755 extern/SAASound/src/SAAEnv.cpp create mode 100755 extern/SAASound/src/SAAEnv.h create mode 100755 extern/SAASound/src/SAAFreq.cpp create mode 100755 extern/SAASound/src/SAAFreq.dat create mode 100755 extern/SAASound/src/SAAFreq.h create mode 100644 extern/SAASound/src/SAAImpl.cpp create mode 100755 extern/SAASound/src/SAAImpl.h create mode 100755 extern/SAASound/src/SAANoise.cpp create mode 100755 extern/SAASound/src/SAANoise.h create mode 100755 extern/SAASound/src/SAASndC.cpp create mode 100644 extern/SAASound/src/SAASndC.h create mode 100755 extern/SAASound/src/SAASound.cpp create mode 100644 extern/SAASound/src/SAASound.h create mode 100644 extern/SAASound/src/config.h.in create mode 100644 extern/SAASound/src/defns.h create mode 100644 extern/SAASound/src/minIni/minGlue-FatFs.h create mode 100644 extern/SAASound/src/minIni/minGlue-Linux.h create mode 100644 extern/SAASound/src/minIni/minGlue-ccs.h create mode 100644 extern/SAASound/src/minIni/minGlue-efsl.h create mode 100644 extern/SAASound/src/minIni/minGlue-ffs.h create mode 100644 extern/SAASound/src/minIni/minGlue-mdd.h create mode 100644 extern/SAASound/src/minIni/minGlue-stdio.h create mode 100644 extern/SAASound/src/minIni/minGlue.h create mode 100644 extern/SAASound/src/minIni/minGlue_stdio_tchar.h create mode 100644 extern/SAASound/src/minIni/minIni.c create mode 100644 extern/SAASound/src/minIni/minIni.h create mode 100755 extern/SAASound/src/resource.h create mode 100755 extern/SAASound/src/types.h create mode 100644 extern/SAASound/tests/SimCoupe dsk/testcases.dsk.gz create mode 100644 extern/SAASound/tools/freqdat.py create mode 100644 extern/SAASound/tools/levels.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e466ddf..414c883c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,8 @@ else() set(SYSTEM_SDL_MIN_VER 2.0.0) endif() +list(APPEND DEPENDENCIES_INCLUDE_DIRS "extern/SAASound/include") + find_package(Threads REQUIRED) list(APPEND DEPENDENCIES_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) @@ -213,6 +215,15 @@ src/log.cpp src/fileutils.cpp src/utfutils.cpp +extern/SAASound/src/SAAAmp.cpp +extern/SAASound/src/SAADevice.cpp +extern/SAASound/src/SAAEnv.cpp +extern/SAASound/src/SAAFreq.cpp +extern/SAASound/src/SAAImpl.cpp +extern/SAASound/src/SAANoise.cpp +extern/SAASound/src/SAASndC.cpp +extern/SAASound/src/SAASound.cpp + extern/Nuked-OPN2/ym3438.c extern/opm/opm.c src/engine/platform/sound/sn76496.cpp diff --git a/extern/SAASound/.gitignore b/extern/SAASound/.gitignore new file mode 100644 index 00000000..55e4b391 --- /dev/null +++ b/extern/SAASound/.gitignore @@ -0,0 +1,15 @@ +.vs +out/ +CMakeSettings.json +Makefile +config.h +config.log +config.status +libtool +stamp-h1 +include/Makefile +src/.deps +src/.libs +src/*.o +src/*.lo +src/*.la diff --git a/extern/SAASound/CMakeLists.txt b/extern/SAASound/CMakeLists.txt new file mode 100644 index 00000000..a695b1d1 --- /dev/null +++ b/extern/SAASound/CMakeLists.txt @@ -0,0 +1,102 @@ +cmake_minimum_required(VERSION 3.10) + +if (APPLE) + set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE STRING "OSX Architectures") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9") + option(BUILD_FRAMEWORK "Build a Mac OS X framework instead of a shared library" ON) +endif () + +project(SAASound) + +if (MSVC) + add_definitions(-DUNICODE -D_UNICODE) +endif () + +set(EXTERNAL_CLK_HZ "8000000" CACHE STRING "External clock in Hz") +option(SAAFREQ_FIXED_CLOCKRATE "Use precomputed frequency table, external clock is locked" OFF) +set(SAMPLE_RATE_HZ "44100" CACHE STRING "Output sample rate in Hz") +set(DEFAULT_OVERSAMPLE "6" CACHE STRING "Default oversample factor") +set(DEFAULT_UNBOOSTED_MULTIPLIER "11.3" CACHE STRING "Default scaling factor (before volume boost), not configurable in SAAConfig.cfg") +set(DEFAULT_BOOST "1" CACHE STRING "Default volume boost multiplier, configurable in SAAConfig.cfg") +option(DEBUGSAA "Create a text log of all register:value writes as well as a raw PCM output file" OFF) +set(DEBUG_SAA_REGISTER_LOG "debugsaa.txt" CACHE STRING "Path to register log file (if DEBUGSAA)") +set(DEBUG_SAA_PCM_LOG "debugsaa.pcm" CACHE STRING "Path to raw PCM output file (if DEBUGSAA)") +option(USE_CONFIG_FILE "Compile support for loading a config file on startup (SAAsound.cfg)" ON) +set(CONFIG_FILE_PATH "SAASound.cfg" CACHE STRING "Path to config file (if USE_CONFIG_FILE)") + +#include(CheckStructHasMember) +#CHECK_STRUCT_HAS_MEMBER("struct dirent" d_type dirent.h HAVE_STRUCT_DIRENT_D_TYPE LANGUAGE CXX) + +if (MSVC) + set(RESOURCES resources/SAASound.def) +endif () + +add_library(SAASound SHARED) +set(API_HEADERS include/SAASound.h) +target_sources(SAASound + PRIVATE + src/defns.h + src/SAAAmp.cpp + src/SAAAmp.h + src/SAADevice.cpp + src/SAADevice.h + src/SAAEnv.cpp + src/SAAEnv.h + src/SAAFreq.cpp + src/SAAFreq.h + src/SAAImpl.cpp + src/SAAImpl.h + src/SAANoise.cpp + src/SAANoise.h + src/SAASndC.cpp + src/SAASound.cpp + src/types.h + src/SAASndC.h + ${RESOURCES} + ${API_HEADERS} +) + +if (USE_CONFIG_FILE) +target_sources(SAASound + PRIVATE + src/SAAConfig.cpp + src/SAAConfig.h + src/minIni/minIni.h + src/minIni/minIni.c + src/minIni/minGlue.h +) +endif () + +set_target_properties(SAASound PROPERTIES + VERSION 3.5 + SOVERSION 3 + CXX_STANDARD 11) + +target_include_directories(SAASound PRIVATE + ${CMAKE_BINARY_DIR} + PUBLIC + include) + +if (APPLE AND BUILD_FRAMEWORK) + set_target_properties(SAASound PROPERTIES + OUTPUT_NAME SAASound + FRAMEWORK TRUE + FRAMEWORK_VERSION C + MACOSX_FRAMEWORK_IDENTIFIER com.beermex) + set_source_files_properties(${API_HEADERS} PROPERTIES + MACOSX_PACKAGE_LOCATION Headers) +endif () + +configure_file(src/config.h.in ${CMAKE_BINARY_DIR}/saasound_cmake_config.h) +target_compile_definitions(SAASound PRIVATE HAVE_CONFIG_H=1) + +install(TARGETS SAASound + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + FRAMEWORK DESTINATION lib + PUBLIC_HEADER DESTINATION include) + +if (NOT APPLE OR NOT BUILD_FRAMEWORK) + install(FILES ${API_HEADERS} DESTINATION include) +endif () diff --git a/extern/SAASound/INSTALL b/extern/SAASound/INSTALL new file mode 100755 index 00000000..0344f460 --- /dev/null +++ b/extern/SAASound/INSTALL @@ -0,0 +1,21 @@ +SAASound build and install notes +-------------------------------- + +Win32: + - Open VS17/SAASound.sln in Visual Studio 2017 + - Select and build the Release target + - Copy the resulting SAASound.dll from the Release folder + to Windows\System (or WinNT\System32 on NT/W2K/XP) + or alternatively simply copy it directly to the application folder + that requires it (e.g. for SimCoupe and SAAPlay, copy the + SAASound.dll to the same folder as the executable application) + +Linux/Unix: + - To build: cmake . ; make + - To install: sudo make install + +-- + +Last updated 15th August 2018 +by Dave Hooper +and Simon Owen diff --git a/extern/SAASound/LICENCE b/extern/SAASound/LICENCE new file mode 100755 index 00000000..d9a50047 --- /dev/null +++ b/extern/SAASound/LICENCE @@ -0,0 +1,31 @@ +SAASound - a portable Phillips SAA 1099 sound chip emulator +----------------------------------------------------------- + +Copyright (c) 1998-2004, Dave Hooper +Copyright (c) 2004-2018, Dave Hooper + Simon Owen +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +- Neither the name Dave Hooper 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 OWNER 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. diff --git a/extern/SAASound/MODIFIED.md b/extern/SAASound/MODIFIED.md new file mode 100644 index 00000000..57c03c25 --- /dev/null +++ b/extern/SAASound/MODIFIED.md @@ -0,0 +1,5 @@ +# modified version + +this is a modified version of SAASound for Furnace. + +it fixes some warnings and doesn't use its CMakeLists because I need to static-link it and disable config file support. diff --git a/extern/SAASound/README b/extern/SAASound/README new file mode 100755 index 00000000..2e3ed21f --- /dev/null +++ b/extern/SAASound/README @@ -0,0 +1,7 @@ +S A A S O U N D +=============== + +SAASound is a portable Phillips SAA 1099 sound chip emulator, written in C++. + +Email: Dave Hooper +Homepage: https://github.com/stripwax/SAASound diff --git a/extern/SAASound/README.MD b/extern/SAASound/README.MD new file mode 100755 index 00000000..eabac2dc --- /dev/null +++ b/extern/SAASound/README.MD @@ -0,0 +1,14 @@ +SAA SOUND is a software emulation library for the Philips SAA-1099 sound chip device (popular in computing and gaming devices in the late 1980's / early 1990's). Development of SAA SOUND began around 1996 with completion around 1999 and active development ending around 2001 (aside from an update in 2004 to address an issue with frequency generator SYNC behaviour that was exploited to generate digital audio in a demo from Fred 59 magazine, which was failing to be emulated correctly!) + +Simon Owen (author of SimCoupe) made additional infrastructure changes to support compilation on a variety of different platforms. The latest releases of SAASound.dll continue to be compatible with the released version (1.0) of SimCoupe . + +After a considerable time, the original author revisited the project in August 2018 following a conversation about emulation accuracy, which he intends to continue to improve following some recent reverse-engineering efforts of original hardware by the DosBox team, in conjunction with some crowd-sourced test cases comparing emulated behaviour to real original devices. + +It has been incorporated into standalone chiptune players, computer hardware emulators (e.g. SimCoupe), and has even been the basis of SAA-1099 hardware emulators implemented in FPGA devices (such as MiST / MiSTer) + +Moved permanently to: + https://github.com/stripwax/SAASound/ + +To use with SimCoupe (https://github.com/simonowen/simcoupe) : +* If using version 1.0 of SimCoupe, or any version from 20200117 and newer, simply copy the SAASound.dll from this repository, over the top of the SAASound.dll in your installed version of SimCoupe +* If using version 1.1 Alpha of SimCoupe, this has an embedded version of SAASound built-in. This means you cannot replace the SAA Sound version. You should probably upgrade to a more recent build of SimCoupe diff --git a/extern/SAASound/VS17/.gitignore b/extern/SAASound/VS17/.gitignore new file mode 100644 index 00000000..82638471 --- /dev/null +++ b/extern/SAASound/VS17/.gitignore @@ -0,0 +1,16 @@ +Debug/* +DebugWithSymbols/* +Release/* +Release-SSE/* +ReleaseWithSymbols/* +x64/* +unused/* +Backup*/* +.vs/* +SAASound/Debug/* +SAASound/DebugWithSymbols/* +SAASound/Release/* +SAASound/Release-SSE/* +SAASound/ReleaseWithSymbols/* +SAASound/x64/* +SAASound/*.aps \ No newline at end of file diff --git a/extern/SAASound/VS17/SAASound.sln b/extern/SAASound/VS17/SAASound.sln new file mode 100644 index 00000000..0e78a897 --- /dev/null +++ b/extern/SAASound/VS17/SAASound.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30517.126 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SAASound", "SAASound\SAASound.vcxproj", "{EDEC064F-E383-4824-8892-EF4B47C9841E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + Release-SSE|x64 = Release-SSE|x64 + Release-SSE|x86 = Release-SSE|x86 + ReleaseWithSymbols|x64 = ReleaseWithSymbols|x64 + ReleaseWithSymbols|x86 = ReleaseWithSymbols|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EDEC064F-E383-4824-8892-EF4B47C9841E}.Debug|x64.ActiveCfg = Debug|x64 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.Debug|x64.Build.0 = Debug|x64 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.Debug|x86.ActiveCfg = Debug|Win32 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.Debug|x86.Build.0 = Debug|Win32 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.Release|x64.ActiveCfg = Release|x64 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.Release|x64.Build.0 = Release|x64 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.Release|x86.ActiveCfg = Release|Win32 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.Release|x86.Build.0 = Release|Win32 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.Release-SSE|x64.ActiveCfg = Release-SSE|x64 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.Release-SSE|x64.Build.0 = Release-SSE|x64 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.Release-SSE|x86.ActiveCfg = Release-SSE|Win32 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.Release-SSE|x86.Build.0 = Release-SSE|Win32 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.ReleaseWithSymbols|x64.ActiveCfg = ReleaseWithSymbols|x64 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.ReleaseWithSymbols|x64.Build.0 = ReleaseWithSymbols|x64 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.ReleaseWithSymbols|x86.ActiveCfg = ReleaseWithSymbols|Win32 + {EDEC064F-E383-4824-8892-EF4B47C9841E}.ReleaseWithSymbols|x86.Build.0 = ReleaseWithSymbols|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9ED9C3AD-01B4-4AE1-8DD3-2524D0A05E5C} + EndGlobalSection +EndGlobal diff --git a/extern/SAASound/VS17/SAASound/Profiles/x86/Release-SSE/SAASound!1.pgc b/extern/SAASound/VS17/SAASound/Profiles/x86/Release-SSE/SAASound!1.pgc new file mode 100644 index 0000000000000000000000000000000000000000..b3b000be03ddd56f0b6674c66b3ac2bacff8848b GIT binary patch literal 13280 zcmd^Fe~4UH6~3D$?UJN*nrdCO#&M15nra-kP=eNWjBPqpn=BS(=^yE&AS|thF@J<* zv1Xco90P{2YFMg}G0=A1rgf}P7C{~RM;(Kti%3`s${^dalp;gGun2YizWeSs@6Fx! z=0|1&1uwig_nmXTbIv{QzB~8bdk;NuXyX0Gm|M3zGu61$nC!P7e*EIEFAaSayz|?Q zp=|s6jJX^1eq2@5e+2p|TuV0_lUkVi*^95gaNyj}=T4mX&Xw<)p&=N7FuNH)>r0)i zRKPFpFlIN@`z?2ze4e4KD-&bJls>kOZ5jDbuIHCOv!2&4r+`G%J`s-^-W~AM3u+FQ2>}?cCeH-N}UKK}+RNced9+AL_R( zBF=7Q_T$|&`jb!h;IqCfw3+I$ujfWXOvb}hV){TOqEBQ!9pZbfl{;Vxcpg&;?{qWL zzFvJvZ@Rp*C8g^h|&>YMtqF-j^;bWo5NU9^z@_llj@mFl4L!<5;F#_*J|ff zz4xVhhcTIi=h2Sr^R7c~R^2$7WO34qF-b4VPj|sIdw9R1_BNtE3!kgr`_zlwkq+Y> z@naF26W%+Wk9nuN`AqkK$zU$AktNl`xSZ|qPWKW^!OP7)yweS@uh$~E?8PQ^vfg{$ z^!{6YJ5Bt*Z**x!a9%I&RP%su3wwf zFLlTb2-ALmj>osoat)p&y zYw_LvkxhX=M@&oZv$HV zjgbIfvvCCo>g5p!?NkvLxmD=PPM{UWxefBATkyXG%5PQ5Yq)1zTyr4h_b4g9SA^e! z8pn9uGjIH+=lvY;Tb)FHMYllC?}QKs1`7H7<|kz-+_yYa#g)B(d8URdbzpfWkE?>K zi7WfS@=O`mBCb>p7j#p1FVCF48G83D&urcSUC;=HF0ok?6Ahora4feD50FNx;5UP>_YG0!fjYb zfL_#}?DR@u=QMSToQl%l7&10|0?w=9zGKPe#&df_>+{gMEytb`fE$_|IYy3 zju#K{KgKnwGTM+3zx|)7W3JOcj1j-1BbX1c4A`H}P)Lo5yCf53R&E~8y6n#Zvod7l4SaS?OADZVKB$>*mK!!WJ{`ESP{%O%Sn zq(7Df{DnZ8C~tjyB;ZfY!8c0@RB?+U{>KApl6?Mk$=mz+{8{k_%m4g|4YISgL3Zr4 zk+ueG*B>@0oiiDSx9-^fb9Q{yy{L47%eB@g z@m*oAoVWTrPyF4wc<3i?g5&^C-14U7|Md8uEC={-%5-)3E80)sca8j;+s-+UjvU=; z;}R2ok8{6;dqZ|F^i&WxzamD!gZvElg7-nkuBAD-@TY$lylkS!{!+H~*|^4N_`Mg6 zV{)&xdzkxGTo>*$-siXuUkBEYw;sg1n|p6uFN=%!#Zy7I5Pf)UwiRy{-6L&(EvuKjXtJT_i0uQb5|oSjc{pUjVUI&I|_wWk>u@4tT$gdw_Z<(I8^=Mv&= z1o5_3Z(`%M(c#C78}VM2C>k$UA;!=K$d5@L7w_nwgY^p0f7G6D#k&%XH_|T)L4SBd zcKeL?5#gfo(&Wii;$2(|q@tNqFSO##VV%L=P$d#?DTufAKN1&j@tu;~T3USS zsUcR7zf%(U@s>ude5>UB##>E@w-Us=K)W^g;JTB>qc2|In{NHqZ3RDZ^5CgrSUD=K zfTBH@4E(Zi&n-r7zk;|v&2=crU)mI4@{T%U_8biO`h|TvRCCXl>pzm;_mDIK3ca^< zeuRFc_XE$5eF%Vjzm??{{vh`fxYt6w&Y*>1_7T7EVcVrULdb`lBb2axeaG^6CDe`B#2+z?pBY5F@rL>#H{M@%My!v5n z=cr{B-26)YrC2^JRWqB3?7Mj1b+Qxlz4w~VNORxSpI6KH-{jCS3b~-7`Gj!;59U7y zc$Si9tC^QKAC!guDDyE18_J7sN8w}-tw_Z>z;l`6nQNXEr~g)b{kgY(mm>asJ80)C zWA1D@`;X+Ja;JNr0)N+(!w(u~5&2dUmLh({ z7ar}|uR5Qj+AE$t71nbm`7Wn_6?s+ana`0~y JU*}tR|33xvmAwD} literal 0 HcmV?d00001 diff --git a/extern/SAASound/VS17/SAASound/Profiles/x86/Release-SSE/SAASound.pgd b/extern/SAASound/VS17/SAASound/Profiles/x86/Release-SSE/SAASound.pgd new file mode 100644 index 0000000000000000000000000000000000000000..c043f617f9fa7dad9189b24acdb68082dd9e59f4 GIT binary patch literal 176128 zcmeHw4Vab1mF9QpZvF%X6$KS-R1~5>w_pSlrJJS&BL58vPCIbvzCaJny=~vy2pK2a z$!Jy+P0Sc~%oyj6qqk5jY%bNe%xXFBy!vHrm(^c1v!P+^ z=u;N9C2-@CWEd{z!M{ctZn#|RuOVOv7y^cXAz%m?0)~JgU<+l{I zww^gFnE=Gkf#>Op+GGFeo#Sw&s|D(eyAJ+X_{%|0!u56d6X8D$dLphf;77sJvGumW zkA>d@e**m1;Bj~(83E4|8&lv%!hZ*TJp69>y8)No(MQt`dGKC}R<&RCA#inxCRy%T;K zJhD1@XW@D#JXf-rm3o{Ce;Rx({OR!Lz;nf#tK3|n=4v)qq`3;sm2R#|bA_C%*<8uy zsyF8?xO&bNYOZ3}!%u5wM7M!ulD|sij>#fbxmE@&>2^4jh>pZ zhPuYCYxVIOy{`T18ollf&^3A8GZ5|xu(f%jZq5)e1PlQ~zz{G53;{#H5HJJ`0Yg9# z;8*F7;D64~*udR{5AK8g{^!>%<@39n-~EGq>rY1f!G7yQggHaN5HJJ`0YktLFa!(% zL%*EBUXT;9w$5^J)Xx+|hq zHY1WI0lM;CrI|SNo^05#DYvC7zbxCk4Hg0*WXblPZWl9ub60mM*IVc-ZTNWOhVIuY`iF? zdUM^mY%w>ZSj>@`-QMFS1(HKtZf9;sS4U2Yc9lEC<>&G{-j{lB#_WwN_z>5(OJ{!$ zak=?IS21knL*37c`OXf9_oQ!qYM=MjhNWv-7HvrHH47O6hJYbp2p9r}fFWQA7y^cX zA@E)zzz_dn*qvYN&av}vCV2vT|1-4y_YO;D3QbkxLin)*!jpW*!g2)9p{R~5M?L$_zT1a25s6GISm1XWXe5<{O27A&)Xg;9tn2eF*8v2q450Gh>}C4ALobEQ2-NzdM^bX-gxNkm57c zcbASe)k~}K^-b%dnq+`v+6F`=_`L7)m&>`7rS zVJa2KHELT}bLlsZ38?Cc*B@z?cg< z)vzNz1K~OhwUOUN*&A23qttZoxC}I*ZgBk~ThL+pgB@iCYqP=X!f{9R-CL)01l18q z?g7z#;Qr2d@yEK>XN9%C#Es>RgFPLNHS8;Fh2!zh>*3o=PU{gbJ^{2$wk2sztG`d9 zc5P08eIDAdVTnvjekz-HxX2|>TEEV<yl| z({%h({nSF!dC7TY{m45X|5Ua&8s)}u8c>>nf1!S~`h*`&^6BfG&a;!*;L@difo&$< zOZIV0mBy9t!hUJv^!~0x&8WZj_iE47RL+F5{vt>EGD|)c7NJD{v|ULP-_vmpR&bVk zb4qfSznAz#r&X-8@t1DzeoU_9P34kKYfDXduMA6WV#8``pF$)uby|zTM!VxG(BM0t zxN3;&h`RR(H5~d#?@sTqvADApX(FTJfz~?JkGk$Hn|WA|;po%s?Qs24MtTRGRKB~U zWtitQ^l~k?8rosG9FfC!!YK~X_>G$*i?#`K(`lR3y0#!k#!tS-iQBtvdDr=N+^b)5 z9F#W*dFy+NH-sxC#*KWfL#+R(0ran|W%X1Upj_)D^KT~kgR-o!Hx2e&Wt_Co{=GZ% z8%OYvcju61OOh76(|6|)=xNdx!eh@k2JgkvLxjf`I1X5gF7obtGHk?o>^)z`IF5mQ zH<9s|qXX~XDLzfLHXN)kjDakUz|w~69fG`5B{UYAOK3c5CZP#PE1^lKgM=od4`p25 zDUc>1*7;cclRAiAc^K>J_-HN1CyGoD6dfP=9iR6jrQqq|_>kxLd`d7qo(PT)xsK1yPd-3! ze3aw(9G%FA4M4|7d5%wefaKGZA?tr+0&O|R~qVJKDp&jK+ zLr%(>hI+7Hoez}su0^{$T){5O<#!SFp{L`snBmiqi~7+`b$>_Eeq=onJQr6@&shhC zm6lO0q$QqD7s_M0Y5qDLSGMnmJdKMRyH(zKo_~H2&$t=zCqv#$TqzO#v|O*a-;Kkl z#XRGH56kBhlsJt+h&YV_?!<{{==91E!zrD9`6@26T#iwksi-F|jks!h&b}}_%GY}g z;#scF0UhgFTI62FLaTASn1xp4sH^&EyDx_v%3bchFY)V`(2#Ol98yVgz3)HQLq6qde!bSBREGIX zqMUv4`OIRP5zt1{Ul`)q zdL4zUimdIxV8H0W&Vl_ z;^h9(r#9~|=BveD&9?~jX^_9zT}LHLa2xr2a~fA@$@R^CspL|_XxS(ISlKR#F^`F>NMX@FzA*38RT45b8;rcxgPOc*BnS6 z*7;WNRn7}l^kKb6?nBP~h^JRMd#cFc+Ar5@2h-Q~4kqV0#M7&s zSE|V2TB0~2EGQEbI?X3kO1}y@VI0F;s}+~Z;XVzzFl|V`PJLFnB<>H>-Bc!1IkE$G zU^#D8(dQn>k^M64jj|>!wG%n4<7?ile%vi~6&72={__c>;ciXhta7-2hK~8zCmd`L zu5#XnoQokOl=FEclYKg=oKPQ@F%ArRmBT$tu|8bW=FZQ7+8tTkVs(8Wrw+`=L5KSM z66v5OR8Pk--CfG^DQ7P7(5pV&@pjd=2hRn|Ir6VWh-nAYXE8F+tDLb_^l1>0_$&C3 z+*dyw4>?b$GDuJPb3n6w1{yc0>qexZSA8ac8IK#!ARYFd03Q}WFZ+&oWj^-FonX_e zoJo+wu%wpoQ%J{sRiPZVH67DZPCqixlO}(16*)f!pJ@kbFUH*n8T2Y=3hVXH96!a8 z|F#cHog`*oI8XKZXjaO(SqUdyh&cR^Dgw6S49r@4v9P4_*-)HsbU_IhTqGQr)y>eCR z_2)=85q^Yws>+@zz6aAW-`TiMgbaF>(*-%7d-iL6d>^DEpU*FGDL(sZukBUp#XV=T zZ?$^88W6t{y3ngW*Hoz&_85AX>a#DL`>N=}hctg9d<0In@co(Z(v(QY^5@{X5xUT; zKD!|2KVLHM??Zf+BAx8JZAM5WaGxz5^D+Kz$e>p_-19zT_w1L$^0=3i{cfQCOgZ;M z2EEGpB;>^JQAMO<`8B@Xk-VZ_D?vXBCcVnJ0dk&r<^Hq6{;>e*P7ma$u3))*>u}u- z8T2aW7RdS7*C(D9${B%-*k6o#@vT05%1gd>a9@TDdX@79*6UF{eJT8_k**$oAUPWm z_ap_ z_f?Ve_eeJtKCIUm&~(gK!gVoZ(5svWf_kyc*yD~LC#|2xc^+Tq-5V|A`Toy$Q$B|| zR(LrXju`Zz+>nmLkTs67G<}$#VZ>*&tT0W$&!`T(m!`l^_41HEGmx&7O?i4{I#0X$ zzXkdh_-Ek10RJrf&*1mKzXJa{Jo47iGoO}2T|m3fAM(r@0)~JgUXp!4Jc1MYVNz z)@vhlq1U{RLC&wf_q9hbHRT-noY$ESKg`c8q*_V7orv2B8T2aW+mQ3$?%J_2l#@d` z?ztH5W=&GNC}6pK*Wr32WYDXeCm`o@fA`yqZeBkobipjSCh zLJsHm!*rZeJs*Ccc__+x95U!t&eM?dL|5fJ9c9dbuW>U%vXdk%=T^k+h75X@^9<5e>&wVMPn!H^At#>4yc!wVhlcx^DV^hCdgl8w16(MC zS2;g{oL{|k`NR_>sOpbB;We z^E~LgA%kA!yaYKv|Mj`w4A;*ZkZz94n<5;_c@uFjLI%Ccd8JCdxK4Q?ytX^3g_JOK>c|n;wM4|y~=q5az1p@ z$$gFAvyODk#e4StaU>RXKlrtAH z=vB_!khA2K`#uJibL4j;L^}fm^+OKATakfYhc=uNItoXDA0OXJ1`A%kA!OhBf$esIzshU5G!q??aC1Iby4 z_%|VgUgb=xBIm1+(F9-To<8!tP@R}>F^F*(u;^9JWXKseuInoh?Hu_X2sOhG)b8sb zXD(#WtDGs26OTWifDGPa;qx7&bIeCMO^`vaa;8#F9jq4x>A0_@1$15TJPrFZuTNBd1@I|+jJNj2=QCcoL5)F3oWAKXEqS!Aj)yBvg{+EQtBVIxikW)f@=Cej3 zlQP5_Bm({9(J@}%8RS~|T`uX=UMg+FlEYnxndFmBFMYEJOF5CEgHni9WhqI&VoGfE z?MF3`x>JPaqb*LsoIKTvJZdBFSG9N(&jfX+v{WmW;_{VC7Hl#J=U}uP!JH+%8+)=N z-Trh4=@-pXTtiFYT2u102IP%ISssD1Zd%*WGCuna`Q&MS@$D zre)k1w4j!yJ(l0b{4QbZY72}3O}@4`5h0H@@wBz@D(M{euv6AJ^mxsG9BA?_85py%rQqH;`(qGx&&62&+KRZnYtCJ~H@lND(i-DkSsdjg#HZ3Lj>q>53GqFHbJtwi816%~ zHY&Wy=(!S_0-Yp8Tc#444qcvhd1s(EN~jU~O6WrLLl! zz&nKZwA?8ieJP*m8Be?AQXVb13oU&hy~8JCC$shewj7_R_c_ccO@1FidN|)FUv)7| zcMev1G#~3S3DgZPmNobg{z*yfA6z{-&(rBxsVLG`vPhC%!I|A^PfGm-7uK}_DP*i6 zt}KZ zLEc1o^0+^jywl+M)=ho*me2Z8+eZB5y(aIo)cjbK6UOpgP-3b3c$5~#@{LMj*&9wo znPDs~n@cR~eloNQV`=SNVu{8ncv}i%X(w1>iP0HJeMKy-qJPP?4w2*YI?PK;=@Lsk zr=o|1u~UJ%#1iFcc*=&c)6o_ZE4}6Xir5)IPh#0BGx01B^ELumiDg^O#>$4gzX;A3 zqBU=KI?n@NLL}#5MmYEAWI4<>k<;a!y_sT1`UR(RvcP27OIJ zeRz&aXcz8m39)akPkzVsZ|#5VryG)ut{*Dz20Ur^xH8y#JCj^R>`iEKiDjSOhJWnW zaQ(N))%APc18H2wog?A>K%R_6b&l5!Ys^B+es7B4{lfDirQ}oSTkM~8$?XVz!RhoR zya`F@E+8YJd(g`ybRXXMB=i7!mxLZfFXMUIBzYL)k%U;rr<4C!*8Leq!||Tv8|G*5 zPxX415pZX~^SnnIm%*JOeDZi#Q`dU@{S2&1dtBW9b;g_tKL!4?_$RV6sFm=^)BNOp z7EegczXvqkRQR*uZ^l2(e~#yqr}@dd8N28;|0|&Broo>Je+&L;{^_1ip5`a-7Q9_) z{?|d%od^FRczGuyC*#(!$q(Pg&Uf_Z%lY48Qn;D$GvGgue_H-5&nHjIC-3vgZ9c!; zM-AZJhJWJfTsz&F-09_{PZP=zImh9?WDWTJH7t3{^&P%hJn2Gwvq+!jOs6^dB$fA3 zK8-kba%7g#n`x>&VfaqTl|M^BD4LJ^CHID-o##gKix)9$}$kFsX;U*U12TsqlX&L+;cCKG^kou9w zcShEM()o;F-9}^VlqY!lg~Bv^#&O+Gu;WuK%*k-@`&IoM`RRDy6A{J7IHE%Y zWzHv^dEm2*&n1>i&G_3)qW#_o)&S%1or9~T)!sSy%O$L};P!@VX(FGdPcbGU>~nP5 zI_2*QuZbYRTGe8-ywufI*3pR76TYwFm*0F}tx34P$TxsmAL8>{czjk%h|gsS@fj>3 z_SRE@fb=OrfV!n^tiPHix{kwBPx^b(uWim&psvSbWIkDH&`)IPD2qCColt5u72^z_ z&*U@qJnWs|v!1xHR7#tRe^QEQL>}YJ83KlYAz%m?0)~JgUzw~2P&=>8TozryN#`k#85cKqmm;%VA@>Fk3sY6gZ2aI z{u(sPqa8Q8XFW}Ob#%KuO?!HDf8}Y~#iRS5p!wa&9R&y`&*0DBIUc@1p(A-e!k_;I z&tIRST{Ztb0XlKBFHG~7`18C=gMErFUnY4P^b7FA+$AM04(jGd2Ge z`TrRnE#~Iyb%%q@-s?Jx%(2ODn?e;g6Q_Gigwii|p*T+>?o3>U`LAb*W2xd+1v(MM zC!J2ildnRb-#DFQzE5V8m}d&Sd}k}gdtDskcMyH^Yozav%Qu*7H8;)7=aDZO5xit`20eb)R-=GRapE|L^d1n18tt z{IqrZq8yJz^n;+8{%+)^#LBzqE+6}+K9+OFsqCNm*e@cMqZnrs)7X1lEanM)>|>t( zBS()&Uhug;VuW@_2QxsnHDN0B&u4;-` ze2M~6OmHbiyZziMgL8zO&OvnYhS#qX#3@d{8mw`I^&yq^O_>p!mRyMRHCSE2uv=aO zlvT9p1k<@@{N?IlN}XFC4Hg@78$v9ahVBA9ZGu^zOQd8WxKx#Jp z>8@rw2W;z)y62SShZwiDTSJZZ$;XCb%^Z zrA~CK97>(w);&l~g+Jb{LMS!ftve_+&aFf!bzD-&6(R#=4P?)H(2D+-ieT z$GEiwrAE8lO1a;0CE@aobgKqRjd1G-qzg>|fj&q?GT;qEdA#fS8{Oh~7mxQdzH)wOT)B-eLfmn_Rb}p0 zQXK1BGvtjew<2@#_oL~;6vz1XQXbb+M*DW+JwuJ^;K?85+laHv zIR9|p9%24E-wv985oFeu@h4SN#*w{sSo!n?sZ5!g>gH-Q$r~?=-Q z96QCu;>qq~ANFI(KY~8Tx7WuIPUSu8S}&9Q!sV#Nie?zgl?u)a|24SNT>jCneTlI+ zwr@?jecyKF)g(`YJI|#*&h=wrB901{*DaFEK*`Qh#&Q*cn(>6;0+)Mavf8E4+&ovr zG!MGJNwNypbS&m+A;#WWHI_5(lzuB>mqI%C9JRTWDxI_Be1BL1?v*aZsN@QS)3+8< zBm8`M;^Kyv`z7P*%6BxWT89+^E@h27^+t-24|C52r7~{RCdHFDq9^m$Bv+Ox^353n zhQND^z*yX#{NCp`ejUR6mgo07zuEbn&Tn&mkMkRx-`)Inoq%+s;2(gW0zV0UGW>M- zX81<ElUk%TNA&p-JvX8kM)b;vZjb1#5nYPt{)oOgqVI_4dn5Xxi2hbY zKNZn`6VWe5^sgfN&4{jT3cy5gTtuH1(bFP&PDD3F^wNl47tx&&-4oH*Mf8mk{l$pB zJEFfH(T_&-_ai!q-0mp;`G|fwqJI<7Zuv4v-I(Sy%+SP$K0BftBKo5dy*Q%RMD)gp z?vCi45q*6`-x|?hj_CU%`Wq4bctrmwqMwWCUq25q(EQ-y6|!`9>Q8hJYbp2p9r}fFWQA7y^cXAz%m? z0)~JgU(?(;i)YMhM!a z+v>x#Rrin&)27{+SU6 zsR%#r!?d$EQhr3sS!G}8!?e41hYw$f@QXe?58-JeMdl?4_xLbv^*!pttq7~BV%qIn zJWA3pL-;KD-v;`q7eRJ;Gak_-ce7^5HDPZ~AZ!;U@kFCtMf8H~R2)gm?S! zH3(0XpXW;Y5Z>s+yAZzLhiNbHH6Q*Y!gKvkwB3NP{#+aF3O?oeUqE=A`~;Y@BEQau zzXbl>K71F#FZ=L42sfaO>Fz^#rw>1X@Z&!GAi^Wz=^n;)r4K)X>m5G)7_Kk+@V9ZD z23v-7Paxdm!%rgos1HBQw8y(7&T9Q)AAT16TYdN^2tVh;KSQ|Qn}~h^;jKQr2jPc& z_$7qj^x;TtB@xXy=ZpZ0DarrpPveVDc&8@$nKTBzOW!?XkWxEiw- zarV~`Fa!(%L%~KJBh`&Ri2i)dLF7GB@j6=d=Dhn*xXcjA846v7QU>SU2XmaeAIN0~i5n;n`D+g_ z-(WJsxQE|k`C(Z@8Rj~qx@Qh?9VQ&&^0c0IJ@g2yKf>CY>wX90xQ1)45`pF;02A(& z;9BtE;J6k()Q)Tahsr$?JX^vtFWgTZxSz$`p+kUsJGf6_s9fKEw)6g!$GuQP>A2_Q z{dU}!awNNDL!|#rLnLP?v3zWZ``iBL_6LLD@a=u*!~Ie3x8r_7a|e$A_vC$0-0e|a zAB*S%#o>V}eK9vgvJX_J4^lqvgWO-&XzjAUP#yN^>JG9k?>or+?-m2%4wT>pEHu=`~4 zKCpguAMuB8FFO1+TlF4c`}BJxH$;@@1Ut>a z^6VkT@e95@+AxXTC5KoSEkCraLf%gfP!HO8F!%l-Ksy@mmz#Qk{bhgjr5%vajd~Bu z3d{K*hi80%F@Adp>rPuWgSmMJQP!k`$UB%$2O3A4Sx1B0ewg8UG+;VV9cZ)3oFTBk z2zXO{k865wnyyU$jzpB{ha{<}bj<$pJd|7*~p{8yv+v9RYD=AQ{V)bFy0 z&P8-T=urNhphJBhj?(`X=&-yuL5FTQZqTs3&Hx>zpBvHZBl_c@!~S(U=uqBwL5J=4 zcTxOk*rE*cPXQg~zZ7(c&y`X9r$LAH{~GA9yzfQvFM$r_odMgHVSSf@4$IGj4)Ok6 z6#r;M?}^f9U>7sYe{w{BD594{^j6Se{XQGf_ka%de-w16|DGs)26i{Y_|rj$c+By3 zIkk^8gAUtgd6d5x<^OUN|GkK&-OsQ-lVRgC)JMy;t074ObRZXm~xc;V@e7DCdw{tX)@8@S6&G&G8jyXru?O`0hS@0?2+__O4zc=t% z;oPZF9N*==8Te#6;#58p^SexCdQ<&mJI8Mf{C93L{;JNNWfw!h5HJJ`0YktLFa!(% zL%YdYcnqTGXRnuv_rq?+Uo%2w>=8LZyUl-MT zbVR3ki+)-zwmMYko5~Zu#`|P;F$4?&L% zO~03PT-@GQ%=JP>HO^M$VBkmFg*cXl{M zKe{B}+0~otDAl)QySpzW1NH;aDYHnS(xaq27{`|&D zZqqt2H!x3HQe_Is*RnO6-;!IJ+mY)I z-(|SVo0=5*)wyh^@1HK#6`Qe0|Y8uHmIOcqZyl?hR=NrYdnKs?uLXCpJ5q z)OWKo)5(>7TdrD#imns2Zr+TWwrfYO8J)hX)YmCp7!+!|pmp)$pa-|+ceq~dB(Lt; zvbB`O}V*h8yIqHr!P9Sl4sb^mc98;@dK~ zOZ;7}&z5X?5HHuVLKa7XRG*+|=n~wb6?uGLi^k(szSpw`(G#tD(d*pc!j2aW(m}gR z8?EW;*yeDI>bZBP-7Bk%J=#<7Fmgk!Z?FZ9P@zJ(KI+C1H-ML$RCIvIDfjfIruB;| zpX)@5LYwoQEBZ=3eWf;_mMb_o%Vjh-FKJ-stJHj;3Wi9Ot643vz%fwiAuy^y#S3P+ zRw+z)Vq*wxY^SGeDY8=4~Ek5fH@B>^I$0tL-Rl-4?(Kqfo?h; zELX=vw>-Ga!>c?{%R`wws2e&S4Cdiu9ysNpZypTh;bI=R({C zM9qWCG$z0U%RB_ngYrDQ%LBVS7B$ zwMj9_CB69D4&R*=l53$X!-b>|{0_vn!oX&|RRfO|r>O#I_~P_}Aug8Rf$WIGkwV`A*GOP!QX7MJ*iJFgUoJ}Q$%+@?Yfs&V!Uj$ZE&TM;9ct13e;8O4d2u+P#kLki+MC!{9S?y+`C8W=Wy_jts_f*y|hd!mcFGGEn zCrhAfE7GQ|6Z6+19b1YR?tt%3E=kUHXf8od*nxhs4cuPv>e2hUkh;U&Nk!nzl$%}8 zCHCi*%iazi`{`zI*~>1*bvycA4mzrSE0cxEf~ZE1QFsU*b4Rk>_y4og+_*J9}vh zMkylQin_}nniI&y2pXY)co4)Pgypf$S^{gC~W?~8n6 zyBu^0p?3eyq-gRDYNqpzcx#%Cce)E4-R{Qi6m5B4=5dd7BNfw#gd-pE9q#Rw@1P}j z?{XA4u(}Ne=6sVbp_SzQnynz51J&vMe>>NnrZwupH$}nKGOg45^5JEL8Ulv_0!#4y z|3ypyyaPvd{_&lU|M8C>{%PiR&sP;^zr4}$AB^}5j>|g!4m&Z3Jw54@*8xwhk6jG` zL%tS%$x~B0!4TBzu2x(Gdpve zlV_{ouIF+X#j`j(yTbDv>Ug$>j%RYXiNOEJq&u_2Gc!Dw!ZRhmK$z!dc;17K8!5sc zkmQ*Vp7-F!2s)k**PpPKRf7cm{_*w8-;JbUb%M$FneW zJR8GvLh5)we6ijHTdc-CnF;&@Jp=Y)99 zhi7_t4v1%UxPgS{f#`VdijL=yc!o+HH;3>%51pQ25=#E^oRKD zvqIc7!i^w2L&Y;hbUf$9GfO;=rA~iPF+RVg@jRc!vr2kqNclVirjBQ}xS1w&Ja_dy zl+CkJJZHtTO#5;?<3-0aXgov3^JP4z2X8+IZ|9Sg@e1A9le;h61r;C06JHw7<|F0;v zK0||ZX8#Y1ct^DTKP;EN+eOUP-pF}dTWh+8ncLZu#1Z+<7WYLJ@tqOQY7tA+VtAB+ z^Rd;^<5^yfEgiI43|h*u-|IDEE*`I5ql?7v>2manr3;rgFKcZZtgtJ)@|}fii}jax zm5NwCud<51-L0W-gHh^S_v{M>lQm@P=sP-k`|_o(?YY59tI+&F_y2PRlKcPfKIv9> z4j;jz(s4dyR!?C|Vb`a9 zFTaEJVCT3gU;;wd!DIb6c^&>l_!;niO*`Rw@ius_>F$B&DU7edbApCzv|NLp0?!us z4*Yod-SEf5-v-aJD3fWYqr8)God?e~;H%-Wz6(oVNy4?=QxJa=u5{0@nhnaFAz%m? z0)~JgU zE@XaThH(!fPInxi8^t}0IKQhnIW>xV1aXu_HyvK(lYT6aNnJwsZN%ZfbCdB`btXf< z+$+D@w|5pR868_H=^Y*TvARmuyZQc?xq&U+g-zLR{=8v+OL3N;A@IyuZXTdKM{w*v z4LC<|IiQ_{n&@1?he5NIY|en~Yt9fb1PlQ~zz{G53;{#H5HJJ`0YhN_5cuNE#>}=$ z?%BZ8+VwyBlOJF4m77{WmHqu6ob|+C7O%pW=Bzw^guUp$UAN-bt#iGeDDK)`%5L&R zPj6vU&WG^>?!CKwczdC{8A(v9R2H5^T` z-HYo(3$RliTiCZE?%zP~gy+t7?gyZ}v*6E!=SnuSQV;G2I1Qe25vRj*b)0(uxHF#X z``iJ~eFfYXTX~?1PlQ~zz{G5 z3;{#H5HJJ`0YktLFa$n`2u%ARR@&-p2p9r}fFWQA7y^cXAz%m?0)~JgUgG%709Om literal 0 HcmV?d00001 diff --git a/extern/SAASound/VS17/SAASound/Profiles/x86/ReleaseWithSymbols/SAASound!1.pgc b/extern/SAASound/VS17/SAASound/Profiles/x86/ReleaseWithSymbols/SAASound!1.pgc new file mode 100644 index 0000000000000000000000000000000000000000..e8f0e256cd6b922d7a8a283f4933480929b69e88 GIT binary patch literal 13328 zcmd^FeTZCF6@Q!NW7(u;y4t$+BTmyc-KLsO6OC!D=@@M~h$f4F$PzS;f)J|6TA{29 zwbOufsaU2d*0mrVifNangt1@=^&?|p!w|GC3h7c12SHhDjZ4L3Db)D;-FtuY?%sWG zJ~A6oyzu6{d(QctbMAfj-nm}~?>RW~jwp)G-FxAI%mv|^oguzV|=G?2coXt71S1&kG?K;O5Y_MXe7NmlYJ31i?&t>v6*&|j*z z;6Ebedn`wGnQKuuOKu!Zvp5>Wn5O6Rvu!Yo9rQ2X-fFy0!RJ!YpE|KS)MC6Ne%P}) z67=a}LZ5EuGur{CxT~F&?&O&c#^rpAKHZ78v7m1Dpieh~wqEnb9J<4%%I}HmN_^qHZXk z584cQtC<#qHqby@zZ_{Hn{G9Sx-GRCyVBAByOKT^y6AsfNy5044{2K5$6TM0eyAtD zsPxY{#+smhq!-dOyRs+wsUiFJ(?9f!WvKYenE!zc%TaIhAHXoESH(K+-JnCk+-;4K zM$zS?QN+1Dzr`d1=nMQQuWv#<{(t)U`nxb*M{6Omb~ult9@k47Ko_vNz;lL{V@4? zv{kk{kN?alcwF6F6Gqtq=0{^Un?p!YVu zPxtQc+#+l+wW-gq4PpGuU)yu}PFQcr{!~tsZ^x4RAMdd~f1Bh_ek6mx&lE78PWE2D zV@1SWL0#$phCbvEeg;dB*B~#s|0Bgf3i%ri@R?HZ_eH>S4VQv{p4cpOd^~wfu)mXg zA$iDC@GnuX65ze~_fcL;#~-Df$@lN!^;ehX|Bn{E94{W?{}T74$|yrh{N{hAjyX&N zF?xQt4&ncvsmJ#8bP;yudT%F(Y&1Q`^J;{4JBG2R$8x#a>DkVc!-%DU`tLEG*UBgt zljdoB_rB+w3oc^LJH;18JGtWsV(7<{B7bohvO>E2lk~@wf4Ud#yB0KJy#Nm$&nor^N5A{%0OsB|8^a$&Q&e(pHb6~5h=$h2pIugD_`RR!Ej%0I`lxo)#?4j45O|P3 z!L#5!(Bb+@a$^iX^Ix%ls-g?mWKFs0q_H(#hxu`g?l5)_^Sp}t!d=Gu4EN#d!20oW zbzHacZsXY-_sf#v{mV}_LoUj2rR3Az2vj zFvAF&X1Q)=WV=COP&<({O_z( zbm7GAX1oRLGnfojynZxk<8A(rB*k00fCZzM@((@K$1Lja5wXK~C$}2;faKlATTO|# zV&gqYyEXWz;L&*W%^U3ZLR@wEO_H;JF`f9lrTPW#Yy`hdJabEsn^h3^r@0R$`ORxA z=5MA};(C5%ujT6(_RYY_GhgoiNWO8U-k)`Ugnp!Vb>PRjiyn?gTjL6T4)832XD!4_ z{tpLGoNHZR-?PJr!sK@yREG7CAU{Sq?-J0y)`R4?i|Huz z;N2$rk>1~(pVu9Aq$w8$_%nL<@hZRY(0faI+>g^<@sGAyuJ)jHl%(S1T({Wnz>2Hj z_}GdxN%b)4`0)1t1-uKy{Xp5b+VStOdzC!r;=#P{EJeH@zB4HpQH|qV5Z;edafnJs-z6IL$8D|yxl6?fSM4qSEw@4UajeRjz`yA__L^X$!+dvTA_^jNxl-nm|e|9M=lp(MHI zf2x%nQz4V|-KP-;6Q*z`sXXQaA~+~-k#}!+Z;R&#k_!)cgXj26NpdVrO%iU@=>li? z=6+wY-4YdeS18G@>Qi>BvE4jzOi4KStr=m8{8}f}I8%3aRq68A?*=N^tMa}E?|JaN zL~?x(QMcvSlow)rj%WRqHh65Qnk4&^F@DjBTKdjKMBk<8@SB3TreeEuysyB!OWe1TU&T5Q;O1i7h;)*0{Q<7u z4e+Rk=aO)PuA{6EaQPVL-j>HX;BcB(DE{#n_j-~);Z#q;Nf&W`RquZJe(ji$o2JS; zEIbdC9k0JXu?T(!XLKp@`*V;nrO3~Zw3hF-=v4d*ZRGnYFT=+eo`m>M3!fsNBo2~KjO<6%Ck@J{EjNGcp;Ts6!_<*!#KqkuN{>>>BBkz literal 0 HcmV?d00001 diff --git a/extern/SAASound/VS17/SAASound/Profiles/x86/ReleaseWithSymbols/SAASound.pgd b/extern/SAASound/VS17/SAASound/Profiles/x86/ReleaseWithSymbols/SAASound.pgd new file mode 100644 index 0000000000000000000000000000000000000000..6501b518bf659282406b738c8e70ab6532e2136f GIT binary patch literal 208896 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 literal 0 HcmV?d00001 diff --git a/extern/SAASound/VS17/SAASound/SAASound.vcxproj b/extern/SAASound/VS17/SAASound/SAASound.vcxproj new file mode 100644 index 00000000..cffbde92 --- /dev/null +++ b/extern/SAASound/VS17/SAASound/SAASound.vcxproj @@ -0,0 +1,665 @@ + + + + + DebugWithSymbols + Win32 + + + DebugWithSymbols + x64 + + + Debug + Win32 + + + Release-SSE + Win32 + + + Release-SSE + x64 + + + ReleaseWithSymbols + Win32 + + + ReleaseWithSymbols + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 15.0 + {EDEC064F-E383-4824-8892-EF4B47C9841E} + Win32Proj + SAASound + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\..\include;$(IncludePath) + + + true + $(SolutionDir)\..\include;$(IncludePath) + + + false + $(SolutionDir)\..\include;$(IncludePath) + NativeRecommendedRules.ruleset + false + + + false + $(SolutionDir)\..\include;$(IncludePath) + NativeRecommendedRules.ruleset + false + + + false + $(SolutionDir)\..\include;$(IncludePath) + NativeRecommendedRules.ruleset + false + + + false + $(SolutionDir)\..\include;$(IncludePath) + NativeRecommendedRules.ruleset + false + + + false + $(SolutionDir)\..\include;$(IncludePath) + + + false + $(SolutionDir)\..\include;$(IncludePath) + + + false + $(SolutionDir)\..\include;$(IncludePath) + + + false + $(SolutionDir)\..\include;$(IncludePath) + + + + NotUsing + Level3 + Disabled + true + WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + false + false + Guard + false + false + false + NoExtensions + false + false + NoListing + ProgramDatabase + false + EnableFastChecks + true + Strict + true + CompileAsCpp + + + Windows + DebugFull + $(SolutionDir)..\resources\SAASound.def + + false + false + UseLinkTimeCodeGeneration + true + true + true + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + false + false + Guard + false + false + false + false + false + false + NoExtensions + NoListing + ProgramDatabase + true + EnableFastChecks + true + Strict + true + CompileAsCpp + + + Windows + DebugFull + + $(SolutionDir)..\resources\SAASound.def + false + false + UseLinkTimeCodeGeneration + true + true + true + + + + + NotUsing + Level3 + MaxSpeed + false + true + WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + MultiThreadedDLL + Speed + NoExtensions + false + None + true + false + true + false + Fast + AnySuitable + true + false + false + false + false + AssemblyAndSourceCode + + + Windows + true + true + false + false + $(SolutionDir)..\resources\SAASound.def + + PGOptimization + $(ProjectDir)Profiles\$(PlatformTarget)\$(ConfigurationName)\$(TargetName).pgd + + + + + NotUsing + Level3 + MaxSpeed + false + true + WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + MultiThreadedDLL + Speed + NoExtensions + false + ProgramDatabase + true + false + true + false + Fast + AnySuitable + true + false + false + true + AssemblyAndSourceCode + true + false + false + + + Windows + true + true + DebugFull + true + $(SolutionDir)..\resources\SAASound.def + + + true + PGOptimization + $(ProjectDir)Profiles\$(PlatformTarget)\$(ConfigurationName)\$(TargetName).pgd + + + + + NotUsing + Level3 + Disabled + false + true + WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + MultiThreadedDebugDLL + Speed + AdvancedVectorExtensions512 + false + ProgramDatabase + true + false + false + true + Fast + Disabled + false + false + false + true + AssemblyAndSourceCode + true + Guard + false + true + false + true + EnableFastChecks + + + Windows + false + false + DebugFull + true + $(SolutionDir)..\resources\SAASound.def + + + true + PGInstrument + + + + + NotUsing + Level3 + MaxSpeed + false + true + WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + MultiThreadedDLL + Speed + AdvancedVectorExtensions512 + false + None + true + false + true + false + Fast + AnySuitable + false + false + true + false + false + AssemblyAndSourceCode + + + Windows + true + true + false + false + $(SolutionDir)..\resources\SAASound.def + + + PGOptimization + $(ProjectDir)Profiles\$(PlatformTarget)\$(ConfigurationName)\$(TargetName).pgd + + + + + NotUsing + Level3 + MaxSpeed + false + true + + + WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + None + NoExtensions + Fast + true + false + false + AnySuitable + Speed + true + MultiThreadedDLL + false + false + false + false + AssemblyAndSourceCode + + + Windows + true + true + false + $(SolutionDir)..\resources\SAASound.def + + PGOptimization + $(ProjectDir)Profiles\$(PlatformTarget)\$(ConfigurationName)\$(TargetName).pgd + + + + + NotUsing + Level3 + MaxSpeed + false + true + + + WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + ProgramDatabase + AdvancedVectorExtensions512 + Fast + true + false + false + AnySuitable + Speed + true + MultiThreadedDLL + false + false + true + AssemblyAndSourceCode + true + true + false + false + + + Windows + true + true + DebugFull + $(SolutionDir)..\resources\SAASound.def + + + PGOptimization + true + true + $(ProjectDir)Profiles\$(PlatformTarget)\$(ConfigurationName)\$(TargetName).pgd + + + + + NotUsing + Level3 + Disabled + false + true + + + WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + ProgramDatabase + AdvancedVectorExtensions512 + Fast + true + true + false + Disabled + Speed + true + MultiThreadedDebugDLL + false + false + true + AssemblyAndSourceCode + true + false + Guard + false + true + false + true + + + Windows + false + false + DebugFull + $(SolutionDir)..\resources\SAASound.def + + + PGInstrument + true + true + + + + + NotUsing + Level3 + MaxSpeed + false + true + + + WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + None + AdvancedVectorExtensions512 + Fast + AnySuitable + Speed + true + true + MultiThreadedDLL + false + false + false + false + false + false + AssemblyAndSourceCode + + + Windows + true + true + false + + $(SolutionDir)..\resources\SAASound.def + PGOptimization + $(ProjectDir)Profiles\$(PlatformTarget)\$(ConfigurationName)\$(TargetName).pgd + + + + + + \ No newline at end of file diff --git a/extern/SAASound/VS17/SAASound/SAASound.vcxproj.filters b/extern/SAASound/VS17/SAASound/SAASound.vcxproj.filters new file mode 100644 index 00000000..1cc16689 --- /dev/null +++ b/extern/SAASound/VS17/SAASound/SAASound.vcxproj.filters @@ -0,0 +1,141 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {b1405cde-b230-4c13-915e-159136ba7d5a} + + + {3a21d264-bd0a-4975-b6ca-7aacd2d92334} + + + {a8d6ad5a-de03-49c0-ade1-f28bb9655645} + + + {b0edb4ea-6878-4f31-9442-ad3542ff27c5} + + + + + C++ Header Files + + + C++ Header Files + + + C++ Header Files + + + C++ Header Files + + + C++ Header Files + + + C++ Header Files + + + C++ Header Files + + + C++ Header Files + + + C++ Header Files + + + C++ Header Files + + + C++ Header Files + + + C++ Header Files\minIni + + + C++ Header Files\minIni + + + C++ Header Files\minIni + + + C++ Header Files\minIni + + + C++ Header Files\minIni + + + C++ Header Files\minIni + + + C++ Header Files\minIni + + + C++ Header Files\minIni + + + C++ Header Files\minIni + + + C++ Header Files + + + C++ Header Files\minIni + + + + + C++ Source Code + + + C++ Source Code + + + C++ Source Code + + + C++ Source Code + + + C++ Source Code + + + C++ Source Code + + + C++ Source Code + + + C++ Source Code\minIni + + + C++ Source Code + + + + + + + + Data + + + Resources + + + Resources + + + + + Resources + + + \ No newline at end of file diff --git a/extern/SAASound/VS17/SAASound/SAASound.vcxproj.user b/extern/SAASound/VS17/SAASound/SAASound.vcxproj.user new file mode 100644 index 00000000..2207c430 --- /dev/null +++ b/extern/SAASound/VS17/SAASound/SAASound.vcxproj.user @@ -0,0 +1,8 @@ + + + + $(TargetDir)\SimCoupe.exe + WindowsLocalDebugger + $(TargetDir) + + \ No newline at end of file diff --git a/extern/SAASound/VS17/SAASound/resource.h b/extern/SAASound/VS17/SAASound/resource.h new file mode 100644 index 00000000..05f4c5f7 --- /dev/null +++ b/extern/SAASound/VS17/SAASound/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by SAASound.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/extern/SAASound/resources/SAASound.cfg b/extern/SAASound/resources/SAASound.cfg new file mode 100644 index 00000000..e3b221b6 --- /dev/null +++ b/extern/SAASound/resources/SAASound.cfg @@ -0,0 +1,42 @@ +# Example SAASound.cfg file +# Only UTF-8 is supported +# Comments describe each parameter +# Commented-out parameter value shows the default value, uncomment to change + + +[Debug] + +# set WriteRegisterLog to True or 1 to enable dump of +# all SAA register changes to RegisterLogPath +#WriteRegisterLog=False +#RegisterLogPath="debugsaa.txt" + +# set WritePCMOutput to True or 1 to enable dump of generated +# audio as raw stereo signed 16-bit values to PCMOutputPath +# +# set PCMSeparateChannels to True or 1 to create 7 output files in total, one for the mixed audio +# and one for each of the 6 audio channels. Files are named automatically by appending digit 0-5 +# to the specified PCMOutputPath +#WritePCMOutput=False +#PCMOutputPath="debugsaa.pcm" +#PCMSeparateChannels=0 + +[Quality] + +# set Oversample to a value >=2 to enable multiple passes of higher-resolution internal calculations +# to help remove aliasing. It's not perfect but Oversample=6 should provide mostly-distortion-free results. +# Set to lower values, or 1 (or 0) to disable, to minimize CPU time spent, for lower-powered systems. +#Oversample=6 + +# set Highpass to True or 1 to enable a small highpass filter (experimental) to remove DC bias, +# which can manifest as pops and clicks when changing speaker volume. +# There is a small computation overhead but it's very small and probably worth enabling anyway. +#Highpass=True + +# set Boost to a number > 1 to boost the output volume. set to 1 or lower to disable boost. +# With Boost=1 (boost disabled), the output volume might be quite quiet overall in some cases but it also +# guarantees there will be no output clipping when all sound generators are running at maxmimum volume. +# Enabling boost can cause saturation (clipping distortion) at louder volumes. +# This value accepts decimal values e.g. Boost=1.414 +#Boost=1 + diff --git a/extern/SAASound/resources/SAASound.def b/extern/SAASound/resources/SAASound.def new file mode 100644 index 00000000..fcc5acfd --- /dev/null +++ b/extern/SAASound/resources/SAASound.def @@ -0,0 +1,52 @@ +; Part of SAASound copyright 1998-2018 Dave Hooper +; +; This is the current .DEF file for the SAASound.dll +; These export ordinals WILL NOT change in future releases +; +; Version 3.2.1 (14th August 2018) +; (c) 1998-2018 dave @ spc + +VERSION 3.2 + +EXPORTS + newSAASND @1 + deleteSAASND @2 + + SAASNDSetSoundParameters @5 + SAASNDWriteAddress @6 + SAASNDWriteData @7 + SAASNDWriteAddressData @8 + SAASNDClear @9 +; SAASNDReadAddress @10 + SAASNDGetCurrentSoundParameters @11 + SAASNDGetCurrentBytesPerSample @12 + SAASNDGetBytesPerSample @13 + SAASNDGetCurrentSampleRate @14 + SAASNDGetSampleRate @15 +; SAASNDGenerate @16 + SAASNDGenerateMany @17 +; SAASNDSendCommand @18 +; SAASNDClickClick @19 + SAASNDSetClockRate @20 + SAASNDSetSampleRate @21 + + CreateCSAASound @25 + DestroyCSAASound @26 + + SetSoundParameters @30 + WriteAddress @31 + WriteData @32 + WriteAddressData @33 + Clear @34 +; ReadAddress @35 + GetCurrentSoundParameters @36 + GetCurrentSampleRate @37 + GetSampleRate @38 + GetCurrentBytesPerSample @39 + GetBytesPerSample @40 +; Generate @41 + GenerateMany @42 +; SendCommand @43 +; ClickClick @44 + SetClockRate @45 + SetSampleRate @46 diff --git a/extern/SAASound/src/SAAAmp.cpp b/extern/SAASound/src/SAAAmp.cpp new file mode 100755 index 00000000..8f2473fb --- /dev/null +++ b/extern/SAASound/src/SAAAmp.cpp @@ -0,0 +1,203 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// SAAAmp.cpp: implementation of the CSAAAmp class. +// This class handles Tone/Noise mixing, Envelope application and +// amplification. +// +////////////////////////////////////////////////////////////////////// + +#include "SAASound.h" +#include "types.h" +#include "SAANoise.h" +#include "SAAEnv.h" +#include "SAAFreq.h" +#include "SAAAmp.h" +#include "defns.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CSAAAmp::CSAAAmp(CSAAFreq * const ToneGenerator, const CSAANoise * const NoiseGenerator, const CSAAEnv * const EnvGenerator) +: +m_pcConnectedToneGenerator(ToneGenerator), +m_pcConnectedNoiseGenerator(NoiseGenerator), +m_pcConnectedEnvGenerator(EnvGenerator), +m_bUseEnvelope(EnvGenerator != NULL) +{ + leftlevel = 0; + leftlevela0x0e = 0; + rightlevel = 0; + rightlevela0x0e = 0; + m_nMixMode = 0; + m_bMute=true; + m_bSync = false; + m_nOutputIntermediate=0; + last_level_byte=0; + SetAmpLevel(0x00); + +} + +CSAAAmp::~CSAAAmp() +{ + // Nothing to do +} + +void CSAAAmp::SetAmpLevel(BYTE level_byte) +{ + // if level unchanged since last call then do nothing + if (level_byte != last_level_byte) + { + last_level_byte = level_byte; + leftlevel = level_byte & 0x0f; + leftlevela0x0e = leftlevel & 0x0e; + + rightlevel = (level_byte >> 4) & 0x0f; + rightlevela0x0e = rightlevel & 0x0e; + } + +} + +void CSAAAmp::SetToneMixer(BYTE bEnabled) +{ + if (bEnabled == 0) + { + // clear mixer bit + m_nMixMode &= ~(0x01); + } + else + { + // set mixer bit + m_nMixMode |= 0x01; + } +} + +void CSAAAmp::SetNoiseMixer(BYTE bEnabled) +{ + if (bEnabled == 0) + { + m_nMixMode &= ~(0x02); + } + else + { + m_nMixMode |= 0x02; + } +} + +void CSAAAmp::Mute(bool bMute) +{ + // m_bMute refers to the GLOBAL mute setting (register 28 bit 0) + // NOT the per-channel mixer settings !! + m_bMute = bMute; +} + +void CSAAAmp::Sync(bool bSync) +{ + // m_bSync refers to the GLOBAL sync setting (register 28 bit 1) + m_bSync = bSync; +} + +void CSAAAmp::Tick(void) +{ + // updates m_nOutputIntermediate to 0, 1 or 2 + // + + // connected oscillator always ticks (this isn't really connected to the amp) + int level = m_pcConnectedToneGenerator->Tick(); + + switch (m_nMixMode) + { + case 0: + // no tone or noise for this channel + m_nOutputIntermediate = 0; + break; + case 1: + // tone only for this channel + m_nOutputIntermediate = level * 2; + // NOTE: ConnectedToneGenerator returns either 0 or 1 + break; + case 2: + // noise only for this channel + m_nOutputIntermediate = m_pcConnectedNoiseGenerator->Level() * 2; + // NOTE: ConnectedNoiseGenerator()->Level() returns either 0 or 1 + break; + case 3: + // tone+noise for this channel ... mixing algorithm : + // tone noise output + // 0 0 0 + // 1 0 2 + // 0 1 0 + // 1 1 1 + // = 2 * tone - 1 * (tone & noise) + // = tone * (2 - noise) + m_nOutputIntermediate = level * (2 - m_pcConnectedNoiseGenerator->Level()); + break; + } + // intermediate is between 0 and 2 +} + +inline int CSAAAmp::EffectiveAmplitude(int amp, int env) const +{ + // Return the effective amplitude of the low-pass-filtered result of the logical + // AND of the amplitude PDM and envelope PDM patterns. This is a more accurate + // evaluation of the SAA than simply returning amp * env , based on how the SAA + // implements pulse-density modulation. + static const int pdm[16][16] = { + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,2,2,2,2,2,2,2,2,4,4,4,4}, + {0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8}, + {0,1,1,2,4,5,5,6,6,7,7,8,10,11,11,12}, + {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}, + {0,1,2,3,6,7,8,9,10,11,12,13,16,17,18,19}, + {0,2,3,5,6,8,9,11,12,14,15,17,18,20,21,23}, + {0,2,3,5,8,10,11,13,14,16,17,19,22,24,25,27}, + {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30}, + {0,2,4,6,10,12,14,16,18,20,22,24,28,30,32,34}, + {0,3,5,8,10,13,15,18,20,23,25,28,30,33,35,38}, + {0,3,5,8,12,15,17,20,22,25,27,30,34,37,39,42}, + {0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45}, + {0,3,6,9,14,17,20,23,26,29,32,35,40,43,46,49}, + {0,4,7,11,14,18,21,25,28,32,35,39,42,46,49,53}, + {0,4,7,11,16,20,23,27,30,34,37,41,46,50,53,57} + }; + + return(pdm[amp][env] * 4); +} + +void CSAAAmp::TickAndOutputStereo(unsigned int & left, unsigned int & right) +{ + // This returns a value between 0 and 480 inclusive. + // This represents the full dynamic range of one output mixer (tone, or noise+tone, at full volume, + // without envelopes enabled). Note that, with envelopes enabled, the actual dynamic range + // is reduced on-chip to just over 88% of this (424), so the "loudest" output requires disabling envs. + // NB for 6 channels at full volume, with simple additive mixing, you would see a combined + // output of 2880, and a multiplier of 11 (=31680) fits comfortably within 16-bit signed output range. + + if (m_bSync) + { + // TODO check this + left = right = 0; + return; + } + + // first, do the Tick: + Tick(); + + // now calculate the returned amplitude for this sample: + //////////////////////////////////////////////////////// + + if (m_bMute) + { + left = right = 0; + } + else if (m_bUseEnvelope && m_pcConnectedEnvGenerator->IsActive()) + { + left = EffectiveAmplitude(m_pcConnectedEnvGenerator->LeftLevel(), leftlevela0x0e) * (2 - m_nOutputIntermediate); + right = EffectiveAmplitude(m_pcConnectedEnvGenerator->RightLevel(), rightlevela0x0e) * (2 - m_nOutputIntermediate); + } + else + { + left = leftlevel * m_nOutputIntermediate * 16; + right = rightlevel * m_nOutputIntermediate * 16; + } +} diff --git a/extern/SAASound/src/SAAAmp.h b/extern/SAASound/src/SAAAmp.h new file mode 100755 index 00000000..4a6761f2 --- /dev/null +++ b/extern/SAASound/src/SAAAmp.h @@ -0,0 +1,44 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// SAAAmp.h: interface for the CSAAAmp class. +// This class handles Tone/Noise mixing, Envelope application and +// amplification. +// +////////////////////////////////////////////////////////////////////// + +#ifndef SAAAMP_H_INCLUDED +#define SAAAMP_H_INCLUDED + +class CSAAAmp +{ +private: + int leftlevel; + int leftlevela0x0e; + int rightlevel; + int rightlevela0x0e; + int m_nOutputIntermediate; + unsigned int m_nMixMode; + CSAAFreq * const m_pcConnectedToneGenerator; // not const because amp calls ->Tick() + const CSAANoise * const m_pcConnectedNoiseGenerator; + const CSAAEnv * const m_pcConnectedEnvGenerator; + const bool m_bUseEnvelope; + mutable bool m_bMute; + mutable bool m_bSync; + mutable BYTE last_level_byte; + int EffectiveAmplitude(int amp, int env) const; + +public: + CSAAAmp(CSAAFreq * const ToneGenerator, const CSAANoise * const NoiseGenerator, const CSAAEnv * const EnvGenerator); + ~CSAAAmp(); + + void SetAmpLevel(BYTE level_byte); // really just a BYTE + void SetToneMixer(BYTE bEnabled); + void SetNoiseMixer(BYTE bEnabled); + void Mute(bool bMute); + void Sync(bool bSync); + void Tick(void); + void TickAndOutputStereo(unsigned int & left, unsigned int & right); + +}; + +#endif // SAAAMP_H_INCLUDED diff --git a/extern/SAASound/src/SAAConfig.cpp b/extern/SAASound/src/SAAConfig.cpp new file mode 100644 index 00000000..c63b2a3a --- /dev/null +++ b/extern/SAASound/src/SAAConfig.cpp @@ -0,0 +1,113 @@ +// Part of SAASound copyright 2020 Dave Hooper +// +// SAAConfig.cpp: configuration file handler class +// +////////////////////////////////////////////////////////////////////// + +#include "defns.h" +#ifdef USE_CONFIG_FILE + +#include "SAAConfig.h" +#define INI_READONLY +#define INI_ANSIONLY +#include "minIni/minIni.h" +#include + +SAAConfig::SAAConfig() +: +m_bHasReadConfig(false), +m_bGenerateRegisterLogs(false), +m_bGeneratePcmLogs(false), +m_bGeneratePcmSeparateChannels(false), +m_nBoost(DEFAULT_BOOST), +m_bHighpass(true), +m_nOversample(DEFAULT_OVERSAMPLE), +m_minIni(_T(CONFIG_FILE_PATH)) +{ +} + +void SAAConfig::ReadConfig() +{ + // Assume (i.e. require) that the config file is always in UTF-8 . + // These days, I think that's a good assumption to want to make. + // It's also easy for people to create UTF-8 configs. + // Define a helper to let us read from UTF-8 and convert to system locale + // across platforms (and assume this will be a no-op on *nix) + +#if defined(WIN32) && defined(UNICODE) + // WIN32 support for UNICODE requires different types + // Convert config file contents from utf8 to wchar_t + // minIni has been compiled to use plain char for file read-write operations + // (which supports UTF8) and wchar_t for filenames (which supports unicode filenames + // on win32) + std::wstring_convert> converter; +#define wrapped_gets(_mstring, _section, _element, _default) do{ \ + std::string _temp_u8 = m_minIni.gets(u8"" _section "", u8"" _element "", u8"" _default ""); \ + _mstring = converter.from_bytes(_temp_u8.c_str()); \ + } \ + while(0) +#else + // For *nix I think I'm supposed to convert the text from the + // file encoding (which I'm assuming is utf8) to system/user locale (if different) + // which I think that requires converting first to wchar_t and then to current locale + // For now I'm just going to assume you'using UTF8, and I will do no conversion. + // minIni has been compiled to use plain char for everything, + // which is really just a utf8 passthrough assumption. + // If you're compiling for WIN32 but with UNICODE not defined: you're on your own + // (it might work but I'm not testing or supporting you..) + +#define wrapped_gets(_mstring, _section, _element, _default) \ + _mstring = m_minIni.gets(u8"" _section "", u8"" _element "", u8"" _default ""); +#endif + +#define _u8ify(x) ( u8"" x "" ) + m_bGenerateRegisterLogs = m_minIni.getbool(u8"Debug", u8"WriteRegisterLog", false); + if (m_bGenerateRegisterLogs) + { + wrapped_gets(m_strRegisterLogPath, "Debug", "RegisterLogPath", DEBUG_SAA_REGISTER_LOG); + } + + m_bGeneratePcmLogs = m_minIni.getbool(u8"Debug", u8"WritePCMOutput", false); + if (m_bGeneratePcmLogs) + { + wrapped_gets(m_strPcmOutputPath, "Debug", "PCMOutputPath", DEBUG_SAA_PCM_LOG); + m_bGeneratePcmSeparateChannels = m_minIni.getbool(u8"Debug", u8"PCMSeparateChannels", false); + } + + m_bHighpass = m_minIni.getbool(u8"Quality", u8"Highpass", true); + + m_nOversample = m_minIni.geti(u8"Quality", u8"Oversample", DEFAULT_OVERSAMPLE); + if (m_nOversample < 1) + // oversample of 0 or negative doesn't make sense + m_nOversample = 1; + if (m_nOversample > 15) + // oversample of 16 (=65536x oversample) or more is ridiculous + // apart from just CPU cycles, our (32-bit)int-based accumulation would overflow + m_nOversample = 15; + + m_nBoost = m_minIni.getf(u8"Quality", u8"Boost", DEFAULT_BOOST); + if (m_nBoost < 1) + { + // interpret setting Boost=0 as disabling boost i.e. volume multipler = 100% (not 0%) + // Since "Boost=1" also means volume multipler = 100%, we can just ignore values of + // Boost between 0 and 1 as meaning 'no boost' + m_nBoost = 1; + } +} + +t_string SAAConfig::getChannelPcmOutputPath(int i) +{ + // returns a name for the i'th PCM output file when generating + // single files per channel. This is really just a helper to localise + // the string manipulation involved in this. + // TODO - maybe make the naming convention more flexible? + t_string filename = m_strPcmOutputPath; +#if defined UNICODE + filename += std::to_wstring(i); +#else + filename += std::to_string(i); +#endif + return filename; +} + +#endif // USE_CONFIG_FILE \ No newline at end of file diff --git a/extern/SAASound/src/SAAConfig.h b/extern/SAASound/src/SAAConfig.h new file mode 100644 index 00000000..a655ec59 --- /dev/null +++ b/extern/SAASound/src/SAAConfig.h @@ -0,0 +1,41 @@ +// Part of SAASound copyright 2020 Dave Hooper +// +// SAAConfig.h: configuration file handler class +// +////////////////////////////////////////////////////////////////////// + +#include "defns.h" +#ifdef USE_CONFIG_FILE + +#ifndef SAA_CONFIG_H_INCLUDED +#define SAA_CONFIG_H_INCLUDED + +#define INI_READONLY +#define INI_ANSIONLY /*nb not really 'ANSI', this just forces all read/write to use 8-bit char*/ +#include "minIni/minIni.h" + +class SAAConfig +{ +private: + minIni m_minIni; + bool m_bHasReadConfig; + +public: + bool m_bGenerateRegisterLogs; + bool m_bGeneratePcmLogs; + bool m_bGeneratePcmSeparateChannels; + t_string m_strRegisterLogPath; + t_string m_strPcmOutputPath; + unsigned int m_nOversample; + bool m_bHighpass; + double m_nBoost; + + SAAConfig(); + void ReadConfig(); + + t_string getChannelPcmOutputPath(int); +}; + +#endif // SAA_CONFIG_H_INCLUDED + +#endif // USE_CONFIG_FILE \ No newline at end of file diff --git a/extern/SAASound/src/SAADevice.cpp b/extern/SAASound/src/SAADevice.cpp new file mode 100644 index 00000000..5cfb17b2 --- /dev/null +++ b/extern/SAASound/src/SAADevice.cpp @@ -0,0 +1,391 @@ +// Part of SAASound copyright 2020 Dave Hooper +// +// SAADevice.cpp: connecting the subcomponents of the SAA1099 together. +// This class handles device inputs and outputs (clocking, data and +// address bus, and simulated output) +// +////////////////////////////////////////////////////////////////////// + +#include "SAASound.h" +#include "types.h" +#include "SAAEnv.h" +#include "SAANoise.h" +#include "SAAFreq.h" +#include "SAAAmp.h" +#include "SAASound.h" +#include "SAAImpl.h" +#include "defns.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CSAADevice::CSAADevice() + : + m_nCurrentSaaReg(0), + m_bOutputEnabled(false), + m_bSync(false), + m_nOversample(0), + m_Noise0(0xffffffff), + m_Noise1(0xffffffff), + m_Env0(), + m_Env1(), + m_Osc0(&m_Noise0, NULL), + m_Osc1(NULL, &m_Env0), + m_Osc2(NULL, NULL), + m_Osc3(&m_Noise1, NULL), + m_Osc4(NULL, &m_Env1), + m_Osc5(NULL, NULL), + m_Amp0(&m_Osc0, &m_Noise0, NULL), + m_Amp1(&m_Osc1, &m_Noise0, NULL), + m_Amp2(&m_Osc2, &m_Noise0, &m_Env0), + m_Amp3(&m_Osc3, &m_Noise1, NULL), + m_Amp4(&m_Osc4, &m_Noise1, NULL), + m_Amp5(&m_Osc5, &m_Noise1, &m_Env1) +{ + // Create and link up the objects that make up the emulator + Noise[0] = &m_Noise0; + Noise[1] = &m_Noise1; + Env[0] = &m_Env0; + Env[1] = &m_Env1; + + // Create oscillators (tone generators) and link to noise generators and + // envelope controllers + Osc[0] = &m_Osc0; + Osc[1] = &m_Osc1; + Osc[2] = &m_Osc2; + Osc[3] = &m_Osc3; + Osc[4] = &m_Osc4; + Osc[5] = &m_Osc5; + + // Create amplification/mixing stages and link to appropriate oscillators, + // noise generators and envelope controllers + Amp[0] = &m_Amp0; + Amp[1] = &m_Amp1; + Amp[2] = &m_Amp2; + Amp[3] = &m_Amp3; + Amp[4] = &m_Amp4; + Amp[5] = &m_Amp5; + + _SetClockRate(EXTERNAL_CLK_HZ); + _SetOversample(DEFAULT_OVERSAMPLE); +} + +CSAADevice::~CSAADevice() +{ +} + +////////////////////////////////////////////////////////////////////// +// CSAASound members +////////////////////////////////////////////////////////////////////// + +void CSAADevice::_SetClockRate(unsigned int nClockRate) +{ + m_Osc0._SetClockRate(nClockRate); + m_Osc1._SetClockRate(nClockRate); + m_Osc2._SetClockRate(nClockRate); + m_Osc3._SetClockRate(nClockRate); + m_Osc4._SetClockRate(nClockRate); + m_Osc5._SetClockRate(nClockRate); + m_Noise0._SetClockRate(nClockRate); + m_Noise1._SetClockRate(nClockRate); +} + +void CSAADevice::_SetSampleRate(unsigned int nSampleRate) +{ + m_Osc0._SetSampleRate(nSampleRate); + m_Osc1._SetSampleRate(nSampleRate); + m_Osc2._SetSampleRate(nSampleRate); + m_Osc3._SetSampleRate(nSampleRate); + m_Osc4._SetSampleRate(nSampleRate); + m_Osc5._SetSampleRate(nSampleRate); + m_Noise0._SetSampleRate(nSampleRate); + m_Noise1._SetSampleRate(nSampleRate); +} + +void CSAADevice::_SetOversample(unsigned int nOversample) +{ + if (nOversample != (unsigned int)m_nOversample) + { + m_nOversample = nOversample; + m_Osc0._SetOversample(nOversample); + m_Osc1._SetOversample(nOversample); + m_Osc2._SetOversample(nOversample); + m_Osc3._SetOversample(nOversample); + m_Osc4._SetOversample(nOversample); + m_Osc5._SetOversample(nOversample); + m_Noise0._SetOversample(nOversample); + m_Noise1._SetOversample(nOversample); + } +} + +void CSAADevice::_WriteData(BYTE nData) +{ +#if defined(DEBUG) || defined(DEBUGSAA) + m_Reg[m_nCurrentSaaReg] = nData; +#endif + + // route nData to the appropriate place + switch (m_nCurrentSaaReg) + { + // Amplitude data (==> Amp) + case 0: + m_Amp0.SetAmpLevel(nData); + break; + case 1: + m_Amp1.SetAmpLevel(nData); + break; + case 2: + m_Amp2.SetAmpLevel(nData); + break; + case 3: + m_Amp3.SetAmpLevel(nData); + break; + case 4: + m_Amp4.SetAmpLevel(nData); + break; + case 5: + m_Amp5.SetAmpLevel(nData); + break; + + // Freq data (==> Osc) + case 8: + m_Osc0.SetFreqOffset(nData); + break; + case 9: + m_Osc1.SetFreqOffset(nData); + break; + case 10: + m_Osc2.SetFreqOffset(nData); + break; + case 11: + m_Osc3.SetFreqOffset(nData); + break; + case 12: + m_Osc4.SetFreqOffset(nData); + break; + case 13: + m_Osc5.SetFreqOffset(nData); + break; + + // Freq octave data (==> Osc) for channels 0,1 + case 16: + m_Osc0.SetFreqOctave(nData & 0x07); + m_Osc1.SetFreqOctave((nData >> 4) & 0x07); + break; + + // Freq octave data (==> Osc) for channels 2,3 + case 17: + m_Osc2.SetFreqOctave(nData & 0x07); + m_Osc3.SetFreqOctave((nData >> 4) & 0x07); + break; + + // Freq octave data (==> Osc) for channels 4,5 + case 18: + m_Osc4.SetFreqOctave(nData & 0x07); + m_Osc5.SetFreqOctave((nData >> 4) & 0x07); + break; + + // Tone mixer control (==> Amp) + case 20: + m_Amp0.SetToneMixer(nData & 0x01); + m_Amp1.SetToneMixer(nData & 0x02); + m_Amp2.SetToneMixer(nData & 0x04); + m_Amp3.SetToneMixer(nData & 0x08); + m_Amp4.SetToneMixer(nData & 0x10); + m_Amp5.SetToneMixer(nData & 0x20); + break; + + // Noise mixer control (==> Amp) + case 21: + m_Amp0.SetNoiseMixer(nData & 0x01); + m_Amp1.SetNoiseMixer(nData & 0x02); + m_Amp2.SetNoiseMixer(nData & 0x04); + m_Amp3.SetNoiseMixer(nData & 0x08); + m_Amp4.SetNoiseMixer(nData & 0x10); + m_Amp5.SetNoiseMixer(nData & 0x20); + break; + + // Noise frequency/source control (==> Noise) + case 22: + m_Noise0.SetSource(nData & 0x03); + m_Noise1.SetSource((nData >> 4) & 0x03); + break; + + // Envelope control data (==> Env) for envelope controller #0 + case 24: + m_Env0.SetEnvControl(nData); + break; + + // Envelope control data (==> Env) for envelope controller #1 + case 25: + m_Env1.SetEnvControl(nData); + break; + + // Global enable and reset (sync) controls + case 28: + { + // Reset (sync) bit + bool bSync = bool(nData & 0x02); + if (bSync != m_bSync) + { + // Sync all devices + // This amounts to telling them all to reset to a + // known state, which is also a state that doesn't change + // (i.e. no audio output, although there are some exceptions) + // bSync=true => all devices are sync (aka reset); + // bSync=false => all devices are allowed to run and generate changing output + m_Osc0.Sync(bSync); + m_Osc1.Sync(bSync); + m_Osc2.Sync(bSync); + m_Osc3.Sync(bSync); + m_Osc4.Sync(bSync); + m_Osc5.Sync(bSync); + m_Noise0.Sync(bSync); + m_Noise1.Sync(bSync); + m_Amp0.Sync(bSync); + m_Amp1.Sync(bSync); + m_Amp2.Sync(bSync); + m_Amp3.Sync(bSync); + m_Amp4.Sync(bSync); + m_Amp5.Sync(bSync); + m_bSync = bSync; + } + + // Global mute bit + bool bOutputEnabled = bool(nData & 0x01); + if (bOutputEnabled != m_bOutputEnabled) + { + // unmute all amps - sound 'enabled' + m_Amp0.Mute(!bOutputEnabled); + m_Amp1.Mute(!bOutputEnabled); + m_Amp2.Mute(!bOutputEnabled); + m_Amp3.Mute(!bOutputEnabled); + m_Amp4.Mute(!bOutputEnabled); + m_Amp5.Mute(!bOutputEnabled); + m_bOutputEnabled = bOutputEnabled; + } + } + break; + + default: + // anything else means data is being written to a register + // that is not used within the SAA-1099 architecture + // hence, we ignore it. + {} + } +} + +void CSAADevice::_WriteAddress(BYTE nReg) +{ + m_nCurrentSaaReg = nReg & 31; + if (m_nCurrentSaaReg == 24) + { + m_Env0.ExternalClock(); + } + else if (m_nCurrentSaaReg == 25) + { + m_Env1.ExternalClock(); + } +} + +#if defined(DEBUG) || defined(DEBUGSAA) || defined(USE_CONFIG_FILE) +BYTE CSAADevice::_ReadAddress(void) +{ + // Not a real hardware function of the SAA-1099, which is write-only + // However, this is used by SAAImpl to generate debug logs (if enabled) + return(m_nCurrentSaaReg); +} +#endif +#if defined(DEBUG) +BYTE CSAADevice::_ReadData(void) +{ + // Not a real hardware function of the SAA-1099, which is write-only + // This is only compiled for Debug builds + return(m_Reg[m_nCurrentSaaReg]); +} +#endif + +void CSAADevice::_TickAndOutputStereo(unsigned int& left_mixed, unsigned int& right_mixed) +{ + unsigned int temp_left, temp_right; + unsigned int accum_left = 0, accum_right = 0; + for (int i = 1 << m_nOversample; i > 0; i--) + { + m_Noise0.Tick(); + m_Noise1.Tick(); + m_Amp0.TickAndOutputStereo(temp_left, temp_right); + accum_left += temp_left; + accum_right += temp_right; + m_Amp1.TickAndOutputStereo(temp_left, temp_right); + accum_left += temp_left; + accum_right += temp_right; + m_Amp2.TickAndOutputStereo(temp_left, temp_right); + accum_left += temp_left; + accum_right += temp_right; + m_Amp3.TickAndOutputStereo(temp_left, temp_right); + accum_left += temp_left; + accum_right += temp_right; + m_Amp4.TickAndOutputStereo(temp_left, temp_right); + accum_left += temp_left; + accum_right += temp_right; + m_Amp5.TickAndOutputStereo(temp_left, temp_right); + accum_left += temp_left; + accum_right += temp_right; + } + left_mixed = accum_left; + right_mixed = accum_right; +} + +void CSAADevice::_TickAndOutputSeparate(unsigned int& left_mixed, unsigned int& right_mixed, + unsigned int& left0, unsigned int& right0, + unsigned int& left1, unsigned int& right1, + unsigned int& left2, unsigned int& right2, + unsigned int& left3, unsigned int& right3, + unsigned int& left4, unsigned int& right4, + unsigned int& left5, unsigned int& right5 +) +{ + unsigned int temp_left, temp_right; + unsigned int accum_left = 0, accum_right = 0; + left0 = left1 = left2 = left3 = left4 = left5 = 0; + right0 = right1 = right2 = right3 = right4 = right5 = 0; + for (int i = 1 << m_nOversample; i > 0; i--) + { + m_Noise0.Tick(); + m_Noise1.Tick(); + m_Amp0.TickAndOutputStereo(temp_left, temp_right); + left0 += temp_left; + right0 += temp_right; + accum_left += temp_left; + accum_right += temp_right; + m_Amp1.TickAndOutputStereo(temp_left, temp_right); + left1 += temp_left; + right1 += temp_right; + accum_left += temp_left; + accum_right += temp_right; + m_Amp2.TickAndOutputStereo(temp_left, temp_right); + left2 += temp_left; + right2 += temp_right; + accum_left += temp_left; + accum_right += temp_right; + m_Amp3.TickAndOutputStereo(temp_left, temp_right); + left3 += temp_left; + right3 += temp_right; + accum_left += temp_left; + accum_right += temp_right; + m_Amp4.TickAndOutputStereo(temp_left, temp_right); + left4 += temp_left; + right4 += temp_right; + accum_left += temp_left; + accum_right += temp_right; + m_Amp5.TickAndOutputStereo(temp_left, temp_right); + left5 += temp_left; + right5 += temp_right; + accum_left += temp_left; + accum_right += temp_right; + } + left_mixed = accum_left; + right_mixed = accum_right; +} \ No newline at end of file diff --git a/extern/SAASound/src/SAADevice.h b/extern/SAASound/src/SAADevice.h new file mode 100644 index 00000000..639d9062 --- /dev/null +++ b/extern/SAASound/src/SAADevice.h @@ -0,0 +1,68 @@ +// Part of SAASound copyright 2020 Dave Hooper +// +// SAADevice.h: connecting the subcomponents of the SAA1099 together. +// This class handles device inputs and outputs (clocking, data and +// address bus, and simulated output) +// +////////////////////////////////////////////////////////////////////// + +#ifndef SAADEVICE_H_INCLUDED +#define SAADEVICE_H_INCLUDED + +#include "SAASound.h" +#include "SAANoise.h" +#include "SAAEnv.h" +#include "SAAFreq.h" +#include "SAAAmp.h" + +class CSAADevice +{ +private: + int m_nCurrentSaaReg; + bool m_bOutputEnabled; + bool m_bSync; + int m_nOversample; + + CSAANoise m_Noise0, m_Noise1; + CSAAEnv m_Env0, m_Env1; + CSAAFreq m_Osc0, m_Osc1, m_Osc2, m_Osc3, m_Osc4, m_Osc5; + CSAAAmp m_Amp0, m_Amp1, m_Amp2, m_Amp3, m_Amp4, m_Amp5; + + CSAANoise* Noise[2]; + CSAAEnv* Env[2]; + CSAAFreq* Osc[6]; + CSAAAmp* Amp[6]; + +#if defined(DEBUG) || defined(DEBUGSAA) + BYTE m_Reg[32]; +#endif + +public: + CSAADevice(); + ~CSAADevice(); + + void _WriteAddress(BYTE nReg); + void _WriteData(BYTE nData); +#if defined(DEBUG) || defined(DEBUGSAA) || defined(USE_CONFIG_FILE) + BYTE _ReadAddress(void); +#endif +#if defined(DEBUG) + BYTE _ReadData(void); +#endif + + void _SetClockRate(unsigned int nClockRate); + void _SetSampleRate(unsigned int nSampleRate); + void _SetOversample(unsigned int nOversample); + void _TickAndOutputStereo(unsigned int& left_mixed, unsigned int& right_mixed); + void _TickAndOutputSeparate(unsigned int& left_mixed, unsigned int& right_mixed, + unsigned int& left0, unsigned int& right0, + unsigned int& left1, unsigned int& right1, + unsigned int& left2, unsigned int& right2, + unsigned int& left3, unsigned int& right3, + unsigned int& left4, unsigned int& right4, + unsigned int& left5, unsigned int& right5 + ); + +}; + +#endif // SAADEVICE_H_INCLUDED \ No newline at end of file diff --git a/extern/SAASound/src/SAAEnv.cpp b/extern/SAASound/src/SAAEnv.cpp new file mode 100755 index 00000000..049f51f9 --- /dev/null +++ b/extern/SAASound/src/SAAEnv.cpp @@ -0,0 +1,380 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// SAAEnv.cpp: implementation of the CSAAEnv class. +// +////////////////////////////////////////////////////////////////////// + +#include "SAASound.h" +#include "types.h" +#include "SAAEnv.h" + + +////////////////////////////////////////////////////////////////////// +// Static member initialisation +////////////////////////////////////////////////////////////////////// + +const ENVDATA CSAAEnv::cs_EnvData[8] = +{ + {1,false, { {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}, + {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}}, + {1,true, { {{15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15},{15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15}}, + {{14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14},{14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14}}}}, + {1,false, { {{15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}, + {{14,14,12,12,10,10,8,8,6,6,4,4,2,2,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}}, + {1,true, { {{15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}, + {{14,14,12,12,10,10,8,8,6,6,4,4,2,2,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}}, + {2,false, { {{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}, {15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}}, + {{0,0,2,2,4,4,6,6,8,8,10,10,12,12,14,14}, {14,14,12,12,10,10,8,8,6,6,4,4,2,2,0,0}}}}, + {2,true, { {{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}, {15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}}, + {{0,0,2,2,4,4,6,6,8,8,10,10,12,12,14,14}, {14,14,12,12,10,10,8,8,6,6,4,4,2,2,0,0}}}}, + {1,false, { {{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}, + {{0,0,2,2,4,4,6,6,8,8,10,10,12,12,14,14}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}}, + {1,true, { {{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}, + {{0,0,2,2,4,4,6,6,8,8,10,10,12,12,14,14}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}} +}; + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CSAAEnv::CSAAEnv() +: +m_bEnabled(false), +m_nPhase(0), +m_nPhasePosition(0), +m_bEnvelopeEnded(true), +m_nResolution(1), +m_bNewData(false), +m_nNextData(0) +{ + // initialise itself with the value 'zero' + SetEnvControl(0); +} + +CSAAEnv::~CSAAEnv() +{ + // Nothing to do +} + +void CSAAEnv::InternalClock(void) +{ + // will only do something if envelope clock mode is set to internal + // and the env control is enabled + if (m_bEnabled && (!m_bClockExternally)) Tick(); +} + +void CSAAEnv::ExternalClock(void) +{ + // will only do something if envelope clock mode is set to external + // and the env control is enabled + if (m_bClockExternally && m_bEnabled) Tick(); +} + +void CSAAEnv::SetEnvControl(int nData) +{ + // process immediate stuff first: + // start with the Enabled flag. if env is disabled, + // there's not much to do + bool bEnabled = ((nData & 0x80)==0x80); + if (!bEnabled && !m_bEnabled) + return; + m_bEnabled = bEnabled; + if (!m_bEnabled) + { + // env control was enabled, and now disabled + // Any subsequent env control changes are immediate. + m_bEnvelopeEnded = true; + return; + } + + // Resolution (3bit/4bit) is also immediately processed + int new_resolution = ((nData & 0x10) == 0x10) ? 2 : 1; + // NOTE: undocumented behaviour when changing resolution mid-waveform + // Empirically, the following matches observations: + // * When ticking the env generator with 4-bit resolution, the position += 1 + // * When ticking the env generator with 3-bit resolution, the position += 2 + // * When changing between 4-bit resolution and 3-bit resolution + // without ticking the env generator, the position is unchanged + // (although, effectively, the LSB is ignored. Purely as an implementation + // detail, I'm implementing this as clearing the LSB ie LSB=0; see next point) + // * When changing between 3-bit resolution and 4-bit resolution + // without ticking the env generator, the position LSB is set to 1 + // See test case: envext_34b + // + if (m_nResolution == 1 && new_resolution == 2) + { + // change from 4-bit to 3-bit + m_nPhasePosition &= 0xe; + } + else if (m_nResolution == 2 && new_resolution == 1) + { + // change from 3-bit to 4-bit + m_nPhasePosition |= 0x1; + } + m_nResolution = new_resolution; + + // now buffered stuff: but only if it's ok to, and only if the + // envgenerator is not disabled. otherwise it just stays buffered until + // the Tick() function sets m_bEnvelopeEnded to true and realises there is + // already some new data waiting + if (m_bEnvelopeEnded) + { + SetNewEnvData(nData); // also does the SetLevels() call for us. + m_bNewData=false; + } + else + { + // since the 'next resolution' changes arrive unbuffered, we + // may need to change the current level because of this: + SetLevels(); + + // store current new data, and set the newdata flag: + m_bNewData = true; + m_nNextData = nData; + } + +} + +int CSAAEnv::LeftLevel(void) const +{ + return m_nLeftLevel; +} + +int CSAAEnv::RightLevel(void) const +{ + return m_nRightLevel; +} + +inline void CSAAEnv::Tick(void) +{ + // if disabled, do nothing + if (!m_bEnabled) // m_bEnabled is set directly, not buffered, so this is ok + { + // for sanity, reset stuff: + m_bEnvelopeEnded = true; + m_nPhase = 0; + m_nPhasePosition = 0; + return; + } + + // else : m_bEnabled + + + if (m_bEnvelopeEnded) + { + // do nothing + // (specifically, don't change the values of m_bEnvelopeEnded, + // m_nPhase and m_nPhasePosition, as these will still be needed + // by SetLevels() should it be called again) + + return; + } + + + // else : !m_bEnvelopeEnded + // Continue playing the same envelope ... + // increments the phaseposition within an envelope. + // also handles looping and resolution appropriately. + // Changes the level of the envelope accordingly + // through calling SetLevels() . This must be called after making + // any changes that will affect the output levels of the env controller!! + // SetLevels also handles left-right channel inverting + + // increment phase position + m_nPhasePosition += m_nResolution; + + // if this means we've gone past 16 (the end of a phase) + // then change phase, and if necessary, loop + // Refer to datasheet for meanings of (3) and (4) in following text + // w.r.t SAA1099 envelopes + + // Note that we will always reach position (3) or (4), even if we keep toggling + // resolution from 4-bit to 3-bit and back, because the counter will always wrap to 0. + // In fact it's quite elegant: + // No matter how you increment and toggle and increment and toggle, the counter + // will at some point be either 0xe (either 4-bit mode or 3-bit mode) or 0xf (4-bit mode only). + // Depending on the mode, even if you change the mode, the next increment, + // or the one after it, will then take it to 0. + // 0xe + 2 (3bit mode) => 0x0 + // 0xe + 1 (4bit mode) => 0xf + // 0xf + 1 (4bit mode) => 0x0 + // 0xe -> (toggle 3bit mode to 4bit mode) => 0xf + // 0xe -> (toggle 4bit mode to 3bit mode) => 0xe + // 0xf -> (toggle 4bit mode to 3bit mode) => 0xe + // + // but there is a subtlety (of course), which is that any changes at point (3) + // can take place immediately you hit point (3), but changes at point (4) are actually + // only acted upon when the counter transitions from 0xe (or 0xf) to 0x0 (which also + // means that, for these looping envelopes, which are the ones that have a point(4), + // immediately after the counter wrapping to 0x0, a write to the env data register will + // NOT set the waveform and will NOT reset the phase/phaseposition (even though it + // will still let you toggle the 4bit/3bit mode, which will change the phaseposition LSB!) + // See test case: envext_34c + + bool bProcessNewDataIfAvailable = false; + if (m_nPhasePosition >= 16) + { + m_nPhase++; + + // if we should loop, then do so - and we've reached position (4) + // otherwise, if we shouldn't loop, + // then we've reached position (3) and so we say that + // we're ok for new data. + if (m_nPhase == m_nNumberOfPhases) + { + // at position (3) or (4) + if (!m_bLooping) + { + // position (3) only + // note that it seems that the sustain level is ALWAYS zero + // in the case of non-looping waveforms + m_bEnvelopeEnded = true; + bProcessNewDataIfAvailable = true; + } + else + { + // position (4) only + // note that any data already latched is ONLY acted upon + // at THIS point. If (after this Tick has completed) any new + // env data is written, it will NOT be acted upon, until + // we get back to position (4) again. + // this is why m_bEnvelopeEnded (which affects the behaviour + // of the SetEnvControl method) is FALSE here. + // See test case: envext_34c (as noted earlier) + m_bEnvelopeEnded = false; + // set phase pointer to start of envelope for loop + // and reset m_nPhasePosition + m_nPhase=0; + m_nPhasePosition -= 16; + bProcessNewDataIfAvailable = true; + } + } + else // (m_nPhase < m_nNumberOfPhases) + { + // not at position (3) or (4) ... + // (i.e., we're in the middle of an envelope with + // more than one phase. Specifically, we're in + // the middle of envelope 4 or 5 - the + // triangle envelopes - but that's not important) + + // any commands sent to this envelope controller + // will be buffered. Set the flag to indicate this. + m_bEnvelopeEnded = false; + m_nPhasePosition -= 16; + } + } + else // (m_nPhasePosition < 16) + { + // still within the same phase; + // but, importantly, we are no longer at the start of the phase ... + // so new data cannot be acted on immediately, and must + // be buffered + m_bEnvelopeEnded = false; + // Phase and PhasePosition have already been updated. + // SetLevels() will need to be called to actually calculate + // the output 'level' of this envelope controller + } + + + // if we have new (buffered) data, now is the time to act on it + if (m_bNewData && bProcessNewDataIfAvailable) + { + m_bNewData = false; + SetNewEnvData(m_nNextData); + } + else + { + // ok, we didn't have any new buffered date to act on, + // so we just call SetLevels() to calculate the output level + // for whatever the current envelope is + SetLevels(); + } + +} + +inline void CSAAEnv::SetLevels(void) +{ + // sets m_nLeftLevel + // Also sets m_nRightLevel in terms of m_nLeftLevel + // and m_bInvertRightChannel + + // m_nResolution: 1 means 4-bit resolution; 2 means 3-bit resolution. Resolution of envelope waveform. + + // Note that this is handled 'immediately', and doesn't wait for synchronisation of + // the envelope waveform (this is important, see test case EnvExt_imm) + // It is therefore possible to switch between 4-bit and 3-bit resolution in the middle of + // an envelope waveform. if you are at an 'odd' phase position, you would be able to hear + // the difference. if you are at an 'even' phase position, the volume level for 4-bit + // and 3-bit would be the same. + // NOTE: additional test cases are required. + + switch (m_nResolution) + { + case 1: // 4 bit res waveforms + default: + { + // special case: if envelope is not a looping one, and we're at the end + // then our level should be zero (all of the non-looping waveforms have + // a sustain level of zero): + if (m_bEnvelopeEnded && !m_bLooping) + m_nLeftLevel = 0; + else + m_nLeftLevel = m_pEnvData->nLevels[0][m_nPhase][m_nPhasePosition]; + + if (m_bInvertRightChannel) + m_nRightLevel = 15-m_nLeftLevel; + else + m_nRightLevel = m_nLeftLevel; + break; + } + case 2: // 3 bit res waveforms + { + // special case: if envelope is not a looping one, and we're at the end + // then our level should be zero (all of the non-looping waveforms have + // a sustain level of zero): + if (m_bEnvelopeEnded && !m_bLooping) + m_nLeftLevel = 0; + else + m_nLeftLevel = m_pEnvData->nLevels[1][m_nPhase][m_nPhasePosition]; + if (m_bInvertRightChannel) + m_nRightLevel = 14-m_nLeftLevel; + else + m_nRightLevel = m_nLeftLevel; + break; + } + } +} + + +inline void CSAAEnv::SetNewEnvData(int nData) +{ + // loads envgenerator's registers according to the bits set + // in nData + + m_nPhase = 0; + m_nPhasePosition = 0; + m_pEnvData = &(cs_EnvData[(nData >> 1) & 0x07]); + m_bInvertRightChannel = ((nData & 0x01) == 0x01); + m_bClockExternally = ((nData & 0x20) == 0x20); + m_nNumberOfPhases = m_pEnvData->nNumberOfPhases; + m_bLooping = m_pEnvData->bLooping; + m_nResolution = (((nData & 0x10)==0x10) ? 2 : 1); + m_bEnabled = ((nData & 0x80) == 0x80); + if (m_bEnabled) + { + m_bEnvelopeEnded = false; + // is this right? + // YES. See test case EnvExt_34c (setting data multiple times + // when at a point (3) resets the waveform so you're no longer + // at a point (3). + } + else + { + // DISABLED - so set stuff accordingly + m_bEnvelopeEnded = true; + m_nPhase = 0; + m_nPhasePosition = 0; + } + + SetLevels(); +} diff --git a/extern/SAASound/src/SAAEnv.h b/extern/SAASound/src/SAAEnv.h new file mode 100755 index 00000000..d52d6dcf --- /dev/null +++ b/extern/SAASound/src/SAAEnv.h @@ -0,0 +1,51 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// SAAEnv.h: interface for the CSAAEnv class. +// +////////////////////////////////////////////////////////////////////// + +#ifndef SAAENV_H_INCLUDED +#define SAAENV_H_INCLUDED + +class CSAAEnv +{ +private: + int m_nLeftLevel, m_nRightLevel; + ENVDATA const * m_pEnvData; + + bool m_bEnabled; + bool m_bInvertRightChannel; + BYTE m_nPhase; + BYTE m_nPhasePosition; + bool m_bEnvelopeEnded; + bool m_bLooping; + char m_nNumberOfPhases; + char m_nResolution; + bool m_bNewData; + BYTE m_nNextData; + bool m_bClockExternally; + static const ENVDATA cs_EnvData[8]; + + void Tick(void); + void SetLevels(void); + void SetNewEnvData(int nData); + +public: + CSAAEnv(); + ~CSAAEnv(); + + void InternalClock(void); + void ExternalClock(void); + void SetEnvControl(int nData); // really just a BYTE + int LeftLevel(void) const; + int RightLevel(void) const; + bool IsActive(void) const; + +}; + +inline bool CSAAEnv::IsActive(void) const +{ + return m_bEnabled; +} + +#endif // SAAENV_H_INCLUDED diff --git a/extern/SAASound/src/SAAFreq.cpp b/extern/SAASound/src/SAAFreq.cpp new file mode 100755 index 00000000..0058c35a --- /dev/null +++ b/extern/SAASound/src/SAAFreq.cpp @@ -0,0 +1,280 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// SAAFreq.cpp: implementation of the CSAAFreq class. +// only 7-bit fractional accuracy on oscillator periods. I may consider fixing that. +// +////////////////////////////////////////////////////////////////////// + +#include "SAASound.h" +#include "types.h" +#include "SAANoise.h" +#include "SAAEnv.h" +#include "SAAFreq.h" +#include "defns.h" + +#ifdef SAAFREQ_FIXED_CLOCKRATE +// 'load in' the data for the static frequency lookup table +// precomputed for a fixed clockrate +// See: tools/freqdat.py +const unsigned long CSAAFreq::m_FreqTable[2048] = { +#include "SAAFreq.dat" +}; +#else +unsigned long CSAAFreq::m_FreqTable[2048]; +unsigned long CSAAFreq::m_nClockRate = 0; +#endif // SAAFREQ_FIXED_CLOCKRATE + +const int INITIAL_LEVEL = 1; + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CSAAFreq::CSAAFreq(CSAANoise * const NoiseGenerator, CSAAEnv * const EnvGenerator) +: +m_nCounter(0), m_nAdd(0), m_nCounter_low(0), +m_nOversample(0), +m_nCounterLimit_low(1), m_nLevel(INITIAL_LEVEL), +m_nCurrentOffset(0), m_nCurrentOctave(0), m_nNextOffset(0), m_nNextOctave(0), +m_bIgnoreOffsetData(false), m_bNewData(false), +m_bSync(false), +m_nSampleRate(SAMPLE_RATE_HZ), +m_pcConnectedNoiseGenerator(NoiseGenerator), +m_pcConnectedEnvGenerator(EnvGenerator), +m_nConnectedMode((NoiseGenerator == NULL) ? ((EnvGenerator == NULL) ? 0 : 1) : 2) +{ + _SetClockRate(EXTERNAL_CLK_HZ); + SetAdd(); // current octave, current offset +} + +CSAAFreq::~CSAAFreq() +{ + // Nothing to do +} + +void CSAAFreq::SetFreqOffset(BYTE nOffset) +{ + // nOffset between 0 and 255 + + if (!m_bSync) + { + m_nNextOffset = nOffset; + m_bNewData=true; + if (m_nNextOctave==m_nCurrentOctave) + { + // According to Philips, if you send the SAA-1099 + // new Octave data and then new Offset data in that + // order, on the next half-cycle of the current frequency + // generator, ONLY the octave data is acted upon. + // The offset data will be acted upon next time. + + // ?? TEST CASE : if you set the octave and then the offset + // but the octave you set it to is the same one it already was. + // Will this ignore the offset data? + // Do you get the same behaviour if you set offset THEN octave + // even if you set octave to the same value it was before? + + m_bIgnoreOffsetData=true; + } + } + else + { + // updates straightaway if m_bSync + m_bNewData=false; + m_bIgnoreOffsetData = false; + m_nCurrentOffset = nOffset; + m_nNextOffset = nOffset; + m_nCurrentOctave = m_nNextOctave; + SetAdd(); + } + +} + +void CSAAFreq::SetFreqOctave(BYTE nOctave) +{ + // nOctave between 0 and 7 + + if (!m_bSync) + { + m_nNextOctave = nOctave; + m_bNewData=true; + m_bIgnoreOffsetData = false; + } + else + { + // updates straightaway if m_bSync + m_bNewData=false; + m_bIgnoreOffsetData = false; + m_nCurrentOctave = nOctave; + m_nNextOctave = nOctave; + m_nCurrentOffset = m_nNextOffset; + SetAdd(); + } +} + +void CSAAFreq::UpdateOctaveOffsetData(void) +{ + // loads the buffered new octave and new offset data into the current registers + // and sets up the new frequency for this frequency generator (i.e. sets up m_nAdd) + // - called during Sync, and called when waveform half-cycle completes + + // How the SAA-1099 really treats new data: + // if only new octave data is present, + // then set new period based on just the octave data + // Otherwise, if only new offset data is present, + // then set new period based on just the offset data + // Otherwise, if new octave data is present, and new offset data is present, + // and the offset data was set BEFORE the octave data, + // then set new period based on both the octave and offset data + // Else, if the offset data came AFTER the new octave data + // then set new period based on JUST THE OCTAVE DATA, and continue + // signalling the offset data as 'new', so it will be acted upon + // next half-cycle + // + // Weird, I know. But that's how it works. Philips even documented as much. + + if (!m_bNewData) + { + // optimise for the most common case! No new data! + return; + } + + m_nCurrentOctave=m_nNextOctave; + if (!m_bIgnoreOffsetData) + { + m_nCurrentOffset=m_nNextOffset; + m_bNewData=false; + } + m_bIgnoreOffsetData=false; + + SetAdd(); +} + +void CSAAFreq::_SetSampleRate(unsigned int nSampleRate) +{ + m_nSampleRate = nSampleRate; +} + +void CSAAFreq::_SetOversample(unsigned int oversample) +{ + // oversample is a power of 2 i.e. + // if oversample == 2 then 4x oversample + // if oversample == 6 then 64x oversample + if (oversample < m_nOversample) + { + m_nCounter_low <<= (m_nOversample - oversample); + } + else + { + m_nCounter_low >>= (oversample - m_nOversample); + } + + m_nCounterLimit_low = 1<= (m_nSampleRate<<12)) + { + m_nCounter -= (m_nSampleRate<<12); + m_nCounter_low++; + if (m_nCounter_low >= m_nCounterLimit_low) + { + // period elapsed for (at least) one half-cycle of + // current frequency + m_nCounter_low = 0; + // flip state - from 0 to 1 or vice versa + m_nLevel = 1 - m_nLevel; + + // trigger any connected devices + switch (m_nConnectedMode) + { + case 1: + // env trigger + m_pcConnectedEnvGenerator->InternalClock(); + break; + + case 2: + // noise trigger + m_pcConnectedNoiseGenerator->Trigger(); + break; + + default: + // do nothing + break; + } + + // get new frequency (set period length m_nAdd) if new data is waiting: + UpdateOctaveOffsetData(); + } + } + + return m_nLevel; +} + +void CSAAFreq::SetAdd(void) +{ + // nOctave between 0 and 7; nOffset between 0 and 255 + + // Used to be: + // m_nAdd = (15625 << nOctave) / (511 - nOffset); + // Now just table lookup: + m_nAdd = m_FreqTable[m_nCurrentOctave<<8 | m_nCurrentOffset]; +} + +void CSAAFreq::Sync(bool bSync) +{ + m_bSync = bSync; + + // update straightaway if m_bSync + if (m_bSync) + { + m_nCounter = 0; + m_nCounter_low = 0; + + // this seems to need to be required to make the Fred59 SPACE DEMO audio work correctly + m_nLevel = INITIAL_LEVEL; + + m_nCurrentOctave=m_nNextOctave; + m_nCurrentOffset=m_nNextOffset; + SetAdd(); + } +} diff --git a/extern/SAASound/src/SAAFreq.dat b/extern/SAASound/src/SAAFreq.dat new file mode 100755 index 00000000..04fb9081 --- /dev/null +++ b/extern/SAASound/src/SAAFreq.dat @@ -0,0 +1,141 @@ +/* +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// Precalculated oscillator frequency period steps +// Higher scaling for better accuracy. +// +// After construction, it's important to SetSampleRate before +// trying to use the generator. +// (Just because the CSAANoise object has a default samplerate +// doesn't mean you should rely on it) +// +////////////////////////////////////////////////////////////////////// +*/ + 250489 , 250980 , 251473 , 251969 , 252465 , 252964 , 253465 , 253968 , 254473 , 254980 , 255489 , 256000 , 256513 , 257028 , 257545 , 258065 , + 258586 , 259109 , 259635 , 260163 , 260692 , 261224 , 261759 , 262295 , 262834 , 263374 , 263918 , 264463 , 265010 , 265560 , 266112 , 266667 , + 267223 , 267782 , 268344 , 268908 , 269474 , 270042 , 270613 , 271186 , 271762 , 272340 , 272921 , 273504 , 274090 , 274678 , 275269 , 275862 , + 276458 , 277056 , 277657 , 278261 , 278867 , 279476 , 280088 , 280702 , 281319 , 281938 , 282561 , 283186 , 283814 , 284444 , 285078 , 285714 , + 286353 , 286996 , 287640 , 288288 , 288939 , 289593 , 290249 , 290909 , 291572 , 292237 , 292906 , 293578 , 294253 , 294931 , 295612 , 296296 , + 296984 , 297674 , 298368 , 299065 , 299766 , 300469 , 301176 , 301887 , 302600 , 303318 , 304038 , 304762 , 305489 , 306220 , 306954 , 307692 , + 308434 , 309179 , 309927 , 310680 , 311436 , 312195 , 312958 , 313725 , 314496 , 315271 , 316049 , 316832 , 317618 , 318408 , 319202 , 320000 , + 320802 , 321608 , 322418 , 323232 , 324051 , 324873 , 325700 , 326531 , 327366 , 328205 , 329049 , 329897 , 330749 , 331606 , 332468 , 333333 , + 334204 , 335079 , 335958 , 336842 , 337731 , 338624 , 339523 , 340426 , 341333 , 342246 , 343164 , 344086 , 345013 , 345946 , 346883 , 347826 , + 348774 , 349727 , 350685 , 351648 , 352617 , 353591 , 354571 , 355556 , 356546 , 357542 , 358543 , 359551 , 360563 , 361582 , 362606 , 363636 , + 364672 , 365714 , 366762 , 367816 , 368876 , 369942 , 371014 , 372093 , 373178 , 374269 , 375367 , 376471 , 377581 , 378698 , 379822 , 380952 , + 382090 , 383234 , 384384 , 385542 , 386707 , 387879 , 389058 , 390244 , 391437 , 392638 , 393846 , 395062 , 396285 , 397516 , 398754 , 400000 , + 401254 , 402516 , 403785 , 405063 , 406349 , 407643 , 408946 , 410256 , 411576 , 412903 , 414239 , 415584 , 416938 , 418301 , 419672 , 421053 , + 422442 , 423841 , 425249 , 426667 , 428094 , 429530 , 430976 , 432432 , 433898 , 435374 , 436860 , 438356 , 439863 , 441379 , 442907 , 444444 , + 445993 , 447552 , 449123 , 450704 , 452297 , 453901 , 455516 , 457143 , 458781 , 460432 , 462094 , 463768 , 465455 , 467153 , 468864 , 470588 , + 472325 , 474074 , 475836 , 477612 , 479401 , 481203 , 483019 , 484848 , 486692 , 488550 , 490421 , 492308 , 494208 , 496124 , 498054 , 500000 , + 500978 , 501961 , 502947 , 503937 , 504931 , 505929 , 506931 , 507937 , 508946 , 509960 , 510978 , 512000 , 513026 , 514056 , 515091 , 516129 , + 517172 , 518219 , 519270 , 520325 , 521385 , 522449 , 523517 , 524590 , 525667 , 526749 , 527835 , 528926 , 530021 , 531120 , 532225 , 533333 , + 534447 , 535565 , 536688 , 537815 , 538947 , 540084 , 541226 , 542373 , 543524 , 544681 , 545842 , 547009 , 548180 , 549356 , 550538 , 551724 , + 552916 , 554113 , 555315 , 556522 , 557734 , 558952 , 560175 , 561404 , 562637 , 563877 , 565121 , 566372 , 567627 , 568889 , 570156 , 571429 , + 572707 , 573991 , 575281 , 576577 , 577878 , 579186 , 580499 , 581818 , 583144 , 584475 , 585812 , 587156 , 588506 , 589862 , 591224 , 592593 , + 593968 , 595349 , 596737 , 598131 , 599532 , 600939 , 602353 , 603774 , 605201 , 606635 , 608076 , 609524 , 610979 , 612440 , 613909 , 615385 , + 616867 , 618357 , 619855 , 621359 , 622871 , 624390 , 625917 , 627451 , 628993 , 630542 , 632099 , 633663 , 635236 , 636816 , 638404 , 640000 , + 641604 , 643216 , 644836 , 646465 , 648101 , 649746 , 651399 , 653061 , 654731 , 656410 , 658098 , 659794 , 661499 , 663212 , 664935 , 666667 , + 668407 , 670157 , 671916 , 673684 , 675462 , 677249 , 679045 , 680851 , 682667 , 684492 , 686327 , 688172 , 690027 , 691892 , 693767 , 695652 , + 697548 , 699454 , 701370 , 703297 , 705234 , 707182 , 709141 , 711111 , 713092 , 715084 , 717087 , 719101 , 721127 , 723164 , 725212 , 727273 , + 729345 , 731429 , 733524 , 735632 , 737752 , 739884 , 742029 , 744186 , 746356 , 748538 , 750733 , 752941 , 755162 , 757396 , 759644 , 761905 , + 764179 , 766467 , 768769 , 771084 , 773414 , 775758 , 778116 , 780488 , 782875 , 785276 , 787692 , 790123 , 792570 , 795031 , 797508 , 800000 , + 802508 , 805031 , 807571 , 810127 , 812698 , 815287 , 817891 , 820513 , 823151 , 825806 , 828479 , 831169 , 833876 , 836601 , 839344 , 842105 , + 844884 , 847682 , 850498 , 853333 , 856187 , 859060 , 861953 , 864865 , 867797 , 870748 , 873720 , 876712 , 879725 , 882759 , 885813 , 888889 , + 891986 , 895105 , 898246 , 901408 , 904594 , 907801 , 911032 , 914286 , 917563 , 920863 , 924188 , 927536 , 930909 , 934307 , 937729 , 941176 , + 944649 , 948148 , 951673 , 955224 , 958801 , 962406 , 966038 , 969697 , 973384 , 977099 , 980843 , 984615 , 988417 , 992248 , 996109 , 1000000 , + 1001957 , 1003922 , 1005894 , 1007874 , 1009862 , 1011858 , 1013861 , 1015873 , 1017893 , 1019920 , 1021956 , 1024000 , 1026052 , 1028112 , 1030181 , 1032258 , + 1034343 , 1036437 , 1038540 , 1040650 , 1042770 , 1044898 , 1047035 , 1049180 , 1051335 , 1053498 , 1055670 , 1057851 , 1060041 , 1062241 , 1064449 , 1066667 , + 1068894 , 1071130 , 1073375 , 1075630 , 1077895 , 1080169 , 1082452 , 1084746 , 1087049 , 1089362 , 1091684 , 1094017 , 1096360 , 1098712 , 1101075 , 1103448 , + 1105832 , 1108225 , 1110629 , 1113043 , 1115468 , 1117904 , 1120350 , 1122807 , 1125275 , 1127753 , 1130243 , 1132743 , 1135255 , 1137778 , 1140312 , 1142857 , + 1145414 , 1147982 , 1150562 , 1153153 , 1155756 , 1158371 , 1160998 , 1163636 , 1166287 , 1168950 , 1171625 , 1174312 , 1177011 , 1179724 , 1182448 , 1185185 , + 1187935 , 1190698 , 1193473 , 1196262 , 1199063 , 1201878 , 1204706 , 1207547 , 1210402 , 1213270 , 1216152 , 1219048 , 1221957 , 1224880 , 1227818 , 1230769 , + 1233735 , 1236715 , 1239709 , 1242718 , 1245742 , 1248780 , 1251834 , 1254902 , 1257985 , 1261084 , 1264198 , 1267327 , 1270471 , 1273632 , 1276808 , 1280000 , + 1283208 , 1286432 , 1289673 , 1292929 , 1296203 , 1299492 , 1302799 , 1306122 , 1309463 , 1312821 , 1316195 , 1319588 , 1322997 , 1326425 , 1329870 , 1333333 , + 1336815 , 1340314 , 1343832 , 1347368 , 1350923 , 1354497 , 1358090 , 1361702 , 1365333 , 1368984 , 1372654 , 1376344 , 1380054 , 1383784 , 1387534 , 1391304 , + 1395095 , 1398907 , 1402740 , 1406593 , 1410468 , 1414365 , 1418283 , 1422222 , 1426184 , 1430168 , 1434174 , 1438202 , 1442254 , 1446328 , 1450425 , 1454545 , + 1458689 , 1462857 , 1467049 , 1471264 , 1475504 , 1479769 , 1484058 , 1488372 , 1492711 , 1497076 , 1501466 , 1505882 , 1510324 , 1514793 , 1519288 , 1523810 , + 1528358 , 1532934 , 1537538 , 1542169 , 1546828 , 1551515 , 1556231 , 1560976 , 1565749 , 1570552 , 1575385 , 1580247 , 1585139 , 1590062 , 1595016 , 1600000 , + 1605016 , 1610063 , 1615142 , 1620253 , 1625397 , 1630573 , 1635783 , 1641026 , 1646302 , 1651613 , 1656958 , 1662338 , 1667752 , 1673203 , 1678689 , 1684211 , + 1689769 , 1695364 , 1700997 , 1706667 , 1712375 , 1718121 , 1723906 , 1729730 , 1735593 , 1741497 , 1747440 , 1753425 , 1759450 , 1765517 , 1771626 , 1777778 , + 1783972 , 1790210 , 1796491 , 1802817 , 1809187 , 1815603 , 1822064 , 1828571 , 1835125 , 1841727 , 1848375 , 1855072 , 1861818 , 1868613 , 1875458 , 1882353 , + 1889299 , 1896296 , 1903346 , 1910448 , 1917603 , 1924812 , 1932075 , 1939394 , 1946768 , 1954198 , 1961686 , 1969231 , 1976834 , 1984496 , 1992218 , 2000000 , + 2003914 , 2007843 , 2011788 , 2015748 , 2019724 , 2023715 , 2027723 , 2031746 , 2035785 , 2039841 , 2043912 , 2048000 , 2052104 , 2056225 , 2060362 , 2064516 , + 2068687 , 2072874 , 2077079 , 2081301 , 2085540 , 2089796 , 2094070 , 2098361 , 2102669 , 2106996 , 2111340 , 2115702 , 2120083 , 2124481 , 2128898 , 2133333 , + 2137787 , 2142259 , 2146751 , 2151261 , 2155789 , 2160338 , 2164905 , 2169492 , 2174098 , 2178723 , 2183369 , 2188034 , 2192719 , 2197425 , 2202151 , 2206897 , + 2211663 , 2216450 , 2221258 , 2226087 , 2230937 , 2235808 , 2240700 , 2245614 , 2250549 , 2255507 , 2260486 , 2265487 , 2270510 , 2275556 , 2280624 , 2285714 , + 2290828 , 2295964 , 2301124 , 2306306 , 2311512 , 2316742 , 2321995 , 2327273 , 2332574 , 2337900 , 2343249 , 2348624 , 2354023 , 2359447 , 2364896 , 2370370 , + 2375870 , 2381395 , 2386946 , 2392523 , 2398126 , 2403756 , 2409412 , 2415094 , 2420804 , 2426540 , 2432304 , 2438095 , 2443914 , 2449761 , 2455635 , 2461538 , + 2467470 , 2473430 , 2479419 , 2485437 , 2491484 , 2497561 , 2503667 , 2509804 , 2515971 , 2522167 , 2528395 , 2534653 , 2540943 , 2547264 , 2553616 , 2560000 , + 2566416 , 2572864 , 2579345 , 2585859 , 2592405 , 2598985 , 2605598 , 2612245 , 2618926 , 2625641 , 2632391 , 2639175 , 2645995 , 2652850 , 2659740 , 2666667 , + 2673629 , 2680628 , 2687664 , 2694737 , 2701847 , 2708995 , 2716180 , 2723404 , 2730667 , 2737968 , 2745308 , 2752688 , 2760108 , 2767568 , 2775068 , 2782609 , + 2790191 , 2797814 , 2805479 , 2813187 , 2820937 , 2828729 , 2836565 , 2844444 , 2852368 , 2860335 , 2868347 , 2876404 , 2884507 , 2892655 , 2900850 , 2909091 , + 2917379 , 2925714 , 2934097 , 2942529 , 2951009 , 2959538 , 2968116 , 2976744 , 2985423 , 2994152 , 3002933 , 3011765 , 3020649 , 3029586 , 3038576 , 3047619 , + 3056716 , 3065868 , 3075075 , 3084337 , 3093656 , 3103030 , 3112462 , 3121951 , 3131498 , 3141104 , 3150769 , 3160494 , 3170279 , 3180124 , 3190031 , 3200000 , + 3210031 , 3220126 , 3230284 , 3240506 , 3250794 , 3261146 , 3271565 , 3282051 , 3292605 , 3303226 , 3313916 , 3324675 , 3335505 , 3346405 , 3357377 , 3368421 , + 3379538 , 3390728 , 3401993 , 3413333 , 3424749 , 3436242 , 3447811 , 3459459 , 3471186 , 3482993 , 3494881 , 3506849 , 3518900 , 3531034 , 3543253 , 3555556 , + 3567944 , 3580420 , 3592982 , 3605634 , 3618375 , 3631206 , 3644128 , 3657143 , 3670251 , 3683453 , 3696751 , 3710145 , 3723636 , 3737226 , 3750916 , 3764706 , + 3778598 , 3792593 , 3806691 , 3820896 , 3835206 , 3849624 , 3864151 , 3878788 , 3893536 , 3908397 , 3923372 , 3938462 , 3953668 , 3968992 , 3984436 , 4000000 , + 4007828 , 4015686 , 4023576 , 4031496 , 4039448 , 4047431 , 4055446 , 4063492 , 4071571 , 4079681 , 4087824 , 4096000 , 4104208 , 4112450 , 4120724 , 4129032 , + 4137374 , 4145749 , 4154158 , 4162602 , 4171079 , 4179592 , 4188139 , 4196721 , 4205339 , 4213992 , 4222680 , 4231405 , 4240166 , 4248963 , 4257796 , 4266667 , + 4275574 , 4284519 , 4293501 , 4302521 , 4311579 , 4320675 , 4329810 , 4338983 , 4348195 , 4357447 , 4366738 , 4376068 , 4385439 , 4394850 , 4404301 , 4413793 , + 4423326 , 4432900 , 4442516 , 4452174 , 4461874 , 4471616 , 4481400 , 4491228 , 4501099 , 4511013 , 4520971 , 4530973 , 4541020 , 4551111 , 4561247 , 4571429 , + 4581655 , 4591928 , 4602247 , 4612613 , 4623025 , 4633484 , 4643991 , 4654545 , 4665148 , 4675799 , 4686499 , 4697248 , 4708046 , 4718894 , 4729792 , 4740741 , + 4751740 , 4762791 , 4773893 , 4785047 , 4796253 , 4807512 , 4818824 , 4830189 , 4841608 , 4853081 , 4864608 , 4876190 , 4887828 , 4899522 , 4911271 , 4923077 , + 4934940 , 4946860 , 4958838 , 4970874 , 4982968 , 4995122 , 5007335 , 5019608 , 5031941 , 5044335 , 5056790 , 5069307 , 5081886 , 5094527 , 5107232 , 5120000 , + 5132832 , 5145729 , 5158690 , 5171717 , 5184810 , 5197970 , 5211196 , 5224490 , 5237852 , 5251282 , 5264781 , 5278351 , 5291990 , 5305699 , 5319481 , 5333333 , + 5347258 , 5361257 , 5375328 , 5389474 , 5403694 , 5417989 , 5432361 , 5446809 , 5461333 , 5475936 , 5490617 , 5505376 , 5520216 , 5535135 , 5550136 , 5565217 , + 5580381 , 5595628 , 5610959 , 5626374 , 5641873 , 5657459 , 5673130 , 5688889 , 5704735 , 5720670 , 5736695 , 5752809 , 5769014 , 5785311 , 5801700 , 5818182 , + 5834758 , 5851429 , 5868195 , 5885057 , 5902017 , 5919075 , 5936232 , 5953488 , 5970845 , 5988304 , 6005865 , 6023529 , 6041298 , 6059172 , 6077151 , 6095238 , + 6113433 , 6131737 , 6150150 , 6168675 , 6187311 , 6206061 , 6224924 , 6243902 , 6262997 , 6282209 , 6301538 , 6320988 , 6340557 , 6360248 , 6380062 , 6400000 , + 6420063 , 6440252 , 6460568 , 6481013 , 6501587 , 6522293 , 6543131 , 6564103 , 6585209 , 6606452 , 6627832 , 6649351 , 6671010 , 6692810 , 6714754 , 6736842 , + 6759076 , 6781457 , 6803987 , 6826667 , 6849498 , 6872483 , 6895623 , 6918919 , 6942373 , 6965986 , 6989761 , 7013699 , 7037801 , 7062069 , 7086505 , 7111111 , + 7135889 , 7160839 , 7185965 , 7211268 , 7236749 , 7262411 , 7288256 , 7314286 , 7340502 , 7366906 , 7393502 , 7420290 , 7447273 , 7474453 , 7501832 , 7529412 , + 7557196 , 7585185 , 7613383 , 7641791 , 7670412 , 7699248 , 7728302 , 7757576 , 7787072 , 7816794 , 7846743 , 7876923 , 7907336 , 7937984 , 7968872 , 8000000 , + 8015656 , 8031373 , 8047151 , 8062992 , 8078895 , 8094862 , 8110891 , 8126984 , 8143141 , 8159363 , 8175649 , 8192000 , 8208417 , 8224900 , 8241449 , 8258065 , + 8274747 , 8291498 , 8308316 , 8325203 , 8342159 , 8359184 , 8376278 , 8393443 , 8410678 , 8427984 , 8445361 , 8462810 , 8480331 , 8497925 , 8515593 , 8533333 , + 8551148 , 8569038 , 8587002 , 8605042 , 8623158 , 8641350 , 8659619 , 8677966 , 8696391 , 8714894 , 8733475 , 8752137 , 8770878 , 8789700 , 8808602 , 8827586 , + 8846652 , 8865801 , 8885033 , 8904348 , 8923747 , 8943231 , 8962801 , 8982456 , 9002198 , 9022026 , 9041943 , 9061947 , 9082040 , 9102222 , 9122494 , 9142857 , + 9163311 , 9183857 , 9204494 , 9225225 , 9246050 , 9266968 , 9287982 , 9309091 , 9330296 , 9351598 , 9372998 , 9394495 , 9416092 , 9437788 , 9459584 , 9481481 , + 9503480 , 9525581 , 9547786 , 9570093 , 9592506 , 9615023 , 9637647 , 9660377 , 9683215 , 9706161 , 9729216 , 9752381 , 9775656 , 9799043 , 9822542 , 9846154 , + 9869880 , 9893720 , 9917676 , 9941748 , 9965937 , 9990244 , 10014670 , 10039216 , 10063882 , 10088670 , 10113580 , 10138614 , 10163772 , 10189055 , 10214464 , 10240000 , + 10265664 , 10291457 , 10317380 , 10343434 , 10369620 , 10395939 , 10422392 , 10448980 , 10475703 , 10502564 , 10529563 , 10556701 , 10583979 , 10611399 , 10638961 , 10666667 , + 10694517 , 10722513 , 10750656 , 10778947 , 10807388 , 10835979 , 10864721 , 10893617 , 10922667 , 10951872 , 10981233 , 11010753 , 11040431 , 11070270 , 11100271 , 11130435 , + 11160763 , 11191257 , 11221918 , 11252747 , 11283747 , 11314917 , 11346260 , 11377778 , 11409471 , 11441341 , 11473389 , 11505618 , 11538028 , 11570621 , 11603399 , 11636364 , + 11669516 , 11702857 , 11736390 , 11770115 , 11804035 , 11838150 , 11872464 , 11906977 , 11941691 , 11976608 , 12011730 , 12047059 , 12082596 , 12118343 , 12154303 , 12190476 , + 12226866 , 12263473 , 12300300 , 12337349 , 12374622 , 12412121 , 12449848 , 12487805 , 12525994 , 12564417 , 12603077 , 12641975 , 12681115 , 12720497 , 12760125 , 12800000 , + 12840125 , 12880503 , 12921136 , 12962025 , 13003175 , 13044586 , 13086262 , 13128205 , 13170418 , 13212903 , 13255663 , 13298701 , 13342020 , 13385621 , 13429508 , 13473684 , + 13518152 , 13562914 , 13607973 , 13653333 , 13698997 , 13744966 , 13791246 , 13837838 , 13884746 , 13931973 , 13979522 , 14027397 , 14075601 , 14124138 , 14173010 , 14222222 , + 14271777 , 14321678 , 14371930 , 14422535 , 14473498 , 14524823 , 14576512 , 14628571 , 14681004 , 14733813 , 14787004 , 14840580 , 14894545 , 14948905 , 15003663 , 15058824 , + 15114391 , 15170370 , 15226766 , 15283582 , 15340824 , 15398496 , 15456604 , 15515152 , 15574144 , 15633588 , 15693487 , 15753846 , 15814672 , 15875969 , 15937743 , 16000000 , + 16031311 , 16062745 , 16094303 , 16125984 , 16157791 , 16189723 , 16221782 , 16253968 , 16286282 , 16318725 , 16351297 , 16384000 , 16416834 , 16449799 , 16482897 , 16516129 , + 16549495 , 16582996 , 16616633 , 16650407 , 16684318 , 16718367 , 16752556 , 16786885 , 16821355 , 16855967 , 16890722 , 16925620 , 16960663 , 16995851 , 17031185 , 17066667 , + 17102296 , 17138075 , 17174004 , 17210084 , 17246316 , 17282700 , 17319239 , 17355932 , 17392781 , 17429787 , 17466951 , 17504274 , 17541756 , 17579399 , 17617204 , 17655172 , + 17693305 , 17731602 , 17770065 , 17808696 , 17847495 , 17886463 , 17925602 , 17964912 , 18004396 , 18044053 , 18083885 , 18123894 , 18164080 , 18204444 , 18244989 , 18285714 , + 18326622 , 18367713 , 18408989 , 18450450 , 18492099 , 18533937 , 18575964 , 18618182 , 18660592 , 18703196 , 18745995 , 18788991 , 18832184 , 18875576 , 18919169 , 18962963 , + 19006961 , 19051163 , 19095571 , 19140187 , 19185012 , 19230047 , 19275294 , 19320755 , 19366430 , 19412322 , 19458432 , 19504762 , 19551313 , 19598086 , 19645084 , 19692308 , + 19739759 , 19787440 , 19835351 , 19883495 , 19931873 , 19980488 , 20029340 , 20078431 , 20127764 , 20177340 , 20227160 , 20277228 , 20327543 , 20378109 , 20428928 , 20480000 , + 20531328 , 20582915 , 20634761 , 20686869 , 20739241 , 20791878 , 20844784 , 20897959 , 20951407 , 21005128 , 21059126 , 21113402 , 21167959 , 21222798 , 21277922 , 21333333 , + 21389034 , 21445026 , 21501312 , 21557895 , 21614776 , 21671958 , 21729443 , 21787234 , 21845333 , 21903743 , 21962466 , 22021505 , 22080863 , 22140541 , 22200542 , 22260870 , + 22321526 , 22382514 , 22443836 , 22505495 , 22567493 , 22629834 , 22692521 , 22755556 , 22818942 , 22882682 , 22946779 , 23011236 , 23076056 , 23141243 , 23206799 , 23272727 , + 23339031 , 23405714 , 23472779 , 23540230 , 23608069 , 23676301 , 23744928 , 23813953 , 23883382 , 23953216 , 24023460 , 24094118 , 24165192 , 24236686 , 24308605 , 24380952 , + 24453731 , 24526946 , 24600601 , 24674699 , 24749245 , 24824242 , 24899696 , 24975610 , 25051988 , 25128834 , 25206154 , 25283951 , 25362229 , 25440994 , 25520249 , 25600000 , + 25680251 , 25761006 , 25842271 , 25924051 , 26006349 , 26089172 , 26172524 , 26256410 , 26340836 , 26425806 , 26511327 , 26597403 , 26684039 , 26771242 , 26859016 , 26947368 , + 27036304 , 27125828 , 27215947 , 27306667 , 27397993 , 27489933 , 27582492 , 27675676 , 27769492 , 27863946 , 27959044 , 28054795 , 28151203 , 28248276 , 28346021 , 28444444 , + 28543554 , 28643357 , 28743860 , 28845070 , 28946996 , 29049645 , 29153025 , 29257143 , 29362007 , 29467626 , 29574007 , 29681159 , 29789091 , 29897810 , 30007326 , 30117647 , + 30228782 , 30340741 , 30453532 , 30567164 , 30681648 , 30796992 , 30913208 , 31030303 , 31148289 , 31267176 , 31386973 , 31507692 , 31629344 , 31751938 , 31875486 , 32000000 , + 32062622 , 32125490 , 32188605 , 32251969 , 32315582 , 32379447 , 32443564 , 32507937 , 32572565 , 32637450 , 32702595 , 32768000 , 32833667 , 32899598 , 32965795 , 33032258 , + 33098990 , 33165992 , 33233266 , 33300813 , 33368635 , 33436735 , 33505112 , 33573770 , 33642710 , 33711934 , 33781443 , 33851240 , 33921325 , 33991701 , 34062370 , 34133333 , + 34204593 , 34276151 , 34348008 , 34420168 , 34492632 , 34565401 , 34638478 , 34711864 , 34785563 , 34859574 , 34933902 , 35008547 , 35083512 , 35158798 , 35234409 , 35310345 , + 35386609 , 35463203 , 35540130 , 35617391 , 35694989 , 35772926 , 35851204 , 35929825 , 36008791 , 36088106 , 36167770 , 36247788 , 36328160 , 36408889 , 36489978 , 36571429 , + 36653244 , 36735426 , 36817978 , 36900901 , 36984199 , 37067873 , 37151927 , 37236364 , 37321185 , 37406393 , 37491991 , 37577982 , 37664368 , 37751152 , 37838337 , 37925926 , + 38013921 , 38102326 , 38191142 , 38280374 , 38370023 , 38460094 , 38550588 , 38641509 , 38732861 , 38824645 , 38916865 , 39009524 , 39102625 , 39196172 , 39290168 , 39384615 , + 39479518 , 39574879 , 39670702 , 39766990 , 39863747 , 39960976 , 40058680 , 40156863 , 40255528 , 40354680 , 40454321 , 40554455 , 40655087 , 40756219 , 40857855 , 40960000 , + 41062657 , 41165829 , 41269521 , 41373737 , 41478481 , 41583756 , 41689567 , 41795918 , 41902813 , 42010256 , 42118252 , 42226804 , 42335917 , 42445596 , 42555844 , 42666667 , + 42778068 , 42890052 , 43002625 , 43115789 , 43229551 , 43343915 , 43458886 , 43574468 , 43690667 , 43807487 , 43924933 , 44043011 , 44161725 , 44281081 , 44401084 , 44521739 , + 44643052 , 44765027 , 44887671 , 45010989 , 45134986 , 45259669 , 45385042 , 45511111 , 45637883 , 45765363 , 45893557 , 46022472 , 46152113 , 46282486 , 46413598 , 46545455 , + 46678063 , 46811429 , 46945559 , 47080460 , 47216138 , 47352601 , 47489855 , 47627907 , 47766764 , 47906433 , 48046921 , 48188235 , 48330383 , 48473373 , 48617211 , 48761905 , + 48907463 , 49053892 , 49201201 , 49349398 , 49498489 , 49648485 , 49799392 , 49951220 , 50103976 , 50257669 , 50412308 , 50567901 , 50724458 , 50881988 , 51040498 , 51200000 , + 51360502 , 51522013 , 51684543 , 51848101 , 52012698 , 52178344 , 52345048 , 52512821 , 52681672 , 52851613 , 53022654 , 53194805 , 53368078 , 53542484 , 53718033 , 53894737 , + 54072607 , 54251656 , 54431894 , 54613333 , 54795987 , 54979866 , 55164983 , 55351351 , 55538983 , 55727891 , 55918089 , 56109589 , 56302405 , 56496552 , 56692042 , 56888889 , + 57087108 , 57286713 , 57487719 , 57690141 , 57893993 , 58099291 , 58306050 , 58514286 , 58724014 , 58935252 , 59148014 , 59362319 , 59578182 , 59795620 , 60014652 , 60235294 , + 60457565 , 60681481 , 60907063 , 61134328 , 61363296 , 61593985 , 61826415 , 62060606 , 62296578 , 62534351 , 62773946 , 63015385 , 63258687 , 63503876 , 63750973 , 64000000 diff --git a/extern/SAASound/src/SAAFreq.h b/extern/SAASound/src/SAAFreq.h new file mode 100755 index 00000000..47875462 --- /dev/null +++ b/extern/SAASound/src/SAAFreq.h @@ -0,0 +1,72 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// SAAFreq.h: interface for the CSAAFreq class. +// Note about Samplerates: 0=44100, 1=22050; 2=11025 +// +////////////////////////////////////////////////////////////////////// + +#ifndef SAAFREQ_H_INCLUDE +#define SAAFREQ_H_INCLUDE + +#include "defns.h" + +class CSAAFreq +{ +private: +#ifdef SAAFREQ_FIXED_CLOCKRATE + // 'load in' the data for the static frequency lookup table + // precomputed for a fixed clockrate + // See: tools/freqdat.py + const static unsigned long m_FreqTable[2048]; +#else + // we'll calculate the frequency lookup table at runtime. + static unsigned long m_FreqTable[2048]; + static unsigned long m_nClockRate; +#endif + + unsigned long m_nCounter; + unsigned long m_nAdd; + unsigned long m_nCounter_low; + unsigned int m_nOversample; + unsigned long m_nCounterLimit_low; + int m_nLevel; + + int m_nCurrentOffset; + int m_nCurrentOctave; + int m_nNextOffset; + int m_nNextOctave; + bool m_bIgnoreOffsetData; + bool m_bNewData; + bool m_bSync; + + unsigned long m_nSampleRate; + CSAANoise * const m_pcConnectedNoiseGenerator; + CSAAEnv * const m_pcConnectedEnvGenerator; + const int m_nConnectedMode; // 0 = nothing; 1 = envgenerator; 2 = noisegenerator + + void UpdateOctaveOffsetData(void); + void SetAdd(void); + +public: + CSAAFreq(CSAANoise * const pcNoiseGenerator, CSAAEnv * const pcEnvGenerator); + ~CSAAFreq(); + void SetFreqOffset(BYTE nOffset); + void SetFreqOctave(BYTE nOctave); + void _SetSampleRate(unsigned int nSampleRate); + void _SetOversample(unsigned int oversample); + void _SetClockRate(int nClockRate); + void Sync(bool bSync); + int Tick(void); + int Level(void) const; + +}; + +inline int CSAAFreq::Level(void) const +{ + if (m_bSync) + return 1; + + return m_nLevel; +} + +#endif // SAAFREQ_H_INCLUDE diff --git a/extern/SAASound/src/SAAImpl.cpp b/extern/SAASound/src/SAAImpl.cpp new file mode 100644 index 00000000..455a6a18 --- /dev/null +++ b/extern/SAASound/src/SAAImpl.cpp @@ -0,0 +1,484 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// SAAImpl.cpp: implementation of the CSAASound class. +// the bones of the 'virtual SAA-1099' emulation +// +// the actual sound generation is carried out in the other classes; +// this class provides the output stage and the external interface only +// +////////////////////////////////////////////////////////////////////// + +#include "SAASound.h" + +#include "types.h" +#include "SAAImpl.h" +#include "defns.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CSAASoundInternal::CSAASoundInternal() + : +m_chip(), +m_uParam(0), +m_uParamRate(0), +m_nClockRate(EXTERNAL_CLK_HZ), +m_nSampleRate(SAMPLE_RATE_HZ), +m_nOversample(DEFAULT_OVERSAMPLE), +m_bHighpass(false) +{ +#ifdef USE_CONFIG_FILE + m_Config.ReadConfig(); +#endif + +#if defined(DEBUGSAA) + m_dbgfile.open(_T(DEBUG_SAA_REGISTER_LOG), std::ios_base::out); + m_pcmfile.open(_T(DEBUG_SAA_PCM_LOG), std::ios_base::out | std::ios_base::binary); +#elif defined(USE_CONFIG_FILE) + if (m_Config.m_bGenerateRegisterLogs) + m_dbgfile.open(m_Config.m_strRegisterLogPath, std::ios_base::out); + if (m_Config.m_bGeneratePcmLogs) + m_pcmfile.open(m_Config.m_strPcmOutputPath, std::ios_base::out | std::ios_base::binary); + + if (m_Config.m_bGeneratePcmLogs && m_Config.m_bGeneratePcmSeparateChannels) + { + for (int i = 0; i < 6; i++) + { + m_channel_pcmfile[i].open(m_Config.getChannelPcmOutputPath(i), std::ios_base::out | std::ios_base::binary); + } + } + + +#endif + // set parameters + // TODO support defaults and overrides from config file + // m_chip.SetSoundParameters(SAAP_FILTER | SAAP_11025 | SAAP_8BIT | SAAP_MONO); + // reset the virtual SAA + // m_chip.Clear(); + + m_chip._SetClockRate(m_nClockRate); + m_chip._SetOversample(m_nOversample); +} + +CSAASoundInternal::~CSAASoundInternal() +{ + // +} + +////////////////////////////////////////////////////////////////////// +// CSAASound members +////////////////////////////////////////////////////////////////////// + +void CSAASoundInternal::SetClockRate(unsigned int nClockRate) +{ + m_nClockRate = nClockRate; + m_chip._SetClockRate(m_nClockRate); +} + +void CSAASoundInternal::Clear(void) +{ + // reinitialises virtual SAA: + // sets reg 28 to 0x02; - sync and disabled + // sets regs 00-31 (except 28) to 0x00; + // sets reg 28 to 0x00; + // sets current reg to 0 + WriteAddressData(28,2); + for (int i=31; i>=0; i--) + { + if (i!=28) WriteAddressData(i,0); + } + WriteAddressData(28,0); + WriteAddress(0); +} + +void CSAASoundInternal::WriteData(BYTE nData) +{ + // originated from an OUT 255,d call + m_chip._WriteData(nData); +#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE) +#ifdef USE_CONFIG_FILE + if (m_Config.m_bGenerateRegisterLogs) + { +#endif + m_dbgfile << m_nDebugSample << " " << (int)m_chip._ReadAddress() << ":" << (int)nData << std::endl; +#ifdef USE_CONFIG_FILE + } +#endif +#endif +} + +void CSAASoundInternal::WriteAddress(BYTE nReg) +{ + // originated from an OUT 511,r call + m_chip._WriteAddress(nReg); +#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE) +#ifdef USE_CONFIG_FILE + if (m_Config.m_bGenerateRegisterLogs) + { +#endif + m_dbgfile << m_nDebugSample << " " << (int)nReg << ":"; + if (nReg==24) + { + m_dbgfile << ""; + } + else if (nReg==25) + { + m_dbgfile << ""; + } + m_dbgfile << std::endl; +#ifdef USE_CONFIG_FILE + } +#endif +#endif +} + +void CSAASoundInternal::WriteAddressData(BYTE nReg, BYTE nData) +{ + // performs WriteAddress(nReg) followed by WriteData(nData) + m_chip._WriteAddress(nReg); + m_chip._WriteData(nData); +} + +#ifdef DEBUG +BYTE CSAASoundInternal::ReadAddress(void) +{ + // Not a real hardware function of the SAA-1099, which is write-only + return(m_chip._ReadAddress()); +} +#else +BYTE CSAASoundInternal::ReadAddress(void) +{ + // Not a real hardware function of the SAA-1099, which is write-only + return(0); +} +#endif + +void CSAASoundInternal::SetSoundParameters(SAAPARAM uParam) +{ + // set samplerate properties from uParam (deprecated but still supported) + unsigned int nSampleRate = m_nSampleRate; + switch (uParam & SAAP_MASK_SAMPLERATE) + { + case SAAP_44100: + nSampleRate = 44100; + m_uParamRate = (m_uParamRate & ~SAAP_MASK_SAMPLERATE) | SAAP_44100; + break; + case SAAP_22050: + nSampleRate = 22050; + m_uParamRate = (m_uParamRate & ~SAAP_MASK_SAMPLERATE) | SAAP_22050; + break; + case SAAP_11025: + nSampleRate = 11025; + m_uParamRate = (m_uParamRate & ~SAAP_MASK_SAMPLERATE) | SAAP_11025; + break; + case 0:// change nothing! + default: + break; + } + + if (nSampleRate != m_nSampleRate) + { + m_nSampleRate = nSampleRate; + m_chip._SetSampleRate(m_nSampleRate); + } + + // set filter properties from uParam + m_uParam = (m_uParam & ~SAAP_MASK_FILTER) | (uParam & SAAP_MASK_FILTER); + + m_bHighpass=true; +} + +void CSAASoundInternal::SetSampleRate(unsigned int nSampleRate) +{ + if (nSampleRate != m_nSampleRate) + { + m_nSampleRate = nSampleRate; + m_chip._SetSampleRate(m_nSampleRate); + } +} + +void CSAASoundInternal::SetOversample(unsigned int nOversample) +{ + if (nOversample != m_nOversample) + { + m_nOversample = nOversample; + m_chip._SetOversample(m_nOversample); + } +} + +SAAPARAM CSAASoundInternal::GetCurrentSoundParameters(void) +{ + return m_uParam | m_uParamRate; +} + +unsigned short CSAASoundInternal::GetCurrentBytesPerSample(void) +{ + // 16 bit stereo => 4 bytes per sample + return 4; +} + +/*static*/ unsigned short CSAASound::GetBytesPerSample(SAAPARAM uParam) +{ + // 16 bit stereo => 4 bytes per sample + switch (uParam & (SAAP_MASK_CHANNELS | SAAP_MASK_BITDEPTH)) + { + case SAAP_STEREO | SAAP_16BIT: + return 4; + default: + return 0; + } +} + +unsigned long CSAASoundInternal::GetCurrentSampleRate(void) +{ + return CSAASound::GetSampleRate(m_uParamRate); +} + +/*static*/ unsigned long CSAASound::GetSampleRate(SAAPARAM uParam) // static member function +{ + switch (uParam & SAAP_MASK_SAMPLERATE) + { + case SAAP_11025: + return 11025; + case SAAP_22050: + return 22050; + case SAAP_44100: + return 44100; + default: + return 0; + } +} + +#if defined(USE_CONFIG_FILE) || (defined(DEFAULT_BOOST) && DEFAULT_BOOST>1) +#define DO_BOOST +#endif + +void scale_for_output(unsigned int left_input, unsigned int right_input, + double oversample_scalar, bool highpass, double boost, + double& filterout_z1_left, double& filterout_z1_right, + BYTE* &pBuffer) +{ + double float_left = (double)left_input; + double float_right = (double)right_input; + float_left /= oversample_scalar; + float_right /= oversample_scalar; + + // scale output into good range + float_left *= DEFAULT_UNBOOSTED_MULTIPLIER; + float_right *= DEFAULT_UNBOOSTED_MULTIPLIER; + + if (highpass) + { + /* cutoff = 5 Hz (say) + const double b1 = exp(-2.0 * M_PI * (Fc/Fs)) + const double a0 = 1.0 - b1; + */ + const double b1 = 0.99928787; + const double a0 = 1.0 - b1; + + filterout_z1_left = float_left * a0 + filterout_z1_left * b1; + filterout_z1_right = float_right * a0 + filterout_z1_right * b1; + float_left -= filterout_z1_left; + float_right -= filterout_z1_right; + } + + // multiply by boost, if defined +#if defined(DO_BOOST) + float_left *= boost; + float_right *= boost; +#endif + // convert to 16-bit signed range with hard clipping + signed short left_output = (signed short)(float_left > 32767 ? 32767 : float_left < -32768 ? -32768 : float_left); + signed short right_output = (signed short)(float_right > 32767 ? 32767 : float_right < -32768 ? -32768 : float_right); + + *pBuffer++ = left_output & 0x00ff; + *pBuffer++ = (left_output >> 8) & 0x00ff; + *pBuffer++ = right_output & 0x00ff; + *pBuffer++ = (right_output >> 8) & 0x00ff; +} + +void CSAASoundInternal::GenerateMany(BYTE* pBuffer, unsigned long nSamples) +{ + unsigned int left_mixed, right_mixed; + static double filterout_z1_left_mixed = 0, filterout_z1_right_mixed = 0; + +#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE) + BYTE* pBufferStart = pBuffer; + unsigned long nTotalSamples = nSamples; +#endif + +#if defined(DO_BOOST) +#if defined(USE_CONFIG_FILE) + double nBoost = m_Config.m_nBoost; +#else + double nBoost = DEFAULT_BOOST; +#endif +#else + double nBoost = DEFAULT_BOOST; +#endif + + double oversample = double(1 << m_nOversample); + +#if defined(USE_CONFIG_FILE) + static double filterout_z1_left_0 = 0, filterout_z1_right_0 = 0; + static double filterout_z1_left_1 = 0, filterout_z1_right_1 = 0; + static double filterout_z1_left_2 = 0, filterout_z1_right_2 = 0; + static double filterout_z1_left_3 = 0, filterout_z1_right_3 = 0; + static double filterout_z1_left_4 = 0, filterout_z1_right_4 = 0; + static double filterout_z1_left_5 = 0, filterout_z1_right_5 = 0; + + if (m_Config.m_bGeneratePcmLogs && m_Config.m_bGeneratePcmSeparateChannels) + { + unsigned int left0, right0, left1, right1, left2, right2, left3, right3, left4, right4, left5, right5; + BYTE* pChannelBufferPtr[6] = { m_pChannelBuffer[0], m_pChannelBuffer[1], m_pChannelBuffer[2], m_pChannelBuffer[3], m_pChannelBuffer[4], m_pChannelBuffer[5] }; + + while (nSamples--) + { + m_chip._TickAndOutputSeparate(left_mixed, right_mixed, + left0, right0, + left1, right1, + left2, right2, + left3, right3, + left4, right4, + left5, right5); + scale_for_output(left_mixed, right_mixed, oversample, m_bHighpass, nBoost, filterout_z1_left_mixed, filterout_z1_right_mixed, pBuffer); + + // and the separate channels + scale_for_output(left0, right0, oversample, m_bHighpass, nBoost, filterout_z1_left_0, filterout_z1_right_0, pChannelBufferPtr[0]); + scale_for_output(left1, right1, oversample, m_bHighpass, nBoost, filterout_z1_left_1, filterout_z1_right_1, pChannelBufferPtr[1]); + scale_for_output(left2, right2, oversample, m_bHighpass, nBoost, filterout_z1_left_2, filterout_z1_right_2, pChannelBufferPtr[2]); + scale_for_output(left3, right3, oversample, m_bHighpass, nBoost, filterout_z1_left_3, filterout_z1_right_3, pChannelBufferPtr[3]); + scale_for_output(left4, right4, oversample, m_bHighpass, nBoost, filterout_z1_left_4, filterout_z1_right_4, pChannelBufferPtr[4]); + scale_for_output(left5, right5, oversample, m_bHighpass, nBoost, filterout_z1_left_5, filterout_z1_right_5, pChannelBufferPtr[5]); + + // flush channel output PCM buffers when full + if (pChannelBufferPtr[0] >= m_pChannelBuffer[0] + CHANNEL_BUFFER_SIZE) + { + for (int i = 0; i < 6; i++) + { + m_channel_pcmfile[i].write((const char*)m_pChannelBuffer[i], CHANNEL_BUFFER_SIZE); + pChannelBufferPtr[i] = m_pChannelBuffer[i]; + } + } + } + // flush remaining channel PCM output data + if (pChannelBufferPtr[0] >= m_pChannelBuffer[0]) + { + for (int i = 0; i < 6; i++) + { + m_channel_pcmfile[i].write((const char*)m_pChannelBuffer[i], pChannelBufferPtr[i]-m_pChannelBuffer[i]); + } + } + } + else + { +#endif + while (nSamples--) + { + m_chip._TickAndOutputStereo(left_mixed, right_mixed); + scale_for_output(left_mixed, right_mixed, oversample, m_bHighpass, nBoost, filterout_z1_left_mixed, filterout_z1_right_mixed, pBuffer); + } + +#if defined(USE_CONFIG_FILE) + } +#endif + +#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE) +#ifdef USE_CONFIG_FILE + if (m_Config.m_bGeneratePcmLogs) + { +#endif + m_pcmfile.write((const char *)pBufferStart, nTotalSamples * (unsigned long)GetCurrentBytesPerSample()); + m_nDebugSample += nTotalSamples; +#ifdef USE_CONFIG_FILE + } +#endif + +#endif +} + +/////////////////////////////////////////////////////// + +LPCSAASOUND SAAAPI CreateCSAASound(void) +{ + return (new CSAASoundInternal); +} + +void SAAAPI DestroyCSAASound(LPCSAASOUND object) +{ + delete (object); +} + + +/* thoughts on lowpass filtering as part of oversampling. +I tried this and really it didn't seem to make a lot of (audible) difference. + +// lowpass oversample filter adds complexity and not particularly audibly better than simple averaging. +// use_lowpass_oversample_filter_average_output adds an additional averaging step to the output of the oversample +// filter. this seems critical, because without this, the raw output of the lowpass filter is full of aliases +// If use_lowpass_oversample_filter is False, then the _average_output flag is ignored. +// Default, use_lowpass_oversample_filter is False, it sounds just fine really. + +//#define USE_LOWPASS_OVERSAMPLE_FILTER +#undef USE_LOWPASS_OVERSAMPLE_FILTER +//#define USE_LOWPASS_OVERSAMPLE_FILTER_AVERAGE_OUTPUT +#undef USE_LOWPASS_OVERSAMPLE_FILTER_AVERAGE_OUTPUT + +#ifdef USE_LOWPASS_OVERSAMPLE_FILTER +static double oversample_lp_filterout_z1_left_stages[10] = { 0,0,0,0,0,0,0,0,0,0 }; +static double oversample_lp_filterout_z1_right_stages[10] = { 0,0,0,0,0,0,0,0,0,0 }; +double averaged_filterout_left = 0.0, averaged_filterout_right = 0.0; +const int nStages = 10; +for (int i = 0; i < 1 << m_nOversample; i++) +{ + Noise[0]->Tick(); + Noise[1]->Tick(); + f_left = f_right = 0; + for (int c = 0; c < 6; c++) + { + Amp[c]->TickAndOutputStereo(temp_left, temp_right); + f_left += (double)temp_left; + f_right += (double)temp_right; + } + // apply lowpass here. + // HACK: ASSUME m_nOversample is 64 (I was experimenting only using the 64x oversample anyway) + // therefore Fs = 44100*64 + // let's set Fc = 10kHz + // so Fc/Fs = 0.00354308390022675736961451247166 + // const double b1 = exp(-2.0 * M_PI * (Fc/Fs)) + // const double a0 = 1.0 - b1; + // const double b1 = 0.9779841137335348363722276130195; + const double b1 = 0.977; + const double a0 = 1.0 - b1; + + oversample_lp_filterout_z1_left_stages[0] = f_left * a0 + oversample_lp_filterout_z1_left_stages[0] * b1; + for (int stage = 1; stage < nStages; stage++) + oversample_lp_filterout_z1_left_stages[stage] = oversample_lp_filterout_z1_left_stages[stage - 1] * a0 + oversample_lp_filterout_z1_left_stages[stage] * b1; + oversample_lp_filterout_z1_right_stages[0] = f_right * a0 + oversample_lp_filterout_z1_right_stages[0] * b1; + for (int stage = 1; stage < nStages; stage++) + oversample_lp_filterout_z1_right_stages[stage] = oversample_lp_filterout_z1_right_stages[stage - 1] * a0 + oversample_lp_filterout_z1_right_stages[stage] * b1; + +#ifdef USE_LOWPASS_OVERSAMPLE_FILTER_AVERAGE_OUTPUT + averaged_filterout_left += oversample_lp_filterout_4z1_left; + averaged_filterout_right += oversample_lp_filterout_4z1_right; +#endif +} + +// by the end of this loop we will have computed the oversample lowpass filter m_nOversample times +// and yielded exactly ONE sample output. +#ifdef USE_LOWPASS_OVERSAMPLE_FILTER_AVERAGE_OUTPUT +f_left = averaged_filterout_left / (1 << m_nOversample); +f_right = averaged_filterout_right / (1 << m_nOversample); +#else +f_left = oversample_lp_filterout_z1_left_stages[nStages - 1]; +f_right = oversample_lp_filterout_z1_right_stages[nStages - 1]; +#endif + +#else + // do the simple 1/N averaging which is easier and sounds good enough + +#endif + +*/ + diff --git a/extern/SAASound/src/SAAImpl.h b/extern/SAASound/src/SAAImpl.h new file mode 100755 index 00000000..61fa79c5 --- /dev/null +++ b/extern/SAASound/src/SAAImpl.h @@ -0,0 +1,75 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// This is the internal implementation (header file) of the SAASound object. +// This is done so that the external interface to the object always stays the same +// (SAASound.h) even though the internal object can change +// .. Meaning future releases don't require relinking everyone elses code against +// the updated saasound stuff +// +////////////////////////////////////////////////////////////////////// + +#ifndef SAAIMPL_H_INCLUDED +#define SAAIMPL_H_INCLUDED + +#include "SAASound.h" +#include "SAADevice.h" +#ifdef USE_CONFIG_FILE +#include "SAAConfig.h" +#endif + +#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE) +#include +#include +#include + +#if defined(USE_CONFIG_FILE) +const int CHANNEL_BUFFER_SIZE=1024; +#endif +#endif + +class CSAASoundInternal : public CSAASound +{ +private: + CSAADevice m_chip; + int m_uParam, m_uParamRate; + unsigned int m_nClockRate; + unsigned int m_nSampleRate; + unsigned int m_nOversample; + bool m_bHighpass; +#ifdef USE_CONFIG_FILE + SAAConfig m_Config; +#endif +#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE) + unsigned long m_nDebugSample; + std::ofstream m_dbgfile, m_pcmfile; +#if defined(USE_CONFIG_FILE) + std::ofstream m_channel_pcmfile[6]; + BYTE m_pChannelBuffer[6][CHANNEL_BUFFER_SIZE]; +#endif +#endif + +public: + CSAASoundInternal(); + ~CSAASoundInternal(); + + void SetClockRate(unsigned int nClockRate); + void SetSampleRate(unsigned int nClockRate); + void SetOversample(unsigned int nOversample); + void SetSoundParameters(SAAPARAM uParam); + void WriteAddress(BYTE nReg); + void WriteData(BYTE nData); + void WriteAddressData(BYTE nReg, BYTE nData); + BYTE ReadAddress(void); + void Clear(void); + + SAAPARAM GetCurrentSoundParameters(void); + unsigned long GetCurrentSampleRate(void); + static unsigned long GetSampleRate(SAAPARAM uParam); + unsigned short GetCurrentBytesPerSample(void); + static unsigned short GetBytesPerSample(SAAPARAM uParam); + + void GenerateMany(BYTE * pBuffer, unsigned long nSamples); + +}; + +#endif // SAAIMPL_H_INCLUDED diff --git a/extern/SAASound/src/SAANoise.cpp b/extern/SAASound/src/SAANoise.cpp new file mode 100755 index 00000000..b360ba83 --- /dev/null +++ b/extern/SAASound/src/SAANoise.cpp @@ -0,0 +1,180 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// SAANoise.cpp: implementation of the CSAANoise class. +// One noise generator +// +// After construction, it's important to SetSampleRate before +// trying to use the generator. +// (Just because the CSAANoise object has a default samplerate +// doesn't mean you should rely on it) +// +////////////////////////////////////////////////////////////////////// + +#include "SAASound.h" + +#include "types.h" +#include "SAANoise.h" +#include "defns.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CSAANoise::CSAANoise() +: +m_nCounter(0), +m_nCounter_low(0), +m_nOversample(0), +m_nCounterLimit_low(1), +m_bSync(false), +m_nSampleRate(SAMPLE_RATE_HZ), +m_nSourceMode(0), +m_nRand(1) +{ + _SetClockRate(EXTERNAL_CLK_HZ); + m_nAdd = m_nAddBase; +} + +CSAANoise::CSAANoise(unsigned long seed) +: +m_nCounter(0), +m_nCounter_low(0), +m_nOversample(0), +m_nCounterLimit_low(1), +m_bSync(false), +m_nSampleRate(SAMPLE_RATE_HZ), +m_nSourceMode(0), +m_nRand(seed) +{ + _SetClockRate(EXTERNAL_CLK_HZ); + m_nAdd = m_nAddBase; +} + +CSAANoise::~CSAANoise() +{ + // Nothing to do +} + +void CSAANoise::_SetClockRate(int nClockRate) +{ + // at 8MHz the clock rate is 31.250kHZ + // This is simply the clock rate divided by 256 i.e. 2^8 + // We then shift this by 2^12 (like the Freq) for better + // period accuracy. So that's the same as shifting by (12-8) + m_nAddBase = nClockRate << (12 - 8); +} + +void CSAANoise::Seed(unsigned long seed) +{ + m_nRand = seed; +} + +void CSAANoise::SetSource(int nSource) +{ + m_nSourceMode = nSource; + m_nAdd = m_nAddBase >> m_nSourceMode; +} + +void CSAANoise::Trigger(void) +{ + // Trigger only does anything useful when we're + // clocking from the frequency generator - i.e + // if bUseFreqGen = true (i.e. SourceMode = 3) + + // So if we're clocking from the noise generator + // clock (ie, SourceMode = 0, 1 or 2) then do nothing + +// No point actually checking m_bSync here ... because if sync is true, +// then frequency generators won't actually be generating Trigger pulses +// so we wouldn't even get here! + // EXCEPT - cool edge case: if sync is set, then actually the Noise Generator + // is triggered on EVERY CLOCK PULSE (i.e. 8MHz noise). So indeed it is correct + // to not check for sync here. NEEDS TEST CASE. + + if (m_nSourceMode == 3) + { + ChangeLevel(); + } +} + +void CSAANoise::Tick(void) +{ + // Tick only does anything useful when we're + // clocking from the noise generator clock + // (ie, SourceMode = 0, 1 or 2) + + // So, if SourceMode = 3 (ie, we're clocking from a + // frequency generator ==> bUseFreqGen = true) + // then do nothing + if ( (!m_bSync) && (m_nSourceMode!=3) ) + { + m_nCounter += m_nAdd; + while (m_nCounter >= (m_nSampleRate<<12)) + { + m_nCounter -= (m_nSampleRate<<12); + m_nCounter_low++; + if (m_nCounter_low >= m_nCounterLimit_low) + { + m_nCounter_low = 0; + ChangeLevel(); + } + } + } +} + +void CSAANoise::Sync(bool bSync) +{ + if (bSync) + { + m_nCounter = 0; + m_nCounter_low = 0; + } + m_bSync = bSync; +} + + +void CSAANoise::_SetSampleRate(int nSampleRate) +{ + m_nSampleRate = nSampleRate; +} + + +void CSAANoise::_SetOversample(unsigned int oversample) +{ + // oversample is a power of 2 i.e. + // if oversample == 2 then 4x oversample + // if oversample == 6 then 64x oversample + if (oversample < m_nOversample) + { + m_nCounter_low <<= (m_nOversample - oversample); + } + else + { + m_nCounter_low >>= (oversample - m_nOversample); + } + + m_nCounterLimit_low = 1<> 1) ^ 0x20400; + } + else + { + m_nRand >>= 1; + } +} diff --git a/extern/SAASound/src/SAANoise.h b/extern/SAASound/src/SAANoise.h new file mode 100755 index 00000000..61a65dee --- /dev/null +++ b/extern/SAASound/src/SAANoise.h @@ -0,0 +1,54 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// SAANoise.h: interface for the CSAANoise class. +// +////////////////////////////////////////////////////////////////////// + +#ifndef SAANOISE_H_INCLUDED +#define SAANOISE_H_INCLUDED + +class CSAANoise +{ +private: + unsigned long m_nCounter; + unsigned long m_nAdd; + unsigned long m_nCounter_low; + unsigned int m_nOversample; + unsigned long m_nCounterLimit_low; + bool m_bSync; // see description of "SYNC" bit of register 28 + unsigned long m_nSampleRate; // = 44100 when RateMode=0, for example + int m_nSourceMode; + unsigned long m_nAddBase; // nAdd for 31.25 kHz noise at 44.1 kHz samplerate + + // pseudo-random number generator + unsigned long m_nRand; + + void ChangeLevel(void); + + +public: + CSAANoise(); + CSAANoise(unsigned long seed); + ~CSAANoise(); + + void SetSource(int nSource); + void Trigger(void); + void _SetSampleRate(int nSampleRate); + void _SetOversample(unsigned int oversample); + void _SetClockRate(int nClockRate); + void Seed(unsigned long seed); + + void Tick(void); + int Level(void) const; + void Sync(bool bSync); + +}; + +inline int CSAANoise::Level(void) const +{ + // returns 0 or 1 + return (m_nRand & 0x00000001); +} + + +#endif // SAANOISE_H_INCLUDED diff --git a/extern/SAASound/src/SAASndC.cpp b/extern/SAASound/src/SAASndC.cpp new file mode 100755 index 00000000..f03e8211 --- /dev/null +++ b/extern/SAASound/src/SAASndC.cpp @@ -0,0 +1,95 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// Thanks to this file (and associated header file) you can now +// use CSAASound from within a standard 'C' program +// +////////////////////////////////////////////////////////////////////// + +#include "SAASound.h" +#include "types.h" +#include "SAAEnv.h" +#include "SAANoise.h" +#include "SAAFreq.h" +#include "SAAAmp.h" +#include "SAASound.h" +#include "SAAImpl.h" + +SAASND SAAAPI newSAASND(void) +{ + return (SAASND)(new CSAASoundInternal()); +} + +void SAAAPI deleteSAASND(SAASND object) +{ + delete (LPCSAASOUND)(object); +} + +void SAAAPI SAASNDSetClockRate(SAASND object, unsigned int nClockRate) +{ + ((LPCSAASOUND)(object))->SetClockRate(nClockRate); +} + +void SAAAPI SAASNDSetSoundParameters(SAASND object, SAAPARAM uParam) +{ + ((LPCSAASOUND)(object))->SetSoundParameters(uParam); +} + +void SAAAPI SAASNDWriteAddress(SAASND object, BYTE nReg) +{ + ((LPCSAASOUND)(object))->WriteAddress(nReg); +} + +void SAAAPI SAASNDWriteData(SAASND object, BYTE nData) +{ + ((LPCSAASOUND)(object))->WriteData(nData); +} + +void SAAAPI SAASNDWriteAddressData(SAASND object, BYTE nReg, BYTE nData) +{ + ((LPCSAASOUND)(object))->WriteAddressData(nReg, nData); +} + +void SAAAPI SAASNDClear(SAASND object) +{ + ((LPCSAASOUND)(object))->Clear(); +} + +SAAPARAM SAAAPI SAASNDGetCurrentSoundParameters(SAASND object) +{ + return ((LPCSAASOUND)(object))->GetCurrentSoundParameters(); +} + +unsigned short SAAAPI SAASNDGetCurrentBytesPerSample(SAASND object) +{ + return ((LPCSAASOUND)(object))->GetCurrentBytesPerSample(); +} + +unsigned short SAAAPI SAASNDGetBytesPerSample(SAAPARAM uParam) +{ + return CSAASound::GetBytesPerSample(uParam); +} + +unsigned long SAAAPI SAASNDGetCurrentSampleRate(SAASND object) +{ + return ((LPCSAASOUND)(object))->GetCurrentSampleRate(); +} + +unsigned long SAAAPI SAASNDGetSampleRate(SAAPARAM uParam) +{ + return CSAASound::GetSampleRate(uParam); +} + +void SAAAPI SAASNDGenerateMany(SAASND object, BYTE * pBuffer, unsigned long nSamples) +{ + ((LPCSAASOUND)(object))->GenerateMany(pBuffer, nSamples); +} + +void SAAAPI SAASNDSetSampleRate(SAASND object, unsigned int nSampleRate) +{ + return ((LPCSAASOUND)(object))->SetSampleRate(nSampleRate); +} + +void SAAAPI SAASNDSetOversample(SAASND object, unsigned int nOversample) +{ + return ((LPCSAASOUND)(object))->SetOversample(nOversample); +} diff --git a/extern/SAASound/src/SAASndC.h b/extern/SAASound/src/SAASndC.h new file mode 100644 index 00000000..c6fd6576 --- /dev/null +++ b/extern/SAASound/src/SAASndC.h @@ -0,0 +1,102 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// ********** +// * PUBLIC * +// ********** +// +// SAASndC.h: "C-style" interface for the CSAASound class. +// +////////////////////////////////////////////////////////////////////// + +#ifndef SAASNDC_H_INCLUDED +#define SAASNDC_H_INCLUDED + +#ifdef _MSC_VER +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +#endif + +#ifndef SAASOUND_H_INCLUDED + +// Parameters for use with SetSoundParameters, for example, +// SetSoundParameters(SAAP_NOFILTER | SAAP_44100 | SAAP_16BIT | SAAP_STEREO); +#define SAAP_FILTER_HIGHPASS_SIMPLE 0x00000400 +#define SAAP_FILTER_OVERSAMPLE64x 0x00000300 +#define SAAP_FILTER_OVERSAMPLE2x 0x00000200 +#define SAAP_FILTER SAAP_FILTER_OVERSAMPLE2x +#define SAAP_NOFILTER 0x00000100 +#define SAAP_44100 0x00000030 +#define SAAP_22050 0x00000020 +#define SAAP_11025 0x00000010 +#define SAAP_16BIT 0x0000000c +#define SAAP_8BIT 0x00000004 +#define SAAP_STEREO 0x00000003 +#define SAAP_MONO 0x00000001 + +// Bitmasks for use with GetCurrentSoundParameters, for example, +// unsigned long CurrentSampleRateParameter = GetCurrentSoundParameters() +#define SAAP_MASK_FILTER 0x00000f00 +#define SAAP_MASK_FILTER_HIGHPASS 0x00000c00 +#define SAAP_MASK_FILTER_OVERSAMPLE 0x00000300 +#define SAAP_MASK_SAMPLERATE 0x000000030 +#define SAAP_MASK_BITDEPTH 0x0000000c +#define SAAP_MASK_CHANNELS 0x00000003 + +typedef unsigned long SAAPARAM; + + +#ifndef BYTE +#define BYTE unsigned char +#endif + +#ifdef WIN32 +#ifndef WINAPI +#define WINAPI __stdcall +#endif +#define EXTAPI __declspec(dllexport) WINAPI +#else // Win32 +#ifndef WINAPI +#define WINAPI /**/ +#endif +#define EXTAPI /**/ +#endif // Win32 + +#endif // SAASOUND_H_INCLUDED + +typedef void * SAASND; + +// the following are implemented as calls, etc, to a class. + +#ifdef __cplusplus +extern "C" { +#endif + +SAASND EXTAPI newSAASND(void); +void EXTAPI deleteSAASND(SAASND object); + +void EXTAPI SAASNDSetSoundParameters(SAASND object, SAAPARAM uParam); +void EXTAPI SAASNDWriteAddress(SAASND object, BYTE nReg); +void EXTAPI SAASNDWriteData(SAASND object, BYTE nData); +void EXTAPI SAASNDWriteAddressData(SAASND object, BYTE nReg, BYTE nData); +void EXTAPI SAASNDClear(SAASND object); +BYTE EXTAPI SAASNDReadAddress(SAASND object); + +SAAPARAM EXTAPI SAASNDGetCurrentSoundParameters(SAASND object); +unsigned short EXTAPI SAASNDGetCurrentBytesPerSample(SAASND object); +unsigned short EXTAPI SAASNDGetBytesPerSample(SAAPARAM uParam); +unsigned long EXTAPI SAASNDGetCurrentSampleRate(SAASND object); +unsigned long EXTAPI SAASNDGetSampleRate(SAAPARAM uParam); + +void EXTAPI SAASNDGenerateMany(SAASND object, BYTE * pBuffer, unsigned long nSamples); + +void EXTAPI SAASNDSetClockRate(SAASND object, unsigned int nClockRate); +void EXTAPI SAASNDSetSampleRate(SAASND object, unsigned int nSampleRate); +void EXTAPI SAASNDSetOversample(SAASND object, unsigned int nOversample); + + +#ifdef __cplusplus +}; // extern "C" +#endif + +#endif // SAASNDC_H_INCLUDED diff --git a/extern/SAASound/src/SAASound.cpp b/extern/SAASound/src/SAASound.cpp new file mode 100755 index 00000000..c5e33d86 --- /dev/null +++ b/extern/SAASound/src/SAASound.cpp @@ -0,0 +1,13 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// SAASound.cpp - dummy function +// +////////////////////////////////////////////////////////////////////// + +#include + +// Provide something so the compiler doesn't optimise us out of existance +int SomeFunction () +{ + return 42; +} diff --git a/extern/SAASound/src/SAASound.h b/extern/SAASound/src/SAASound.h new file mode 100644 index 00000000..c8083148 --- /dev/null +++ b/extern/SAASound/src/SAASound.h @@ -0,0 +1,129 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// SAASound.h: interface for the CSAASound class. +// +// This corresponds to the public (exported) DLL interface, so all +// APIs and client factory methods belong here. +// +// Compatibility notes : the intention is for this to be fully backwards +// compatible across minor and patch versions. Any backwards breaking changes +// should be reflected as a major version increment. New functionality can be added +// in minor versions so long as backwards compatiblity is maintained +// +// Version 3.3.0 (4th Dec 2018) +// +////////////////////////////////////////////////////////////////////// + +#ifndef SAASOUND_H_INCLUDED +#define SAASOUND_H_INCLUDED + +// define this if you want to output diagnostic text and PCM files +//#define DEBUGSAA + +// Parameters for use with SetSoundParameters, for example, +// SetSoundParameters(SAAP_NOFILTER | SAAP_44100 | SAA_16BIT | SAA_STEREO); +// SAAP_FILTER_HIGHPASS_SIMPLE can be ORd with SAAP_FILTER_OVERSAMPLE64x/2x +#define SAAP_FILTER_HIGHPASS_SIMPLE 0x00000400 +#define SAAP_FILTER_OVERSAMPLE64x 0x00000300 +#define SAAP_FILTER_OVERSAMPLE2x 0x00000200 +#define SAAP_FILTER SAAP_FILTER_OVERSAMPLE2x +#define SAAP_NOFILTER 0x00000100 +#define SAAP_44100 0x00000030 +#define SAAP_22050 0x00000020 +#define SAAP_11025 0x00000010 +#define SAAP_16BIT 0x0000000c +#define SAAP_8BIT 0x00000004 +#define SAAP_STEREO 0x00000003 +#define SAAP_MONO 0x00000001 + +// Bitmasks for use with GetCurrentSoundParameters, for example, +// unsigned long CurrentSampleRateParameter = GetCurrentSoundParameters() +#define SAAP_MASK_FILTER 0x00000f00 +#define SAAP_MASK_FILTER_HIGHPASS 0x00000c00 +#define SAAP_MASK_FILTER_OVERSAMPLE 0x00000300 +#define SAAP_MASK_SAMPLERATE 0x000000030 +#define SAAP_MASK_BITDEPTH 0x0000000c +#define SAAP_MASK_CHANNELS 0x00000003 + +typedef unsigned long SAAPARAM; + + +#ifndef BYTE +#define BYTE unsigned char +#endif + +#ifdef _WIN32 +#define SAAAPI _stdcall +#else +#define SAAAPI +#endif + + +#ifdef __cplusplus + +class CSAASound +{ +public: + virtual ~CSAASound() { } + + virtual void SetSoundParameters (SAAPARAM uParam) = 0; + virtual void WriteAddress (BYTE nReg) = 0; + virtual void WriteData (BYTE nData) = 0; + virtual void WriteAddressData (BYTE nReg, BYTE nData) = 0; + virtual void Clear () = 0; + virtual BYTE ReadAddress () = 0; + + virtual SAAPARAM GetCurrentSoundParameters () = 0; + virtual unsigned long GetCurrentSampleRate () = 0; + static unsigned long GetSampleRate (SAAPARAM uParam); + virtual unsigned short GetCurrentBytesPerSample () = 0; + static unsigned short GetBytesPerSample (SAAPARAM uParam); + + virtual void GenerateMany (BYTE * pBuffer, unsigned long nSamples) = 0; + + virtual void SetClockRate(unsigned int nClockRate) = 0; + virtual void SetSampleRate(unsigned int nSampleRate) = 0; + virtual void SetOversample(unsigned int nOversample) = 0; +}; + +typedef class CSAASound * LPCSAASOUND; + +LPCSAASOUND SAAAPI CreateCSAASound(void); +void SAAAPI DestroyCSAASound(LPCSAASOUND object); + +#endif // __cplusplus + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void * SAASND; + +// "C-style" interface for the CSAASound class +SAASND SAAAPI newSAASND(void); +void SAAAPI deleteSAASND(SAASND object); + +void SAAAPI SAASNDSetSoundParameters(SAASND object, SAAPARAM uParam); +void SAAAPI SAASNDWriteAddress(SAASND object, BYTE nReg); +void SAAAPI SAASNDWriteData(SAASND object, BYTE nData); +void SAAAPI SAASNDWriteAddressData(SAASND object, BYTE nReg, BYTE nData); +void SAAAPI SAASNDClear(SAASND object); + +SAAPARAM SAAAPI SAASNDGetCurrentSoundParameters(SAASND object); +unsigned short SAAAPI SAASNDGetCurrentBytesPerSample(SAASND object); +unsigned short SAAAPI SAASNDGetBytesPerSample(SAAPARAM uParam); +unsigned long SAAAPI SAASNDGetCurrentSampleRate(SAASND object); +unsigned long SAAAPI SAASNDGetSampleRate(SAAPARAM uParam); + +void SAAAPI SAASNDGenerateMany(SAASND object, BYTE * pBuffer, unsigned long nSamples); +void SAAAPI SAASNDSetClockRate(SAASND object, unsigned int nClockRate); +void SAAAPI SAASNDSetSampleRate(SAASND object, unsigned int nSampleRate); +void SAAAPI SAASNDSetOversample(SAASND object, unsigned int nOversample); + + +#ifdef __cplusplus +}; // extern "C" +#endif + +#endif // SAASOUND_H_INCLUDED diff --git a/extern/SAASound/src/config.h.in b/extern/SAASound/src/config.h.in new file mode 100644 index 00000000..99db925a --- /dev/null +++ b/extern/SAASound/src/config.h.in @@ -0,0 +1,14 @@ +#pragma once + +#define EXTERNAL_CLK_HZ @EXTERNAL_CLK_HZ@ +#cmakedefine SAAFREQ_FIXED_CLOCKRATE +#define SAMPLE_RATE_HZ @SAMPLE_RATE_HZ@ +#define DEFAULT_OVERSAMPLE @DEFAULT_OVERSAMPLE@ +#define DEFAULT_UNBOOSTED_MULTIPLIER @DEFAULT_UNBOOSTED_MULTIPLIER@ +#define DEFAULT_BOOST @DEFAULT_BOOST@ +#cmakedefine DEBUGSAA +#define DEBUG_SAA_REGISTER_LOG "@DEBUG_SAA_REGISTER_LOG@" +#define DEBUG_SAA_PCM_LOG "@DEBUG_SAA_PCM_LOG@" + +#cmakedefine USE_CONFIG_FILE +#cmakedefine CONFIG_FILE_PATH "@CONFIG_FILE_PATH@" diff --git a/extern/SAASound/src/defns.h b/extern/SAASound/src/defns.h new file mode 100644 index 00000000..81b48c41 --- /dev/null +++ b/extern/SAASound/src/defns.h @@ -0,0 +1,59 @@ +// Part of SAASound copyright 2020 Dave Hooper +// +// defns.h: compile-time configuration parameters +// +////////////////////////////////////////////////////////////////////// + +#ifndef DEFNS_H_INCLUDED +#define DEFNS_H_INCLUDED + +#ifdef HAVE_CONFIG_H +// using CMAKE +#include "saasound_cmake_config.h" +#else + +// initial default SAA1099 crystal clock rate in HZ (can be changed subsequently by calling SetClockRate) +#define EXTERNAL_CLK_HZ 8000000 + +// define SAAFREQ_FIXED_CLOCKRATE if the above external clock rate is the only supported clock rate +// i.e. only support a single compile-time clock rate (=> this also prevents using the SetClockRate method) +#undef SAAFREQ_FIXED_CLOCKRATE +// #define SAAFREQ_FIXED_CLOCKRATE + +// initial default sample rate (audio samplerate) +#define SAMPLE_RATE_HZ 44100 + +// initial default oversample (audio quality) recommend 0<=oversample<=6 +#define DEFAULT_OVERSAMPLE 6 + +// Whether to dump out a log of all register and value changes and raw output pcm +//#define DEBUGSAA +#undef DEBUGSAA + +// the (default) names of the register output and pcm output log files. +// If you're using a config file, you can change these (or, if you enable +// debugging via the config file settings, but leave the filenames unspecified, +// it will use these defaults) +#define DEBUG_SAA_REGISTER_LOG "debugsaa.txt" +#define DEBUG_SAA_PCM_LOG "debugsaa.pcm" +// Whether to include support for these debug logs via config file (only making +// sense if USE_CONFIG_FILE is also defined) + +// Whether to support a startup configuration file that is parsed at load time +// #undef USE_CONFIG_FILE +// NO! this is messy as heck! +//#define USE_CONFIG_FILE + +// and if so, what is its location +#ifdef USE_CONFIG_FILE +#define CONFIG_FILE_PATH "SAASound.cfg" +#endif // USE_CONFIG_FILE + +#define DEFAULT_UNBOOSTED_MULTIPLIER 11.35 + +#define DEFAULT_BOOST 1 + + +#endif // HAVE_CONFIG_H + +#endif // DEFNS_H_INCLUDED diff --git a/extern/SAASound/src/minIni/minGlue-FatFs.h b/extern/SAASound/src/minIni/minGlue-FatFs.h new file mode 100644 index 00000000..a2f3cd82 --- /dev/null +++ b/extern/SAASound/src/minIni/minGlue-FatFs.h @@ -0,0 +1,45 @@ +/* Glue functions for the minIni library, based on the FatFs and Petit-FatFs + * libraries, see http://elm-chan.org/fsw/ff/00index_e.html + * + * By CompuPhase, 2008-2019 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + * + * (The FatFs and Petit-FatFs libraries are copyright by ChaN and licensed at + * its own terms.) + */ + +#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ + +/* You must set FF_USE_STRFUNC to 1 or 2 in the include file ff.h (or tff.h) + * to enable the "string functions" fgets() and fputs(). + */ +#include "ff.h" /* include tff.h for Tiny-FatFs */ + +/* When setting FF_USE_STRFUNC to 2 (for LF to CR/LF translation), INI_LINETERM + * should be defined to "\n" (otherwise "\r\n" will be translated by FatFS to + * "\r\r\n"). +*/ +#if defined FF_USE_STRFUNC && FF_USE_STRFUNC == 2 && !defined INI_LINETERM + #define INI_LINETERM "\n" +#endif + +#define INI_FILETYPE FIL +#define ini_openread(filename,file) (f_open((file), (filename), FA_READ+FA_OPEN_EXISTING) == FR_OK) +#define ini_openwrite(filename,file) (f_open((file), (filename), FA_WRITE+FA_CREATE_ALWAYS) == FR_OK) +#define ini_close(file) (f_close(file) == FR_OK) +#define ini_read(buffer,size,file) f_gets((buffer), (size), (file)) +#define ini_write(buffer,file) f_puts((buffer), (file)) +#define ini_remove(filename) (f_unlink(filename) == FR_OK) + +#define INI_FILEPOS DWORD +#define ini_tell(file,pos) (*(pos) = f_tell((file))) +#define ini_seek(file,pos) (f_lseek((file), *(pos)) == FR_OK) + +static int ini_rename(TCHAR *source, const TCHAR *dest) +{ + /* Function f_rename() does not allow drive letters in the destination file */ + char *drive = strchr(dest, ':'); + drive = (drive == NULL) ? dest : drive + 1; + return (f_rename(source, drive) == FR_OK); +} diff --git a/extern/SAASound/src/minIni/minGlue-Linux.h b/extern/SAASound/src/minIni/minGlue-Linux.h new file mode 100644 index 00000000..f0484916 --- /dev/null +++ b/extern/SAASound/src/minIni/minGlue-Linux.h @@ -0,0 +1,51 @@ +/* Glue functions for the minIni library, based on the C/C++ stdio library and + * using BSD-style file locking to "serialize" concurrent accesses to an INI + * file. It presumes GCC compiler extensions. + * + * By CompuPhase, 2020 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + */ + +/* map required file I/O types and functions to the standard C library */ +#include +#include +#include + +#define INI_FILETYPE FILE* + +static inline int ini_openread(const char *filename, INI_FILETYPE *file) { + if ((*file = fopen((filename),"r")) == NULL) + return 0; + return flock(fileno(*file), LOCK_SH) == 0; +} + +static inline int ini_openwrite(const char *filename, INI_FILETYPE *file) { + if ((*file = fopen((filename),"r+")) == NULL + && (*file = fopen((filename),"w")) == NULL) + return 0; + if (flock(fileno(*file), LOCK_EX) < 0) + return 0; + return ftruncate(fileno(*file), 0); +} + +#define INI_OPENREWRITE +static inline int ini_openrewrite(const char *filename, INI_FILETYPE *file) { + if ((*file = fopen((filename),"r+")) == NULL) + return 0; + return flock(fileno(*file), LOCK_EX) == 0; +} + +#define ini_close(file) (fclose(*(file)) == 0) +#define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL) +#define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0) +#define ini_rename(source,dest) (rename((source), (dest)) == 0) + +#define INI_FILEPOS long int +#define ini_tell(file,pos) (*(pos) = ftell(*(file))) +#define ini_seek(file,pos) (fseek(*(file), *(pos), SEEK_SET) == 0) + +/* for floating-point support, define additional types and functions */ +#define INI_REAL float +#define ini_ftoa(string,value) sprintf((string),"%f",(value)) +#define ini_atof(string) (INI_REAL)strtod((string),NULL) diff --git a/extern/SAASound/src/minIni/minGlue-ccs.h b/extern/SAASound/src/minIni/minGlue-ccs.h new file mode 100644 index 00000000..22a8c92b --- /dev/null +++ b/extern/SAASound/src/minIni/minGlue-ccs.h @@ -0,0 +1,64 @@ +/* minIni glue functions for FAT library by CCS, Inc. (as provided with their + * PIC MCU compiler) + * + * By CompuPhase, 2011-2012 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + * + * (The FAT library is copyright (c) 2007 Custom Computer Services, and + * licensed at its own terms.) + */ + +#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ + +#ifndef FAT_PIC_C + #error FAT library must be included before this module +#endif +#define const /* keyword not supported by CCS */ + +#define INI_FILETYPE FILE +#define ini_openread(filename,file) (fatopen((filename), "r", (file)) == GOODEC) +#define ini_openwrite(filename,file) (fatopen((filename), "w", (file)) == GOODEC) +#define ini_close(file) (fatclose((file)) == 0) +#define ini_read(buffer,size,file) (fatgets((buffer), (size), (file)) != NULL) +#define ini_write(buffer,file) (fatputs((buffer), (file)) == GOODEC) +#define ini_remove(filename) (rm_file((filename)) == 0) + +#define INI_FILEPOS fatpos_t +#define ini_tell(file,pos) (fatgetpos((file), (pos)) == 0) +#define ini_seek(file,pos) (fatsetpos((file), (pos)) == 0) + +#ifndef INI_READONLY +/* CCS FAT library lacks a rename function, so instead we copy the file to the + * new name and delete the old file + */ +static int ini_rename(char *source, char *dest) +{ + FILE fr, fw; + int n; + + if (fatopen(source, "r", &fr) != GOODEC) + return 0; + if (rm_file(dest) != 0) + return 0; + if (fatopen(dest, "w", &fw) != GOODEC) + return 0; + + /* With some "insider knowledge", we can save some memory: the "source" + * parameter holds a filename that was built from the "dest" parameter. It + * was built in a local buffer with the size INI_BUFFERSIZE. We can reuse + * this buffer for copying the file. + */ + while (n=fatread(source, 1, INI_BUFFERSIZE, &fr)) + fatwrite(source, 1, n, &fw); + + fatclose(&fr); + fatclose(&fw); + + /* Now we need to delete the source file. However, we have garbled the buffer + * that held the filename of the source. So we need to build it again. + */ + ini_tempname(source, dest, INI_BUFFERSIZE); + return rm_file(source) == 0; +} +#endif diff --git a/extern/SAASound/src/minIni/minGlue-efsl.h b/extern/SAASound/src/minIni/minGlue-efsl.h new file mode 100644 index 00000000..898243f6 --- /dev/null +++ b/extern/SAASound/src/minIni/minGlue-efsl.h @@ -0,0 +1,63 @@ +/* Glue functions for the minIni library, based on the EFS Library, see + * https://sourceforge.net/projects/efsl/ + * + * By CompuPhase, 2008-2012 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + * + * (EFSL is copyright 2005-2006 Lennart Ysboodt and Michael De Nil, and + * licensed under the GPL with an exception clause for static linking.) + */ + +#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ +#define INI_LINETERM "\r\n" /* set line termination explicitly */ + +#include "efs.h" +extern EmbeddedFileSystem g_efs; + +#define INI_FILETYPE EmbeddedFile +#define ini_openread(filename,file) (file_fopen((file), &g_efs.myFs, (char*)(filename), 'r') == 0) +#define ini_openwrite(filename,file) (file_fopen((file), &g_efs.myFs, (char*)(filename), 'w') == 0) +#define ini_close(file) file_fclose(file) +#define ini_read(buffer,size,file) (file_read((file), (size), (buffer)) > 0) +#define ini_write(buffer,file) (file_write((file), strlen(buffer), (char*)(buffer)) > 0) +#define ini_remove(filename) rmfile(&g_efs.myFs, (char*)(filename)) + +#define INI_FILEPOS euint32 +#define ini_tell(file,pos) (*(pos) = (file)->FilePtr)) +#define ini_seek(file,pos) file_setpos((file), (*pos)) + +#if ! defined INI_READONLY +/* EFSL lacks a rename function, so instead we copy the file to the new name + * and delete the old file + */ +static int ini_rename(char *source, const char *dest) +{ + EmbeddedFile fr, fw; + int n; + + if (file_fopen(&fr, &g_efs.myFs, source, 'r') != 0) + return 0; + if (rmfile(&g_efs.myFs, (char*)dest) != 0) + return 0; + if (file_fopen(&fw, &g_efs.myFs, (char*)dest, 'w') != 0) + return 0; + + /* With some "insider knowledge", we can save some memory: the "source" + * parameter holds a filename that was built from the "dest" parameter. It + * was built in buffer and this buffer has the size INI_BUFFERSIZE. We can + * reuse this buffer for copying the file. + */ + while (n=file_read(&fr, INI_BUFFERSIZE, source)) + file_write(&fw, n, source); + + file_fclose(&fr); + file_fclose(&fw); + + /* Now we need to delete the source file. However, we have garbled the buffer + * that held the filename of the source. So we need to build it again. + */ + ini_tempname(source, dest, INI_BUFFERSIZE); + return rmfile(&g_efs.myFs, source) == 0; +} +#endif diff --git a/extern/SAASound/src/minIni/minGlue-ffs.h b/extern/SAASound/src/minIni/minGlue-ffs.h new file mode 100644 index 00000000..3446fcd7 --- /dev/null +++ b/extern/SAASound/src/minIni/minGlue-ffs.h @@ -0,0 +1,27 @@ +/* Glue functions for the minIni library, based on the "FAT Filing System" + * library by embedded-code.com, now IBEX UK. + * https://github.com/ibexuk/C_Memory_SD_Card_FAT_Driver + * + * By CompuPhase, 2008-2012 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + * + * (The "FAT Filing System" library itself is copyright IBEX UK, and licensed + * at its own terms.) + */ + +#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ +#include + +#define INI_FILETYPE FFS_FILE* +#define ini_openread(filename,file) ((*(file) = ffs_fopen((filename),"r")) != NULL) +#define ini_openwrite(filename,file) ((*(file) = ffs_fopen((filename),"w")) != NULL) +#define ini_close(file) (ffs_fclose(*(file)) == 0) +#define ini_read(buffer,size,file) (ffs_fgets((buffer),(size),*(file)) != NULL) +#define ini_write(buffer,file) (ffs_fputs((buffer),*(file)) >= 0) +#define ini_rename(source,dest) (ffs_rename((source), (dest)) == 0) +#define ini_remove(filename) (ffs_remove(filename) == 0) + +#define INI_FILEPOS long +#define ini_tell(file,pos) (ffs_fgetpos(*(file), (pos)) == 0) +#define ini_seek(file,pos) (ffs_fsetpos(*(file), (pos)) == 0) diff --git a/extern/SAASound/src/minIni/minGlue-mdd.h b/extern/SAASound/src/minIni/minGlue-mdd.h new file mode 100644 index 00000000..ec5e0be1 --- /dev/null +++ b/extern/SAASound/src/minIni/minGlue-mdd.h @@ -0,0 +1,58 @@ +/* minIni glue functions for Microchip's "Memory Disk Drive" file system + * library, as presented in Microchip application note AN1045. + * + * By CompuPhase, 2011-2014 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + * + * (The "Microchip Memory Disk Drive File System" is copyright (c) Microchip + * Technology Incorporated, and licensed at its own terms.) + */ + +#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ + +#include "MDD File System\fsio.h" +#include + +#define INI_FILETYPE FSFILE* +#define ini_openread(filename,file) ((*(file) = FSfopen((filename),FS_READ)) != NULL) +#define ini_openwrite(filename,file) ((*(file) = FSfopen((filename),FS_WRITE)) != NULL) +#define ini_openrewrite(filename,file) ((*(file) = fopen((filename),FS_READPLUS)) != NULL) +#define ini_close(file) (FSfclose(*(file)) == 0) +#define ini_write(buffer,file) (FSfwrite((buffer), 1, strlen(buffer), (*file)) > 0) +#define ini_remove(filename) (FSremove((filename)) == 0) + +#define INI_FILEPOS long int +#define ini_tell(file,pos) (*(pos) = FSftell(*(file))) +#define ini_seek(file,pos) (FSfseek(*(file), *(pos), SEEK_SET) == 0) + +/* Since the Memory Disk Drive file system library reads only blocks of files, + * the function to read a text line does so by "over-reading" a block of the + * of the maximum size and truncating it behind the end-of-line. + */ +static int ini_read(char *buffer, int size, INI_FILETYPE *file) +{ + size_t numread = size; + char *eol; + + if ((numread = FSfread(buffer, 1, size, *file)) == 0) + return 0; /* at EOF */ + if ((eol = strchr(buffer, '\n')) == NULL) + eol = strchr(buffer, '\r'); + if (eol != NULL) { + /* terminate the buffer */ + *++eol = '\0'; + /* "unread" the data that was read too much */ + FSfseek(*file, - (int)(numread - (size_t)(eol - buffer)), SEEK_CUR); + } /* if */ + return 1; +} + +#ifndef INI_READONLY +static int ini_rename(const char *source, const char *dest) +{ + FSFILE* ftmp = FSfopen((source), FS_READ); + FSrename((dest), ftmp); + return FSfclose(ftmp) == 0; +} +#endif diff --git a/extern/SAASound/src/minIni/minGlue-stdio.h b/extern/SAASound/src/minIni/minGlue-stdio.h new file mode 100644 index 00000000..67d24334 --- /dev/null +++ b/extern/SAASound/src/minIni/minGlue-stdio.h @@ -0,0 +1,31 @@ +/* Glue functions for the minIni library, based on the C/C++ stdio library + * + * Or better said: this file contains macros that maps the function interface + * used by minIni to the standard C/C++ file I/O functions. + * + * By CompuPhase, 2008-2014 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + */ + +/* map required file I/O types and functions to the standard C library */ +#include + +#define INI_FILETYPE FILE* +#define ini_openread(filename,file) ((*(file) = fopen((filename),"rb")) != NULL) +#define ini_openwrite(filename,file) ((*(file) = fopen((filename),"wb")) != NULL) +#define ini_openrewrite(filename,file) ((*(file) = fopen((filename),"r+b")) != NULL) +#define ini_close(file) (fclose(*(file)) == 0) +#define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL) +#define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0) +#define ini_rename(source,dest) (rename((source), (dest)) == 0) +#define ini_remove(filename) (remove(filename) == 0) + +#define INI_FILEPOS long int +#define ini_tell(file,pos) (*(pos) = ftell(*(file))) +#define ini_seek(file,pos) (fseek(*(file), *(pos), SEEK_SET) == 0) + +/* for floating-point support, define additional types and functions */ +#define INI_REAL float +#define ini_ftoa(string,value) sprintf((string),"%f",(value)) +#define ini_atof(string) (INI_REAL)strtod((string),NULL) diff --git a/extern/SAASound/src/minIni/minGlue.h b/extern/SAASound/src/minIni/minGlue.h new file mode 100644 index 00000000..079bc67e --- /dev/null +++ b/extern/SAASound/src/minIni/minGlue.h @@ -0,0 +1,34 @@ +/* Glue functions for the minIni library, based on the C/C++ stdio library + * + * Or better said: this file contains macros that maps the function interface + * used by minIni to the standard C/C++ file I/O functions. + * + * By CompuPhase, 2008-2014 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + */ + +/* map required file I/O types and functions to the standard C library */ +#include + +#define INI_ANSIONLY +#define INI_READONLY + +#define INI_FILETYPE FILE* +#define ini_openread(filename,file) ((*(file) = _tfopen((filename), __T("rb"))) != NULL) +#define ini_openwrite(filename,file) ((*(file) = _tfopen((filename), __T("wb"))) != NULL) +#define ini_openrewrite(filename,file) ((*(file) = _tfopen((filename), __T("r+b"))) != NULL) +#define ini_close(file) (fclose(*(file)) == 0) +#define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL) +#define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0) +#define ini_rename(source,dest) (_trename((source), (dest)) == 0) +#define ini_remove(filename) (_tremove(filename) == 0) + +#define INI_FILEPOS long int +#define ini_tell(file,pos) (*(pos) = ftell(*(file))) +#define ini_seek(file,pos) (fseek(*(file), *(pos), SEEK_SET) == 0) + +/* for floating-point support, define additional types and functions */ +#define INI_REAL float +#define ini_ftoa(string,value) sprintf((string), "%f",(value)) +#define ini_atof(string) (INI_REAL)strtod((string),NULL) diff --git a/extern/SAASound/src/minIni/minGlue_stdio_tchar.h b/extern/SAASound/src/minIni/minGlue_stdio_tchar.h new file mode 100644 index 00000000..e1a7f23d --- /dev/null +++ b/extern/SAASound/src/minIni/minGlue_stdio_tchar.h @@ -0,0 +1,31 @@ +/* Glue functions for the minIni library, based on the C/C++ stdio library + * + * Or better said: this file contains macros that maps the function interface + * used by minIni to the standard C/C++ file I/O functions. + * + * By CompuPhase, 2008-2014 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + */ + +/* map required file I/O types and functions to the standard C library */ +#include + +#define INI_FILETYPE FILE* +#define ini_openread(filename,file) ((*(file) = _tfopen((filename), __T("rb"))) != NULL) +#define ini_openwrite(filename,file) ((*(file) = _tfopen((filename), __T("wb"))) != NULL) +#define ini_openrewrite(filename,file) ((*(file) = _tfopen((filename), __T("r+b"))) != NULL) +#define ini_close(file) (fclose(*(file)) == 0) +#define ini_read(buffer,size,file) (_fgetts((buffer),(size),*(file)) != NULL) +#define ini_write(buffer,file) (_fputts((buffer),*(file)) >= 0) +#define ini_rename(source,dest) (_trename((source), (dest)) == 0) +#define ini_remove(filename) (_tremove(filename) == 0) + +#define INI_FILEPOS long int +#define ini_tell(file,pos) (*(pos) = ftell(*(file))) +#define ini_seek(file,pos) (fseek(*(file), *(pos), SEEK_SET) == 0) + +/* for floating-point support, define additional types and functions */ +#define INI_REAL float +#define ini_ftoa(string,value) _stprintf((string), __T("%f"),(value)) +#define ini_atof(string) (INI_REAL)_tcstod((string),NULL) diff --git a/extern/SAASound/src/minIni/minIni.c b/extern/SAASound/src/minIni/minIni.c new file mode 100644 index 00000000..e9738846 --- /dev/null +++ b/extern/SAASound/src/minIni/minIni.c @@ -0,0 +1,862 @@ +/* minIni - Multi-Platform INI file parser, suitable for embedded systems + * + * These routines are in part based on the article "Multiplatform .INI Files" + * by Joseph J. Graf in the March 1994 issue of Dr. Dobb's Journal. + * + * Copyright (c) CompuPhase, 2008-2020 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: minIni.c 53 2015-01-18 13:35:11Z thiadmer.riemersma@gmail.com $ + */ + +#include "minGlue.h" + + +#define MININI_IMPLEMENTATION +#include "minIni.h" +#if defined NDEBUG +#define assert(e) +#else +#include +#endif + +#if defined __linux || defined __linux__ + #define __LINUX__ +#elif defined FREEBSD && !defined __FreeBSD__ + #define __FreeBSD__ +#elif defined(_MSC_VER) + #pragma warning(disable: 4996) /* for Microsoft Visual C/C++ */ +#endif +#if !defined strnicmp && !defined PORTABLE_STRNICMP + #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ + #define strnicmp strncasecmp + #endif +#endif +#if !defined _totupper + #define _totupper toupper +#endif + +#if !defined INI_LINETERM + #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ + #define INI_LINETERM __T("\n") + #else + #define INI_LINETERM __T("\r\n") + #endif +#endif +#if !defined INI_FILETYPE + #error Missing definition for INI_FILETYPE. +#endif + +#if !defined sizearray + #define sizearray(a) (sizeof(a) / sizeof((a)[0])) +#endif + +enum quote_option { + QUOTE_NONE, + QUOTE_ENQUOTE, + QUOTE_DEQUOTE, +}; + +#if defined PORTABLE_STRNICMP +int strnicmp(const mTCHAR *s1, const mTCHAR *s2, size_t n) +{ + while (n-- != 0 && (*s1 || *s2)) { + register int c1, c2; + c1 = *s1++; + if ('a' <= c1 && c1 <= 'z') + c1 += ('A' - 'a'); + c2 = *s2++; + if ('a' <= c2 && c2 <= 'z') + c2 += ('A' - 'a'); + if (c1 != c2) + return c1 - c2; + } /* while */ + return 0; +} +#endif /* PORTABLE_STRNICMP */ + +static mTCHAR *skipleading(const mTCHAR *str) +{ + assert(str != NULL); + while ('\0' < *str && *str <= ' ') + str++; + return (mTCHAR *)str; +} + +static mTCHAR *skiptrailing(const mTCHAR *str, const mTCHAR *base) +{ + assert(str != NULL); + assert(base != NULL); + while (str > base && '\0' < *(str-1) && *(str-1) <= ' ') + str--; + return (mTCHAR *)str; +} + +static mTCHAR *striptrailing(mTCHAR *str) +{ + mTCHAR *ptr = skiptrailing(_mtcschr(str, '\0'), str); + assert(ptr != NULL); + *ptr = '\0'; + return str; +} + +static mTCHAR *ini_strncpy(mTCHAR *dest, const mTCHAR *source, size_t maxlen, enum quote_option option) +{ + size_t d, s; + + assert(maxlen>0); + assert(source != NULL && dest != NULL); + assert((dest < source || (dest == source && option != QUOTE_ENQUOTE)) || dest > source + strlen(source)); + if (option == QUOTE_ENQUOTE && maxlen < 3) + option = QUOTE_NONE; /* cannot store two quotes and a terminating zero in less than 3 characters */ + + switch (option) { + case QUOTE_NONE: + for (d = 0; d < maxlen - 1 && source[d] != '\0'; d++) + dest[d] = source[d]; + assert(d < maxlen); + dest[d] = '\0'; + break; + case QUOTE_ENQUOTE: + d = 0; + dest[d++] = '"'; + for (s = 0; source[s] != '\0' && d < maxlen - 2; s++, d++) { + if (source[s] == '"') { + if (d >= maxlen - 3) + break; /* no space to store the escape character plus the one that follows it */ + dest[d++] = '\\'; + } /* if */ + dest[d] = source[s]; + } /* for */ + dest[d++] = '"'; + dest[d] = '\0'; + break; + case QUOTE_DEQUOTE: + for (d = s = 0; source[s] != '\0' && d < maxlen - 1; s++, d++) { + if ((source[s] == '"' || source[s] == '\\') && source[s + 1] == '"') + s++; + dest[d] = source[s]; + } /* for */ + dest[d] = '\0'; + break; + default: + assert(0); + } /* switch */ + + return dest; +} + +static mTCHAR *cleanstring(mTCHAR *string, enum quote_option *quotes) +{ + int isstring; + mTCHAR *ep; + + assert(string != NULL); + assert(quotes != NULL); + + /* Remove a trailing comment */ + isstring = 0; + for (ep = string; *ep != '\0' && ((*ep != ';' && *ep != '#') || isstring); ep++) { + if (*ep == '"') { + if (*(ep + 1) == '"') + ep++; /* skip "" (both quotes) */ + else + isstring = !isstring; /* single quote, toggle isstring */ + } else if (*ep == '\\' && *(ep + 1) == '"') { + ep++; /* skip \" (both quotes */ + } /* if */ + } /* for */ + assert(ep != NULL && (*ep == '\0' || *ep == ';' || *ep == '#')); + *ep = '\0'; /* terminate at a comment */ + striptrailing(string); + /* Remove double quotes surrounding a value */ + *quotes = QUOTE_NONE; + if (*string == '"' && (ep = _mtcschr(string, '\0')) != NULL && *(ep - 1) == '"') { + string++; + *--ep = '\0'; + *quotes = QUOTE_DEQUOTE; /* this is a string, so remove escaped characters */ + } /* if */ + return string; +} + +static int getkeystring(INI_FILETYPE *fp, const mTCHAR *Section, const mTCHAR *Key, + int idxSection, int idxKey, mTCHAR *Buffer, int BufferSize, + INI_FILEPOS *mark) +{ + mTCHAR *sp, *ep; + int len, idx; + enum quote_option quotes; + mTCHAR LocalBuffer[INI_BUFFERSIZE]; + + assert(fp != NULL); + /* Move through file 1 line at a time until a section is matched or EOF. If + * parameter Section is NULL, only look at keys above the first section. If + * idxSection is positive, copy the relevant section name. + */ + len = (Section != NULL) ? (int)_mtcslen(Section) : 0; + if (len > 0 || idxSection >= 0) { + assert(idxSection >= 0 || Section != NULL); + idx = -1; + do { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, fp)) + return 0; + sp = skipleading(LocalBuffer); + ep = _mtcsrchr(sp, ']'); + } while (*sp != '[' || ep == NULL || + (((int)(ep-sp-1) != len || Section == NULL || _mtcsnicmp(sp+1,Section,len) != 0) && ++idx != idxSection)); + if (idxSection >= 0) { + if (idx == idxSection) { + assert(ep != NULL); + assert(*ep == ']'); + *ep = '\0'; + ini_strncpy(Buffer, sp + 1, BufferSize, QUOTE_NONE); + return 1; + } /* if */ + return 0; /* no more section found */ + } /* if */ + } /* if */ + + /* Now that the section has been found, find the entry. + * Stop searching upon leaving the section's area. + */ + assert(Key != NULL || idxKey >= 0); + len = (Key != NULL) ? (int)_mtcslen(Key) : 0; + idx = -1; + do { + if (mark != NULL) + ini_tell(fp, mark); /* optionally keep the mark to the start of the line */ + if (!ini_read(LocalBuffer,INI_BUFFERSIZE,fp) || *(sp = skipleading(LocalBuffer)) == '[') + return 0; + sp = skipleading(LocalBuffer); + ep = _mtcschr(sp, '='); /* Parse out the equal sign */ + if (ep == NULL) + ep = _mtcschr(sp, ':'); + } while (*sp == ';' || *sp == '#' || ep == NULL + || ((len == 0 || (int)(skiptrailing(ep,sp)-sp) != len || _mtcsnicmp(sp,Key,len) != 0) && ++idx != idxKey)); + if (idxKey >= 0) { + if (idx == idxKey) { + assert(ep != NULL); + assert(*ep == '=' || *ep == ':'); + *ep = '\0'; + striptrailing(sp); + ini_strncpy(Buffer, sp, BufferSize, QUOTE_NONE); + return 1; + } /* if */ + return 0; /* no more key found (in this section) */ + } /* if */ + + /* Copy up to BufferSize chars to buffer */ + assert(ep != NULL); + assert(*ep == '=' || *ep == ':'); + sp = skipleading(ep + 1); + sp = cleanstring(sp, "es); /* Remove a trailing comment */ + ini_strncpy(Buffer, sp, BufferSize, quotes); + return 1; +} + +/** ini_gets() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue default string in the event of a failed read + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_gets(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *DefValue, + mTCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || Key == NULL) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, Section, Key, -1, -1, Buffer, BufferSize, NULL); + (void)ini_close(&fp); + } /* if */ + if (!ok) + ini_strncpy(Buffer, (DefValue != NULL) ? DefValue : _mT(""), BufferSize, QUOTE_NONE); + return (int)_mtcslen(Buffer); +} + +/** ini_getl() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue the default value in the event of a failed read + * \param Filename the name of the .ini file to read from + * + * \return the value located at Key + */ +long ini_getl(const mTCHAR *Section, const mTCHAR *Key, long DefValue, const TCHAR *Filename) +{ + mTCHAR LocalBuffer[64]; + int len = ini_gets(Section, Key, _mT(""), LocalBuffer, sizearray(LocalBuffer), Filename); + return (len == 0) ? DefValue + : ((len >= 2 && _totupper((int)LocalBuffer[1]) == 'X') ? _mtcstol(LocalBuffer, NULL, 16) + : _mtcstol(LocalBuffer, NULL, 10)); +} + +#if defined INI_REAL +/** ini_getf() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue the default value in the event of a failed read + * \param Filename the name of the .ini file to read from + * + * \return the value located at Key + */ +INI_REAL ini_getf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL DefValue, const TCHAR *Filename) +{ + mTCHAR LocalBuffer[64]; + int len = ini_gets(Section, Key, _mT(""), LocalBuffer, sizearray(LocalBuffer), Filename); + return (len == 0) ? DefValue : ini_atof(LocalBuffer); +} +#endif + +/** ini_getbool() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue default value in the event of a failed read; it should + * zero (0) or one (1). + * \param Filename the name and full path of the .ini file to read from + * + * A true boolean is found if one of the following is matched: + * - A string starting with 'y' or 'Y' + * - A string starting with 't' or 'T' + * - A string starting with '1' + * + * A false boolean is found if one of the following is matched: + * - A string starting with 'n' or 'N' + * - A string starting with 'f' or 'F' + * - A string starting with '0' + * + * \return the true/false flag as interpreted at Key + */ +int ini_getbool(const mTCHAR *Section, const mTCHAR *Key, int DefValue, const TCHAR *Filename) +{ + mTCHAR LocalBuffer[2] = _mT(""); + int ret; + + ini_gets(Section, Key, _mT(""), LocalBuffer, sizearray(LocalBuffer), Filename); + LocalBuffer[0] = (mTCHAR)_totupper((int)LocalBuffer[0]); + if (LocalBuffer[0] == 'Y' || LocalBuffer[0] == '1' || LocalBuffer[0] == 'T') + ret = 1; + else if (LocalBuffer[0] == 'N' || LocalBuffer[0] == '0' || LocalBuffer[0] == 'F') + ret = 0; + else + ret = DefValue; + + return(ret); +} + +/** ini_getsection() + * \param idx the zero-based sequence number of the section to return + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_getsection(int idx, mTCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || idx < 0) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, NULL, NULL, idx, -1, Buffer, BufferSize, NULL); + (void)ini_close(&fp); + } /* if */ + if (!ok) + *Buffer = '\0'; + return (int)_mtcslen(Buffer); +} + +/** ini_getkey() + * \param Section the name of the section to browse through, or NULL to + * browse through the keys outside any section + * \param idx the zero-based sequence number of the key to return + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_getkey(const mTCHAR *Section, int idx, mTCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || idx < 0) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, Section, NULL, -1, idx, Buffer, BufferSize, NULL); + (void)ini_close(&fp); + } /* if */ + if (!ok) + *Buffer = '\0'; + return (int)_mtcslen(Buffer); +} + + +#if !defined INI_NOBROWSE +/** ini_browse() + * \param Callback a pointer to a function that will be called for every + * setting in the INI file. + * \param UserData arbitrary data, which the function passes on the + * \c Callback function + * \param Filename the name and full path of the .ini file to read from + * + * \return 1 on success, 0 on failure (INI file not found) + * + * \note The \c Callback function must return 1 to continue + * browsing through the INI file, or 0 to stop. Even when the + * callback stops the browsing, this function will return 1 + * (for success). + */ +int ini_browse(INI_CALLBACK Callback, void *UserData, const TCHAR *Filename) +{ + mTCHAR LocalBuffer[INI_BUFFERSIZE]; + int lenSec, lenKey; + enum quote_option quotes; + INI_FILETYPE fp; + + if (Callback == NULL) + return 0; + if (!ini_openread(Filename, &fp)) + return 0; + + LocalBuffer[0] = '\0'; /* copy an empty section in the buffer */ + lenSec = (int)_mtcslen(LocalBuffer) + 1; + for ( ;; ) { + mTCHAR *sp, *ep; + if (!ini_read(LocalBuffer + lenSec, INI_BUFFERSIZE - lenSec, &fp)) + break; + sp = skipleading(LocalBuffer + lenSec); + /* ignore empty strings and comments */ + if (*sp == '\0' || *sp == ';' || *sp == '#') + continue; + /* see whether we reached a new section */ + ep = _mtcsrchr(sp, ']'); + if (*sp == '[' && ep != NULL) { + *ep = '\0'; + ini_strncpy(LocalBuffer, sp + 1, INI_BUFFERSIZE, QUOTE_NONE); + lenSec = (int)_mtcslen(LocalBuffer) + 1; + continue; + } /* if */ + /* not a new section, test for a key/value pair */ + ep = _mtcschr(sp, '='); /* test for the equal sign or colon */ + if (ep == NULL) + ep = _mtcschr(sp, ':'); + if (ep == NULL) + continue; /* invalid line, ignore */ + *ep++ = '\0'; /* split the key from the value */ + striptrailing(sp); + ini_strncpy(LocalBuffer + lenSec, sp, INI_BUFFERSIZE - lenSec, QUOTE_NONE); + lenKey = (int)_mtcslen(LocalBuffer + lenSec) + 1; + /* clean up the value */ + sp = skipleading(ep); + sp = cleanstring(sp, "es); /* Remove a trailing comment */ + ini_strncpy(LocalBuffer + lenSec + lenKey, sp, INI_BUFFERSIZE - lenSec - lenKey, quotes); + /* call the callback */ + if (!Callback(LocalBuffer, LocalBuffer + lenSec, LocalBuffer + lenSec + lenKey, UserData)) + break; + } /* for */ + + (void)ini_close(&fp); + return 1; +} +#endif /* INI_NOBROWSE */ + +#if ! defined INI_READONLY +static void ini_tempname(mTCHAR *dest, const mTCHAR *source, int maxlength) +{ + mTCHAR *p; + + ini_strncpy(dest, source, maxlength, QUOTE_NONE); + p = _mtcschr(dest, '\0'); + assert(p != NULL); + *(p - 1) = '~'; +} + +static enum quote_option check_enquote(const mTCHAR *Value) +{ + const mTCHAR *p; + + /* run through the value, if it has trailing spaces, or '"', ';' or '#' + * characters, enquote it + */ + assert(Value != NULL); + for (p = Value; *p != '\0' && *p != '"' && *p != ';' && *p != '#'; p++) + /* nothing */; + return (*p != '\0' || (p > Value && *(p - 1) == ' ')) ? QUOTE_ENQUOTE : QUOTE_NONE; +} + +static void writesection(mTCHAR *LocalBuffer, const mTCHAR *Section, INI_FILETYPE *fp) +{ + if (Section != NULL && _mtcslen(Section) > 0) { + mTCHAR *p; + LocalBuffer[0] = '['; + ini_strncpy(LocalBuffer + 1, Section, INI_BUFFERSIZE - 4, QUOTE_NONE); /* -1 for '[', -1 for ']', -2 for '\r\n' */ + p = _mtcschr(LocalBuffer, '\0'); + assert(p != NULL); + *p++ = ']'; + _mtcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */ + if (fp != NULL) + (void)ini_write(LocalBuffer, fp); + } /* if */ +} + +static void writekey(mTCHAR *LocalBuffer, const mTCHAR *Key, const mTCHAR *Value, INI_FILETYPE *fp) +{ + mTCHAR *p; + enum quote_option option = check_enquote(Value); + ini_strncpy(LocalBuffer, Key, INI_BUFFERSIZE - 3, QUOTE_NONE); /* -1 for '=', -2 for '\r\n' */ + p = _mtcschr(LocalBuffer, '\0'); + assert(p != NULL); + *p++ = '='; + ini_strncpy(p, Value, INI_BUFFERSIZE - (p - LocalBuffer) - 2, option); /* -2 for '\r\n' */ + p = _mtcschr(LocalBuffer, '\0'); + assert(p != NULL); + _mtcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */ + if (fp != NULL) + (void)ini_write(LocalBuffer, fp); +} + +static int cache_accum(const mTCHAR *string, int *size, int max) +{ + int len = (int)_mtcslen(string); + if (*size + len >= max) + return 0; + *size += len; + return 1; +} + +static int cache_flush(mTCHAR *buffer, int *size, + INI_FILETYPE *rfp, INI_FILETYPE *wfp, INI_FILEPOS *mark) +{ + int terminator_len = (int)_mtcslen(INI_LINETERM); + int pos = 0; + + (void)ini_seek(rfp, mark); + assert(buffer != NULL); + buffer[0] = '\0'; + assert(size != NULL); + assert(*size <= INI_BUFFERSIZE); + while (pos < *size) { + (void)ini_read(buffer + pos, INI_BUFFERSIZE - pos, rfp); + while (pos < *size && buffer[pos] != '\0') + pos++; /* cannot use _mtcslen() because buffer may not be zero-terminated */ + } /* while */ + if (buffer[0] != '\0') { + assert(pos > 0 && pos <= INI_BUFFERSIZE); + if (pos == INI_BUFFERSIZE) + pos--; + buffer[pos] = '\0'; /* force zero-termination (may be left unterminated in the above while loop) */ + (void)ini_write(buffer, wfp); + } + ini_tell(rfp, mark); /* update mark */ + *size = 0; + /* return whether the buffer ended with a line termination */ + return (pos > terminator_len) && (_mtcscmp(buffer + pos - terminator_len, INI_LINETERM) == 0); +} + +static int close_rename(INI_FILETYPE *rfp, INI_FILETYPE *wfp, const TCHAR *filename, mTCHAR *buffer) +{ + (void)ini_close(rfp); + (void)ini_close(wfp); + (void)ini_tempname(buffer, filename, INI_BUFFERSIZE); + #if defined ini_remove || defined INI_REMOVE + (void)ini_remove(filename); + #endif + (void)ini_rename(buffer, filename); + return 1; +} + +/** ini_puts() + * \param Section the name of the section to write the string in + * \param Key the name of the entry to write, or NULL to erase all keys in the section + * \param Value a pointer to the buffer the string, or NULL to erase the key + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_puts(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const TCHAR *Filename) +{ + INI_FILETYPE rfp; + INI_FILETYPE wfp; + INI_FILEPOS mark; + INI_FILEPOS head, tail; + mTCHAR *sp, *ep; + mTCHAR LocalBuffer[INI_BUFFERSIZE]; + int len, match, flag, cachelen; + + assert(Filename != NULL); + if (!ini_openread(Filename, &rfp)) { + /* If the .ini file doesn't exist, make a new file */ + if (Key != NULL && Value != NULL) { + if (!ini_openwrite(Filename, &wfp)) + return 0; + writesection(LocalBuffer, Section, &wfp); + writekey(LocalBuffer, Key, Value, &wfp); + (void)ini_close(&wfp); + } /* if */ + return 1; + } /* if */ + + /* If parameters Key and Value are valid (so this is not an "erase" request) + * and the setting already exists, there are two short-cuts to avoid rewriting + * the INI file. + */ + if (Key != NULL && Value != NULL) { + match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), &head); + if (match) { + /* if the current setting is identical to the one to write, there is + * nothing to do. + */ + if (_mtcscmp(LocalBuffer,Value) == 0) { + (void)ini_close(&rfp); + return 1; + } /* if */ + /* if the new setting has the same length as the current setting, and the + * glue file permits file read/write access, we can modify in place. + */ + #if defined ini_openrewrite || defined INI_OPENREWRITE + /* we already have the start of the (raw) line, get the end too */ + ini_tell(&rfp, &tail); + /* create new buffer (without writing it to file) */ + writekey(LocalBuffer, Key, Value, NULL); + if (_mtcslen(LocalBuffer) == (size_t)(tail - head)) { + /* length matches, close the file & re-open for read/write, then + * write at the correct position + */ + (void)ini_close(&rfp); + if (!ini_openrewrite(Filename, &wfp)) + return 0; + (void)ini_seek(&wfp, &head); + (void)ini_write(LocalBuffer, &wfp); + (void)ini_close(&wfp); + return 1; + } /* if */ + #endif + } /* if */ + /* key not found, or different value & length -> proceed */ + } else if (Key != NULL && Value == NULL) { + /* Conversely, for a request to delete a setting; if that setting isn't + present, just return */ + match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), NULL); + if (!match) { + (void)ini_close(&rfp); + return 1; + } /* if */ + /* key found -> proceed to delete it */ + } /* if */ + + /* Get a temporary file name to copy to. Use the existing name, but with + * the last character set to a '~'. + */ + (void)ini_close(&rfp); + ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); + if (!ini_openwrite(LocalBuffer, &wfp)) + return 0; + /* In the case of (advisory) file locks, ini_openwrite() may have been blocked + * on the open, and after the block is lifted, the original file may have been + * renamed, which is why the original file was closed and is now reopened */ + if (!ini_openread(Filename, &rfp)) { + /* If the .ini file doesn't exist any more, make a new file */ + assert(Key != NULL && Value != NULL); + writesection(LocalBuffer, Section, &wfp); + writekey(LocalBuffer, Key, Value, &wfp); + (void)ini_close(&wfp); + return 1; + } /* if */ + + (void)ini_tell(&rfp, &mark); + cachelen = 0; + + /* Move through the file one line at a time until a section is + * matched or until EOF. Copy to temp file as it is read. + */ + len = (Section != NULL) ? (int)_mtcslen(Section) : 0; + if (len > 0) { + do { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + /* Failed to find section, so add one to the end */ + flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (Key!=NULL && Value!=NULL) { + if (!flag) + (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ + writesection(LocalBuffer, Section, &wfp); + writekey(LocalBuffer, Key, Value, &wfp); + } /* if */ + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ + } /* if */ + /* Copy the line from source to dest, but not if this is the section that + * we are looking for and this section must be removed + */ + sp = skipleading(LocalBuffer); + ep = _mtcsrchr(sp, ']'); + match = (*sp == '[' && ep != NULL && (int)(ep-sp-1) == len && _mtcsnicmp(sp + 1,Section,len) == 0); + if (!match || Key != NULL) { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* if */ + } while (!match); + } /* if */ + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + /* when deleting a section, the section head that was just found has not been + * copied to the output file, but because this line was not "accumulated" in + * the cache, the position in the input file was reset to the point just + * before the section; this must now be skipped (again) + */ + if (Key == NULL) { + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + (void)ini_tell(&rfp, &mark); + } /* if */ + + /* Now that the section has been found, find the entry. Stop searching + * upon leaving the section's area. Copy the file as it is read + * and create an entry if one is not found. + */ + len = (Key != NULL) ? (int)_mtcslen(Key) : 0; + for( ;; ) { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + /* EOF without an entry so make one */ + flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (Key!=NULL && Value!=NULL) { + if (!flag) + (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ + writekey(LocalBuffer, Key, Value, &wfp); + } /* if */ + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ + } /* if */ + sp = skipleading(LocalBuffer); + ep = _mtcschr(sp, '='); /* Parse out the equal sign */ + if (ep == NULL) + ep = _mtcschr(sp, ':'); + match = (ep != NULL && len > 0 && (int)(skiptrailing(ep,sp)-sp) == len && _mtcsnicmp(sp,Key,len) == 0); + if ((Key != NULL && match) || *sp == '[') + break; /* found the key, or found a new section */ + /* copy other keys in the section */ + if (Key == NULL) { + (void)ini_tell(&rfp, &mark); /* we are deleting the entire section, so update the read position */ + } else { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* if */ + } /* for */ + /* the key was found, or we just dropped on the next section (meaning that it + * wasn't found); in both cases we need to write the key, but in the latter + * case, we also need to write the line starting the new section after writing + * the key + */ + flag = (*sp == '['); + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (Key != NULL && Value != NULL) + writekey(LocalBuffer, Key, Value, &wfp); + /* cache_flush() reset the "read pointer" to the start of the line with the + * previous key or the new section; read it again (because writekey() destroyed + * the buffer) + */ + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + if (flag) { + /* the new section heading needs to be copied to the output file */ + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } else { + /* forget the old key line */ + (void)ini_tell(&rfp, &mark); + } /* if */ + /* Copy the rest of the INI file */ + while (ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* while */ + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ +} + +/* Ansi C "itoa" based on Kernighan & Ritchie's "Ansi C" book. */ +#define ABS(v) ((v) < 0 ? -(v) : (v)) + +static void strreverse(mTCHAR *str) +{ + int i, j; + for (i = 0, j = (int)_mtcslen(str) - 1; i < j; i++, j--) { + mTCHAR t = str[i]; + str[i] = str[j]; + str[j] = t; + } /* for */ +} + +static void long2str(long value, mTCHAR *str) +{ + int i = 0; + long sign = value; + + /* generate digits in reverse order */ + do { + int n = (int)(value % 10); /* get next lowest digit */ + str[i++] = (mTCHAR)(ABS(n) + '0'); /* handle case of negative digit */ + } while (value /= 10); /* delete the lowest digit */ + if (sign < 0) + str[i++] = '-'; + str[i] = '\0'; + + strreverse(str); +} + +/** ini_putl() + * \param Section the name of the section to write the value in + * \param Key the name of the entry to write + * \param Value the value to write + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_putl(const mTCHAR *Section, const mTCHAR *Key, long Value, const TCHAR *Filename) +{ + mTCHAR LocalBuffer[32]; + long2str(Value, LocalBuffer); + return ini_puts(Section, Key, LocalBuffer, Filename); +} + +#if defined INI_REAL +/** ini_putf() + * \param Section the name of the section to write the value in + * \param Key the name of the entry to write + * \param Value the value to write + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_putf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL Value, const TCHAR *Filename) +{ + mTCHAR LocalBuffer[64]; + ini_ftoa(LocalBuffer, Value); + return ini_puts(Section, Key, LocalBuffer, Filename); +} +#endif /* INI_REAL */ +#endif /* !INI_READONLY */ diff --git a/extern/SAASound/src/minIni/minIni.h b/extern/SAASound/src/minIni/minIni.h new file mode 100644 index 00000000..edaa6599 --- /dev/null +++ b/extern/SAASound/src/minIni/minIni.h @@ -0,0 +1,228 @@ +/* minIni - Multi-Platform INI file parser, suitable for embedded systems + * + * Copyright (c) CompuPhase, 2008-2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: minIni.h 53 2015-01-18 13:35:11Z thiadmer.riemersma@gmail.com $ + */ +#ifndef MININI_H +#define MININI_H + +#include "minGlue.h" + + +#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined INI_ANSIONLY +#if !defined UNICODE /* for Windows */ +#define UNICODE +#endif +#if !defined _UNICODE /* for C library */ +#define _UNICODE +#endif +#endif + +#if defined UNICODE +#include +#define t_string std::wstring +#else +#define TCHAR char +#define t_string std::string +#define _tfopen fopen +#define _tremove remove +#define _trename rename +#endif + +#if !defined __T +#define __T(s) s +#endif + +#if !defined _T +#define _T(s) __T(s) +#endif + +#if defined INI_ANSIONLY +#include +#include +#include +#define mTCHAR char +#define _mT(s) s +#define mString std::string +#define _mtcscat strcat +#define _mtcschr strchr +#define _mtcscmp strcmp +#define _mtcscpy strcpy +#define _mtcsicmp stricmp +#define _mtcslen strlen +#define _mtcsncmp strncmp +#define _mtcsnicmp strnicmp +#define _mtcsrchr strrchr +#define _mtcstol strtol +#define _mtcstod strtod +#define _mtotupper toupper +#define _mstprintf sprintf +#define _mtfgets fgets +#define _mtfputs fputs +#define _mtfopen fopen +#define _mtremove remove +#define _mtrename rename +#else +#define mTCHAR TCHAR +#define _mT(s) __T(s) +#define mString t_string +#define _mtcscat _tcscat +#define _mtcschr _tcschr +#define _mtcscmp _tcscmp +#define _mtcscpy _tcscpy +#define _mtcsicmp _tcsicmp +#define _mtcslen _tcslen +#define _mtcsncmp _tcsncmp +#define _mtcsnicmp _tcsnicmp +#define _mtcsrchr _tcsrchr +#define _mtcstol _tcstol +#define _mtcstod _tcstod +#define _mtotupper _totupper +#define _mstprintf _stprintf +#define _mtfgets _tfgets +#define _mtfputs _tfputs +#define _mtfopen _tfopen +#define _mtremove _tremove +#define _mtrename _trename +#endif + + +#if !defined INI_BUFFERSIZE + #define INI_BUFFERSIZE 512 +#endif + +#if defined __cplusplus + extern "C" { +#endif + +int ini_getbool(const mTCHAR *Section, const mTCHAR *Key, int DefValue, const TCHAR *Filename); +long ini_getl(const mTCHAR *Section, const mTCHAR *Key, long DefValue, const TCHAR *Filename); +int ini_gets(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *DefValue, mTCHAR *Buffer, int BufferSize, const TCHAR *Filename); +int ini_getsection(int idx, mTCHAR *Buffer, int BufferSize, const TCHAR *Filename); +int ini_getkey(const mTCHAR *Section, int idx, mTCHAR *Buffer, int BufferSize, const TCHAR *Filename); + +#if defined INI_REAL +INI_REAL ini_getf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL DefValue, const TCHAR *Filename); +#endif + +#if !defined INI_READONLY +int ini_putl(const mTCHAR *Section, const mTCHAR *Key, long Value, const TCHAR *Filename); +int ini_puts(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const TCHAR *Filename); +#if defined INI_REAL +int ini_putf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL Value, const TCHAR *Filename); +#endif +#endif /* INI_READONLY */ + +#if !defined INI_NOBROWSE +typedef int (*INI_CALLBACK)(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData); +int ini_browse(INI_CALLBACK Callback, void *UserData, const TCHAR *Filename); +#endif /* INI_NOBROWSE */ + +#if defined __cplusplus + } +#endif + + +#if defined __cplusplus + +#if defined __WXWINDOWS__ + #include "wxMinIni.h" +#else + #include + + /* The C++ class in minIni.h was contributed by Steven Van Ingelgem. */ + class minIni + { + public: + minIni(const t_string& filename) : iniFilename(filename) + { } + + bool getbool(const mString& Section, const mString& Key, bool DefValue=false) const + { return ini_getbool(Section.c_str(), Key.c_str(), int(DefValue), iniFilename.c_str()) != 0; } + + long getl(const mString& Section, const mString& Key, long DefValue=0) const + { return ini_getl(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); } + + int geti(const mString& Section, const mString& Key, int DefValue=0) const + { return static_cast(this->getl(Section, Key, long(DefValue))); } + + mString gets(const mString& Section, const mString& Key, const mString& DefValue=_mT("")) const + { + mTCHAR buffer[INI_BUFFERSIZE]; + ini_gets(Section.c_str(), Key.c_str(), DefValue.c_str(), buffer, INI_BUFFERSIZE, iniFilename.c_str()); + return buffer; + } + + mString getsection(int idx) const + { + mTCHAR buffer[INI_BUFFERSIZE]; + ini_getsection(idx, buffer, INI_BUFFERSIZE, iniFilename.c_str()); + return buffer; + } + + mString getkey(const mString& Section, int idx) const + { + mTCHAR buffer[INI_BUFFERSIZE]; + ini_getkey(Section.c_str(), idx, buffer, INI_BUFFERSIZE, iniFilename.c_str()); + return buffer; + } + +#if defined INI_REAL + INI_REAL getf(const mString& Section, const mString& Key, INI_REAL DefValue=0) const + { return ini_getf(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); } +#endif + +#if ! defined INI_READONLY + bool put(const mString& Section, const mString& Key, long Value) + { return ini_putl(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } + + bool put(const mString& Section, const mString& Key, int Value) + { return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; } + + bool put(const mString& Section, const mString& Key, bool Value) + { return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; } + + bool put(const mString& Section, const mString& Key, const mString& Value) + { return ini_puts(Section.c_str(), Key.c_str(), Value.c_str(), iniFilename.c_str()) != 0; } + + bool put(const mString& Section, const mString& Key, const mTCHAR* Value) + { return ini_puts(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } + +#if defined INI_REAL + bool put(const mString& Section, const mString& Key, INI_REAL Value) + { return ini_putf(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } +#endif + + bool del(const mString& Section, const mString& Key) + { return ini_puts(Section.c_str(), Key.c_str(), 0, iniFilename.c_str()) != 0; } + + bool del(const mString& Section) + { return ini_puts(Section.c_str(), 0, 0, iniFilename.c_str()) != 0; } +#endif + +#if !defined INI_NOBROWSE + bool browse(INI_CALLBACK Callback, void *UserData) const + { return ini_browse(Callback, UserData, iniFilename.c_str()) != 0; } +#endif + + private: + t_string iniFilename; + }; + +#endif /* __WXWINDOWS__ */ +#endif /* __cplusplus */ + +#endif /* MININI_H */ diff --git a/extern/SAASound/src/resource.h b/extern/SAASound/src/resource.h new file mode 100755 index 00000000..0b893bf3 --- /dev/null +++ b/extern/SAASound/src/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by SAASound.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/extern/SAASound/src/types.h b/extern/SAASound/src/types.h new file mode 100755 index 00000000..4eb62f48 --- /dev/null +++ b/extern/SAASound/src/types.h @@ -0,0 +1,34 @@ +// Part of SAASound copyright 1998-2018 Dave Hooper +// +// handy typedefs +// +////////////////////////////////////////////////////////////////////// + +#ifndef TYPES_H_INCLUDED +#define TYPES_H_INCLUDED + +#if defined(__i386__) || defined(WIN32) || \ + (defined(__alpha__) || defined(__alpha)) || \ + defined(__arm__) || \ + (defined(__mips__) && defined(__MIPSEL__)) +#else +#define __BIG_ENDIAN +#endif + + +#ifndef NULL +#define NULL 0 +#endif + +typedef struct +{ + int nNumberOfPhases; + bool bLooping; + int nLevels[2][2][16]; // [Resolution][Phase][Withinphase] +} ENVDATA; + +#ifdef WIN32 +extern "C" void _stdcall OutputDebugStringA (char*); +#endif + +#endif diff --git a/extern/SAASound/tests/SimCoupe dsk/testcases.dsk.gz b/extern/SAASound/tests/SimCoupe dsk/testcases.dsk.gz new file mode 100644 index 0000000000000000000000000000000000000000..e60aa3997a5e0ed09777859000c1803a1eae78e5 GIT binary patch literal 20721 zcmaKUd0did*Z<5o{Uv^lBh%rxbcshL}XO8aJNk>F64#0&M{B_IgD_ie1V^RCOZAY{DY^_RsGdxVi}cKmn%iXwG&by zjz{x9kk{9jTZs?DQjer%7_+DudU_NAHJ8~RZIIAs(C6B>aEK7e)h1MxnPz0#G5Vv| z-qj}V!_9i5=!y2oOp_n^7ww*gcPt!hNfSU=B2_-pfV53(w#z%nJAp}7@k~mWB7Y(@ zeGfjsK5`+h4RY!I^*yk z=`Pffe0w@6ECZ1iuu@{olf22E*1-^XQz)Ep%;=S9P2;QWX|THqvXNN zG=X7lT@TOkw7XLyrj!3<0T?`_PK?=b^&E<}WEXN^29d%sJGX)A_K__aUSS^|i|G=T zXt~xX;o>Tc=gL}JK#rQRH!!G`RkxlXD~o_moE9exdcQ4H7{*khG^R-R9X!=enGkmr zjB=x# z1Y>VVy)u%S4~b$D7`Bj``i=!D%j)(~H&wJq*uq8T zOYZSL6t+-w4m5PHQr=#$L>S!XYET}fP;{cb1WD3FJ>eb8F>c~vj9X;yGX7Zq2v~ay zALCtgswaFO?EBVI6|_h%u)l`qS^)2z#MiDD3MmtFl->*JM}s>_)7$GrZkJRXCGM2j zs7WK7j)dR3jM4Rs1B}-QJllbztFqee?4m32hz`p%g zBz_~>c%UY&ApAK5pcwfjpk7;fcCj3?FQ%4#+y$9c4!LX(E`*N6eD`no-)|o5mD)&W zi|HYsLjLDSm$&~t^`*p_B`eH}qh;?^H%~qbVjrLEjd@!nmtZZgTmaIy1bbybx{e{U zf|~I1TmOCbZ+2>Wh^)n)8MU;J1ON9CDN9!WK_~Bv6!l0p=|d%Z32;4EuvZ99;>x|^gxR8K zv2Rc3DuFgL2Rl+P4YYH^46H~NN*h`O2G-UtabanFDN7w0)elcSGB;q6eSS^q!?X3q z)VQei$Z`@*?6*+4=GKYt7VhP?3~cGH#aC;EQ1V)oGTrtF5+*bNQb z+tz`(POT8N#F6ermOPEbi`zV!_5(z!zFmQl!L^mF*Wg`OJ8)KB58se8dpsLh$D+q- zb{TgEwy8=r3l5IK2GN2KVQ29s%bh*Uu~|zr0ds`u+z3--skdUXxlsGy0zUuDu!?q+ z;1y0WQm}b76sOK zi=z&3m~$W=axjYF-7xCYkWL$LVU{#URi+bO2p~5a64MFp%o18uJuQmi-!M!Y2;%i! zZ-CGS?wgzAI!f&{mSM^S&pd%VR}#F$_q7<%=9`+l@H`cDro)P5GSh4y8SSRM5HC7+ zYNCMDFo#ikBd*|rAC124?#}PNmmgDXz$|$=i(@{CDPGQR9l0l+yLQvJ2@j5{4)NjQ zQaT4UYX~oiIZ!{z+AyT9KKmX!zuvX2xbvZLrS38R04s#mdU$nCi$YjMS^hNB-noM0Gj@@XTlhA!po%ybjz}s{kKo`Kuwuv_cn#gx zf3AUUNvSO1WNIehTt}<&^P?k1A64-+k>E9b5~*@{@;V_$u)es3;h{DRk-E>|%O+66 zj^(Oe3qHOL?U_Vk3$#1p)g)Q|wTu5bOVqh^8W*$nth^&Jr!hCu5?|f@!vaJZuMR~l&*&0_rxE6Z zbMr5JnX&9DZ62>fo48AZF0HTfU-{6Nx;hr|hEm~Jmi;2^72VQr2I9x{gKx)#1tg`l zOiGf96Ai*b?$X7R9mm(k(;heK$&s8X%X#JpJBPVqd&%6z1w=s2(Jt|9+0!kZbs2LR z;A7(;N%(lb0y(qx#ejJ^wIi50U0NU5s%k+E?wCGIs7^Ntgm`ylxD&$K&^qC5NL4Z= zqQ>Ol3blRuV74ckzbwEIbi{D4Yuu!h5jywg(Ve2R+*r=jawYq{N&>jWbbqS?w4;8~ zC-}sNlz4NgR>)fHeobNL%{p$olQ~7{of!UmiOJ2-V{%D@D*m$zmv63ijBs&5p2pMD z*^O^*_nzlBj&xF*=R=7SV%Yej*Gi}4;?TPu%nzX@=b-gJ8kK@rrIS>$?uWRiOQ9!l zbyfjdg3&Z-?FdG28VECMa)L3Luz-58rcgCc@f=p z@VxTK)gO!k!^d%VJUgHmUQ?_8>Z5jpM=8FKyBl^1+BY8<&kP>r=cMpqHz zHx`Dh&h~Q>ZgLW$vJM5mV4Lq9OEURm^{(kEzT{UI1HE6}MwSHMaoH#{x4*m(y!bob zDV2$Vx)ED0`Sd&J`J+~v45m>h?-*?jRkEC;IVpFqD5_U`<@=#;^cSS%);Qh2X-Bqn z_L)*Z)%#B0%CVG2^)1dmYDU$!D5Tt@wizqv=UL06!*w;9MOCIPR`c`edeLLjG~7Tf zM+Ywrq~*O!W4b<{9H&2nJaxUvl&HJT^H|b@dO{ZUa0f&vFeAPEb$O`X16kc+gLqS~ z7C=#~WeTC?4Fv}e9t$=!K33K4oPl|hz@tb9rK88%(duHMj<5ilC@^FD88d}D?_0qQ z&#fl&@C+e83IT&kad)gcv_gdC+O?VV>Q1qX=eVBd=KAdy9-R{M5an(v)D@33=!kkM zbWYE{GXk}&s8KtV);`a96NHfG(jJ(HSL@G>*1c&N6;Mfdl;m|NH;<)_Y4|y38P^>2 zG%hp3%=;|!S_eh+V%aaS;>eQj8r0Hq3=_X#%f-Vqndj`Y-^4a$Gc zJ8E!@SJ_yx9kZ%9qzNJ%TTGE`xh!CGfkm{#;91T{q+wHnk~FYQ?5%Qah-aDM?o-0DZ(C@ zn2hR7#Rnwl!Nw>7Ym#VJq>FzmePD{??K2pVnC^|!3Z=TP&*{{AUKu&rGStUAAKA_0 zH#&}J94b!c+}fm+=`prz$_DBQn>`%7XV9XpJ}3u5CJVxSJ;REsjETQmcaBz1)Cu4E zDjD3L(OPE|dE%(IayszdYvi1cX=jnY=QQ`2CdTL(1u^U1@h*heeXql<-fQ5oOHj}d zO63&!99olZ%<@B0(~3`S30hrcNu%fy0dxU@TV7sNzrQG`b6Sy864cSqe4pT3`hb2V zfSp$h=TeSgD^7l{XiTZz45GZ z3=_GO-3SY>W%W0%!Rn{R$Is!*jgPGl_UxCJmrl&)I&G%#Li+vhj74wYLEBtWwbNCN z>L$@ERWU9ekkB2m_@ir!)BCe^%&L*uJ)W#=n2y$p-$ifC?_4Jj=|+@S7Mq^^#FM7F ziAa<R`QVi#Um#Deq?;lK#{}@H_Z=(k@K5 zeNgp>M7gk$uokzmCAT>^^-e8(wqS~QQ>A~eVTO~_VB-$+}9~;zf(p6>cV&`9Q z><4E{%Llk+4LARHIr%7wV#)LT_4#5IqNOS7;BtcxB26io)2kzI4RhIOV|Pn!#nbEE z@+HY1f;||kSuC|^eho|g_tfQ)Fw1F}lg*LEA#3JXPebjDFTTi3n$l;_LbW}5w$aX_ z&k<5QW2tBj{ZxMC6To{DBduvJ=2OXhDG%d-59)9ZU0C_cL74|Fdc-VVp3RQwi9SVn zvRqk|;1TyUs%=E-Os(-k8?ZDzte&bxlupd{pB0WTO+bZI#^V}G%|q#%;qg)AMrS4Z zK9(;vcXOmViXAyMw|$es)}5#^gK&5gRUwJFpKrFdyjs#3K(Px#zTcNuLOpfY*G+m; zY(%?DiQ2!m&xv+dTU9HN*XfL2ESg&oH}1Rdi$$QvZ`LH%WIZIDl@uLlYg5%Haa$i1 zD_FRRgv#7A2tQ=tO0DVA9-a4xT|1KImpE2ub|?pz6T%0iW?^Q;_te4J^XHy2*7#}U zHz!EfhOdnzlU3TKE&V50vy6nNL3!4ZJ9linD_8-&-EYcm-XA?Sa$@%z+XE(7)AzE; zD{q?a>u42=gB?F8GJD~aahFyLBSq^4Q@ZDZiKQYaa}FFLYzUcsx7zx^k(lTh7BU@1 z`9aVWiHJjSdZ**9GAM*mQLp4kn~9Eo*bC3wJNK#k9>dh~FHJQ>5}#X6cj{>#47TdC z=06-GcB*mGXxp!M@B=O}GVw|Sb7O&M3VNoBS6XxbX7iBeL&?h%1DEz|xX)}h?sRrr zAR19lY>Y`qM-S6XZ68E<>=j6h5o#}p@8S5yOliF%B3YjY4g9@)O*Az8y4n??(}2E4 zx99Y;o6ERFl~U8bp>!Xn4$G;t-miRkxsN%lb$ILQ^zPVq90P`@(n>kUrSUe?EyFWq zx%*p`vBwqZ>T{QVU0)0H`hd8IpKhr|rlQChHTKkl_S&waN1&*wd4U{x>&YcK+fCUS ztUGbPOAc-LlOIr-($cgo>@Rl{CDe_%3AY_P#7*szHI|5* zBH~KNFjypM75?txNg`Wi6@l$AZob)>QU!@q*2vmZ~$}QdgV|f3m?%%ws}8*X})yu zmxJC9aF;Ofh{B(V(uaC~{BeX~(r*>h4dYQc}vuYIKKy;-x=)$%-K1jr$LXCsokw&bG=1&6I|l z2SiotrE5bik8yD6w1&1TKg>fh^1g@SAAk9I%rrzIbk^t+W;onax?5T^oJ=xGui zFZ4akY%{xMPfZdkB%{P9v^!IJ z%)8ZFg~=`W!#>olacA95K9jEKR_9tN(&B1zTM^qg&Fy^b{ub7UIZ#Ny^m(V@KP`yB zO8ISlk}1eDXkEse+BII9MW|fnROp%SoFmlD3-IZkxwrKt*q9HV8afs z^Ay+V(JQ0s5PXC$e)s(_1Y=}uEM57aclh$<=H=-NYZ}`4=n2zJu=+*m=n$FkOB?99 z4npWB|7h=)36uzECqCz`e9N0J^IyX9u^%B8~t;pkLmN)vs^re{X^x~Cx|JX;UfePfl@%Ewls zS~GMsg%xvecB3_S3HTFAo7J|s-cjh#-#E9?*2Rr(DDK1v7F_H*O~uLgFeB+1B5FIg zwsyLicozV6G)~$p4JpZ~|HEaS zUMGJt0(vUi1Gyi3nBkS&Gj%8L7W3{0C~pU<(^<^Q7wO%Z-efL$8GfWJd{#>W^H`Ur zB3Mwf#TcR4qN`Lt;2o42fHSmi?YysZX&%{=QEKr^!eC0kw(WK>JIBa))yoIG*! zmbRYs-NwLrYqyxsImkq%izN#>N~}@@m=ziDm^c1+CT#cz{9n9eV@hAIN@SOLUHF)_ zv9X)Bq3`sKTR~&Dh&!n76+AnpS)#&jvLw&kUIaPNGfRmQeAo%Js#b+-o4AL(;ieq_ zCCiXlGP%B;N5hxbT_&`JRsS3=4;-Xd z2GeIV14$&ox4fRU@a-N3oYg8LP7&N^h2Lkx@$xGU7)E2mp4T_?E{ZPbgA0cc?U`?? zYoiTDHE5JmF4wsstP25LiK&5y_Et;6!aANgRX5?DY40`WKVi!)f?l2+YQI33ozdlW zF(O`->{qR$>`xUi6MIG(6ZmxE4C9ETlX^V~XkhBpr z>W}D+#tItW$0-(mqYRd=Y=X5z|Kf4ZpKTg1n1}v-RQXp-)bGojjh;J+Mtu!B&yP-| z;l+!hhm^e`v?LC!1GcsYwLn>U{A+>Ayr2ZGF8T{?APwb}JhhzC+wST-t(?H2}8D0WDk%Li|>uH|3F9U^aa#n*&U&lcJ|{wlh`vZPYPF?Jow)MfVEEhQlRe zbNi%$ zMQnYHWp7j6@bT(UsOfMJKFN9YBE-7zXnDw;i0^`(qNf4^Ug||>H(Xn|*5Ht#sQSPp z(rL;i?pL>JEnSJG$IIaQ5Y)W@x)IXm?exu5cSS6+e`Xk?ar$V0fnIoh)T!1CL&j=y z#d&7T^1*uhNw*3kUMqK=v?cWHOnSssB|)m#XgTf-uX_A<4Y%?`olwP%qod|U!Iq(A#`hKgv6oP_5*(jR% zE!k|OGoyPM!*I^F!i6cz$5RiD+57-Y^&BW<3ZUN?v>O#IgClp>F7<``K%Ra>^0P%FbVSRLMlQ;=Nya+<#7u1&w-F{}uSl_4ly? zrnIC@a#4J_(T41QYl8&RF{n1nfvYpO>cOyvOphDzpQSahRt5UvPc45YdePfCeg=HDI~ zunIuGp>``sP|o`(NZ~-;+wO#H-#ge$<-?I|k(DUgp^w9N)$&H$Is6`GwKjaBCWVlqv(3c?Xh_N>Iir}j zFcZw}IwLnoA=wV&ja|$LpQFSLS`KS(l+#OAf4)A&sO`oVpBHr(FQl+ zC!-(Hxi9uPlKPUi1?@(+gMQyWzY#i9QKLmqSbWWr=MOLC(z!>7$s0hY>ai7U-KMst z5=&z*X(%YOrs-jOMeVWY&eb=Mr==hyy2~;5#vjv|S5=R~4od%Ml|I7lM4WrPx6SMi zFKu?0rLes5{Ee&B^@5Gg(@3mLh4sHmFZi!wjembb`t!n%rjoR&?vbKxgh-uV6VrJuHTSZ7ud&vfdtPF)PteyJJyfHQQi6v-Q`OgP57~ zk1RDYAI{ej1D9qHW$lZaPN)L`v-h?$-6%!8-Iv1D4pnMU!!n*5FprF)zqj|UGVYTs zZ$tQ`nMTB$*4%i?`|E&%O>Kwe%01jn*M(ClQm-^qyX$E8UY1vVbHr*VGu>TV{5GL_ zR{@cZzdDkj;&Kkm$}rv&ojwV1;vA{~KC%AHhmrhNTim2~MlW0d3Zjvtn)gW39Yir# z*f;+T^*~)k?ir^l(_~+xiQ9fGXZsa&qu-()4xxobmMcEpMc1p?z>2oAvdf?6olE>) zMRN9;4vg>I+PruCy()>hdOXhx`F4IM z2s5&3wF(<}ft=~JRu9wg6&6T0jZ%fu-n-di={cH&5^7~pNrLBWG_lj~9ncZ+ll|B9 zvE)B9Z8O81SZ&$lIQI#rHem?bAG$L5ZlA>SMp#7#-!YwBJN%j{4=!^JV=cWPm68SP z_xX$O_*LYl_Bv*AnLm#2{(_8p?J{9W*uS2^giBB5G=WDSm3K_KrU-5!y50}2y*3Ai^cw1 z%=7ui-m{S?=1z%eZ~9Vx<@I^+&;c-r7F#249%j}*=g+@qRahvB$VcSVWQ9ctS|W|s z%71Dglv*I(M*prdI4!!Ncq^Gfz0)*+$#kK;44ZZzSa@j6vBLi}SIfKc6G>tA>woY}{c##(yNFF5h(G#A)%>>d=NsSD zEt*c0_l!aa(2?$;_ro0I@WrZDbbkaQ?tpiDe-EY^9_|D>fdmSvuOBfK`uozRhSAsvjcX-MuV$y8i3!~M*0EBtcfIlpLZ0rh_ z^WnUw{ovS=k-9EwZ9p)g)1vSU)5@U&nx3YnPw91Q*B4u< zw^QD`IhVbp(c;w*o78pmtCRC*7!0>nl>AH!`Y6Eg1FljB3v#^s-F9qp4fW2NpF8AxZ zCLM)6Ugp(HU`=+v@>ek*7r0n2^y#bpUZo=@#7D<^B)iTj?{?TmxmTd1+=ByJLrNS< zFtunjq4@EQoZL5=Trv884^+#Oo|i^sz&TD z4BaTa0HW-ufVN}aW9GjHB|>BR?b8bE!;O7t;fUd=ikh+}%`VMC22R8Or=BR2x+t$b z3o2oI%zn~S*nf(+n8IhR5e$^7NcB9M>h`%;DMKdgqg=?)Byz2yl7ym#A=4-MtB$BN zX17}5PtjT$8fV;s1-vC-cv)ccgbXR+w{U?tgm{qCS~^ol=!PtOpX*=M)g z-R^F}ZBdN^6FL%PRb9_JJqYa|;V^cCgcy}kNtv+U*7ZbE^e@Ar0I7Bq(cw+b8Hq2A z`GooGS+Wbw^g7v4U7Z+Z9CReM(c0M$#E&~X?s0|fxNf4)9-1l zcUmd|!a$PbBt4Fd7;zX>(o=AUBNrxSI5`{xfAxiz15v`gwJW+Lb&~INp+LF(_j^|N zaqq({rkE2AnE4NRzj?SOstik$@ZPjm<%99}Nb)L?+bPSisA8!kA$#T8Z}*4=pg#G1 zxPfq^M=CHpo*NtT<|31a8Jka%^7q%Ft1tzIZS&jA(`hcu1shh|#06E=HOm_0O7nJ# zOX)t;P4qdfv)mbDwFOPCN|?6UOlPx;hxUTHsgmMDs8Uj5X8gnj6SIZ8_*Xl%E92KH z-|v(#t9Ld)A`>~+A4~tdHWG{W9)XU0Fa1CbuHKuY-9fv6Q0qIDuq&)LGdNrCka4Z9 zh`C$(M@!43Dr!Aoi{DX-r4aLW|LD9>N>ZdqxfMfBekBCoky|iUL|AkJiEI|)M9aid zzPhu&6HPlN>pP#no>z^--x76?dM+uiy+T!_ z)MK6-wolznU9wumw*j@qXJPZ-c#>I>BRseth`2Khj|Y&hUC6A1?BmwGG5Nr{mMik< z&q4ZUz+S+>z}&JYI;2{<7@rn62p#T6%ul(LI6{&L3k$P8(4gX)u+`DaUIdG)c6c8$ z*?+YmFs$?4Gv-2Im>TV^io;eNRzsV(E+$tvv2-KWty$rgJM7#2epV_Ho8(UPW2GnP zgLD{PDm;)fdvhB@O6d`dbj3yE*dW(IuE_^hK|4E9U6E8}uy!{^KwzqGPm0>=y4IC; zZ?5-QM`C7q2cQ2!;BbN{5ZvF1V@%i)3iML{Kq0K!Uk1*-y3+H9T9n;jiAFzVoxY8k zP~0D<=?smP7azWMCwg&b$o8;UQ(n{lQBxd8g9h;Rj+UgA~R!9U$)relLtP*Ow#`>k+S>7yY>3Ou9ckc*MT4_w#g zSLsuA^{OlVA+qk>!!nfsWEm=UwGWlNm&|23x~WG!9XbRKh%Up#t|Fxt9&<6e#Ju^1 z-JWySgriQAWu@;))w;yh8DL~-Z^tp-UkfIch`PNqYA9Y+3hpsmfd9ZYS~pZODo}Kb z419%)BQS2NH8|*wnmnPr?disqJT-E2U~odw!&X9v_b#04Dti(fPk1aeV3KQ4-t!)A zCa;EccnWRzu+HncLS38-pJ;-*T|e+BnRCkiy$j#nO6=QNZh%40*H!N&nCQSeEW%E} zU1>D?H;YgAI2#cHZ>2^2t_(&!Yc|H<7U0pbINyuIsf-dbPidsSR(K<&?6>gbrc)BK zt|5iJXM*s4!lv|EFQkI&bjFSQM)fSE;rqDY>@hO=2cae5LZMZc|!t&lViFa=SdaC(A zPxXI$`6uolH2FzS)k+w19*7fLhDR}^%LbWM1jI>SOg_*~wUtjV2I<>^y?_BAP891w zDx`{5!O6^M+m|`KCj;VQ9k=G)NfClW{_h;*WegP$8DO^(}Jt#+*~X4 z9&UIey67pqAdK5xPacl6OLHi5-7CBwJ8cwEbF@52G|)E*kEZZMizQjQI@M1x6LOIW z{!_@m^Y@)QOL_3qYI}}$NQN7KnfmL)4bHiC5iB;^cSMIkGEcxvf9R0r^r6*#+}=Xh zQ-Y$LSvN7xES<&cxJitMZA}{X@;Of{$BQ05-M%xFd&y-|w7Adk@#6#z1^xb&??f>+ zSAH6=vO1AOGd1<_41hObc8RYFXCHx6zDruj3`F>1hvX}f6(O-$vUuU)_ap02y{zl`2Ym7dU1p0q%E>g#UcdaH4h^bD#7aut zGs{%$X(y6{70uKzZ?BK{d|W+aE~*?p6y*QC2ANZC!i?11x?nc%6bmM|owE4O7VUO?&j|AWB>++}R+^5pLC+r0@dugy|*{r_x_liXd>nFal9 zFngd4S#C;Gsg(<7?i~46G|c$+(gj+SZqSq%hNl}=oHHa|tLnBCw!LIDmiw*i_AB!W zeO4WLH0bD%a^@g7to@kcQO-N%ts2-Q{Gw%Mmv+HT2KzCx5!5txLXnG)zvI<;iQ=G< zKBikBq+APYL}5m+Qq3-@40pKcDwM7FU8?FIbK*AqD7wIV5B0{ZrlKN*|1cRuRe_f< zW}?ex2F)g=YO5;DZz*DviAU##o07V*++}rJEd7=fsyb)?I<&K;$>lncwLX=lE0Al3dFj5|0wv4M&+n1q?Ft$ZU!sLWg6em+jHQ?%$JWK$MncA%1Rko=Us z$I3a$-HEzNADTx42NF@g)K9wo6Bq5$l?sB;B0_S+1C4I8WZ$dSaiRP)ey{f_sPGl+ zx~Ft*%}bh^hJ)+b?@KDX+9RsD@zV^=e@4wy?E8DBy0AH(rO~pd(=B3q(^2t@U&(IMYSO?CM7rtc|>IsEqli(TMlO*7n~gD=B6$~45#cN3m3`D(ri zjI}UD(=GkcSn1RYG`EVj-xVP&V)ig*7sjJl2kxAh4LV476AG_UMWreRFy9vH$;gqF zD|5fXC4-O3daM5>?L}xo+~z{B(~7Z^Pw=%XeT&p9Q%WjIL<#8UoD08u$_ zCv!?oc!T52%8-oH7x7NQuElmj84rhOB8(@AqSh6ILg!VueL*_hUQ^f6{A@RUsoDlM zI0AcnA3v7{d|Mi{N6vemT73qcylLNz{&Dfo$q4|)IGwEp`q zf0+(63hQl%3VB{O4M%h&Yfy_Ny_`v{Cz!c)J@T}^A*bZv@;%AVAw*JXJ&|`ejd-hx zXg7rn8@^D`Y(&DkpNP5+-`QB1wqUZMz_5ukT(Yk#GIRa#P;ZM<*=aXq&eeYwjIszW z#J7&=P-!Tb$Ol+$1jY@N1gc=>O@!AYc81KA^--=d7 zJ$>>d!qmToBM*kUVPO*ggVhJ~pkt!G(uxvF>qVi2C4Op0@4p_l`Am*|_ad!F)yn(` z>1mLi{UduL%N_bVoD542{FG@&+vaNnRSJr-QC%So{d?)DGq`^j*JUzz@ zUZPhW-*t8_;P1wQQbE`q_vNBRZz24?Enjh7I|zPNfKBMhScHicV}$u)O{{ROY`HEv zr{sWCheSWxG*Xz9F0K?~7bM1K;l=I*2&3x`>ZL9jXYFs=Vj|oT$SMMo6g&xj*urjA44v;{)l?%8YBH64#p5)kPRml7w$z;9@7?+oM^$l5n2+y2djH@_mC5&vFOFXaKG|l8{&|usv;J5p7*|E8f|c^ zv%7!7h~?_?!if9g+qEBg)y-p1Q-zjR^d#w0xVza>!0*q0BD_{$qRZBlH&WuBCDA_A zKW)+#&462%n67&ANm{C5q{~u-}-^)O(%Q~=uk!(bfff@JK2s~L0DbLRfyPXB4e?PHUQ zR?qnc84=$}oKHBv7{DFXe>TO1!j_i?X4kD8m7Tr;sOF|IcEGo=>YY zXkb7Kte}vd@f;Ka%vXR}OhKU>PR4j*kT9^%H4n3|?xLc|=O$X0lOPcgVkheo{w4%V z-@WbPH=U-RtB)Ng9T+FNDWs!Tt`l4ry$Q4e@|}vlC@y5S>|iBcEq&pM*?POU^?i`p z9FqDWY2%4K=51vDZ>qlipvBxYb7E@M^D6ZOpLQsstTH4N8S4}|Y^8`fG@qiuw7!nK zw+E4QZ6JtPBS4~jFH#RJ-o8P{xWx&;7?5NAeYWOxfay`e>DmDj(;-B{%NZqS*1e+wzd4Gmqht7kpD1bGR#&+{z2Yf=}=w)3- zBdFJTRdlFtUHS?{1RA_STv z|8PG!JE)RfL!_`OF>n3tsq&i|mNgi|M!&CnFVBdMop+`+m+6@g3;Fissce0U2$2); zqqIz+p1`x!4p~3ZnwqpC<~mQwt1szX<#1>zylb?~{DDQ?*QdDhUf9Ps5BOnzC}!9b zJu@~BQv8SO&r%O>HtI+W4CpK*Do)ohe;mxV$Iju`h)MDDSOw}npf}@@XR_$eLL}f! zYgfZU2Njh~J&b4zYFrz8dY7VQnw#{3zsjDjUcI5ea#c3+IJ@TzH zw-FvO)l?}~`Ln-qc)Cel=u`guT#Izy){aRERq+LyrtZdA`v&i&EWGP;ajR9)=*S;2 z4>5@!dt}CE7|dPBEMVqsc|{&_20Zhr@#!8HMDfmt7)x7{u`PJU1~{lM#?n?EB4hmS ztNfo`E2rcC#bC=|ei1xl^C^QKW6N7uPoIB2(0H?#PcH}QyMVoFAzdNJtXZIt0Oma< za3-G+=E@Hc0;z z*y|poYa22P7;xzM_YHh=!0}&SSiTC^p2YtbeQsr)r4KBC$;gKzfEowL83jdhxO`BE zi~+O}|9!}x_xI>YiX;f2Qu-pe%llIX&wSt=cofsAz+##rkiIq8D<9Hz37M4!+)!VP zN3k5uM*dy;=6@3$d4J<$Li;K_Wbgm0lsk!u?>m9(<*%ku^%a|9u1B?9}?Wk&Fen z(3in_&Yx^m>rDH|V9j7&0vP&YI&9=2*5DagLzdwZ!0<(|p3FTsd}J_ZFb^ZMfY;k` zRUTpvo{=?V84e?{Uj`Fp?qTR7gA;@42QUDaWhW1D0?)`AvJ8Fz!xzCsnR`$LB8_^5 zl$Hb1KL_?ogmkGQvy9ot5xp@?AgY1xN)AZYkG=|@JSy4pAFm5OorLKK0=C;3vkh5* zn&*H*fb#<1MT@e6-jPV&m5k2FJU>r+^Aq-#;01QA76F?yikS+nhAY**S0EGV+ z!8q=x4B8*JJ#JzfDizAPWPw6rAYGf-$7Ky!2Bku}^B2J`<1!2(07ERmP%P(C2nxxD zbj1P;GR9{N#dPN{f?Z~07=i(YGXO)8oJ%e!VVF9SuTAp1bx7zzz#>=d=_!RIz$8~=TcC}OGsBU@LEgcTx1N{G~`?gM|=?+@=}K34Zx5B_%kr2xDV;t&pw_4M1qX* z8N+ip;)~#r`!Wo@9~l6DE(V1ZLAvq)2Eh4cjL#T~;fODSLyBY=s2>>se+FuyG)UJi zfB|rR8RIhspo;n;I3(>;28VG#^nom)&>OR-P)_Tzyki!q;yBm~NEhEBvo^7h0|T<6 z|5dn*oJ|AqRpZQH1|zfD`(m{1@iUqE_FJ@1%oR*EeW8r6V_Eoq{3M&7YMt8Ff?E&Y>28F~zx^}aV%NnvN$R2RJ zFM?erK21S;j2qWIAJx)+MP{90ACCuKM3J1 zD9UX}HCMqi7m-<-eKF-W^0hLC?A)W~Duek&@XSRShNmAHu99jlf@cmPvr2kn%CE}T z${3$9Tx2l62%b44!%+E=!H!hp2%hl)7=U}QldqLAK4WlXFuw?%@%fa2_%R>F_xbNB zk<+r1cPs@}`~mg?a?K57Rx|KY0B8Dq{`6J2%=~FT5C;y=;l$HmFCf3{KxXY{A2;uf zc?QJX6?wh$+e=0UCWgy6$bKr;sNS6Wo_y7XkgiIA z0Z61kwI*YH#!yyE`y#ljQicKYk)Z&Nz(Kl@00WRnfoe_0_>2KpOZy_Y3;8L7XByz8 z&x+`Wt>qo_K^2$4UO*Z?ip;vjKHd(zY9OjVm*lU)Wh8k3+PKb&L}KRv_XiTGD|jXn znI&t;ChNIA|1X2f!#)*#qhkQWW+YanHwJ!LzV-xoW-~HN){tdT>Gl6I7#=9Y009`z z0t|gIa3Bp`0neNT7-Wpk82bFb42H|h!`p$vh3L%isDX3=Ix`JCdmtcysJ54PEC*FM zgS~1#D_p(~|5Pw*+JHo1R74*EO3Y+Pmj(N{QIQ-&#*mfdkz6?9i{OytPZ=C007(WE zN3%D^u28NFkmPJo#YwOiAU#`=S-aWCfdSbk^i{ZwB-1`hvIB$Zfy}}J4<1OQ4&WJC zLq>2|9!Tt$!B`ndz6eBGDIm#3po*(tuRKWCMP$}f;JW)_?26^e07)+TOp?C}pZ!!Y z;U8z1T5Lm7p&SFK=i(tWdK9590RE5N+4Zj0E3M2 z8ACCBwSu2C8rbdsRZZ08s}9WUv3LaG6A%03_=ELV776 zy*Z$YvtTbk%9N2=2JGX$y)pej&|Ud#?(|i7(5Fex_hSmOV|W1a<%rCx1kTzQ(+_w) zpvfhm3Ole@+2<+ftME?~M9s%mAgP2t0(Ae#kS-4JmP_OqGKQ@Ci~y~`FM>nJG7L=s zLl_V-KoJ`U=>oHlhXJuBV|>Pt4M%(t91`~_g98gla!9t}9PqpWrvVhR08|0|q7$p_%OpA6$0P@|0xgkQ(7u=jKx%+ipsXRA11@O~Y^^h(=?pJ{)12_{95rCc% zK2M`xgUd9C3<0tCyi-d9Bz`;lIH2-|g>wFv<%fX@An?;zcOYHBfXwxO9WGN*zXyCs zrO)56SkB)@ei)dZ0UKL$AYEsXSqT7LUkorHn?k<|ul+R1Spd&}C>Kry7AgbMdH|Va p!9I=!ZlXxe|El~jFnPN0Sql0pyzPIbpmi98zTNSgU$%eq{{Uw+4`cuU literal 0 HcmV?d00001 diff --git a/extern/SAASound/tools/freqdat.py b/extern/SAASound/tools/freqdat.py new file mode 100644 index 00000000..ca93d7e9 --- /dev/null +++ b/extern/SAASound/tools/freqdat.py @@ -0,0 +1,21 @@ +# Part of SAASound copyright 1998-2018 Dave Hooper +# +# freqdat.py +# You can use this to generate an alternative SAAFreq.dat +# +# By default, the SAA-1099 generator will compute a frequency table at runtime +# but you can set the SAA_FIXED_CLOCKRATE define flag and supply your own (fixed, +# precompiled) frequency table named SAAFreq.dat +# +# This is not super-useful for general-purpose computing devices (e.g PCs), but is +# useful for embedded or low-capability devices, or for hardware-based implementations +# +# To use this simply run the file and pipe the output into src/SAAFreq.dat + +BASE = 8000000 +SCALE = 4096 + +for octave in range(0,8): + for offset in range(0,256): + f = 2 * SCALE * (BASE/8000000) * 15625 * (2**octave) / (511-offset) + print(int(f), ',') diff --git a/extern/SAASound/tools/levels.py b/extern/SAASound/tools/levels.py new file mode 100644 index 00000000..3e63b028 --- /dev/null +++ b/extern/SAASound/tools/levels.py @@ -0,0 +1,65 @@ +amps = [ + '0000000000000000000000000000000000000000000000000000000000000000', + '0000000011110000000000000000000000000000000000000000000000000000', + '0000000000001111111100000000000000000000000000000000000000000000', + '0000000011111111111100000000000000000000000000000000000000000000', + '0000000000000000000000000000000000001111111111111111000000000000', + '0000000011110000000000000000000000001111111111111111000000000000', + '0000000000001111111100000000000000001111111111111111000000000000', + '0000000011111111111100000000000000001111111111111111000000000000', + '1111000000000000000011111111111111110000000000000000111111111111', + '1111000011110000000011111111111111110000000000000000111111111111', + '1111000000001111111111111111111111110000000000000000111111111111', + '1111000011111111111111111111111111110000000000000000111111111111', + '1111000000000000000011111111111111111111111111111111111111111111', + '1111000011110000000011111111111111111111111111111111111111111111', + '1111000000001111111111111111111111111111111111111111111111111111', + '1111000011111111111111111111111111111111111111111111111111111111', +] + +envs = [ + '0000000000000000000000000000000000000000000000000000000000000000', + '0000000000001000000000000000100000000000000010000000000000001000', + '0000010000000100000001000000010000000100000001000000010000000100', + '0000010000001100000001000000110000000100000011000000010000001100', + '0011000000110000001100000011000000110000001100000011000000110000', + '0011000000111000001100000011100000110000001110000011000000111000', + '0011010000110100001101000011010000110100001101000011010000110100', + '0011010000111100001101000011110000110100001111000011010000111100', + '1100001111000011110000111100001111000011110000111100001111000011', + '1100001111001011110000111100101111000011110010111100001111001011', + '1100011111000111110001111100011111000111110001111100011111000111', + '1100011111001111110001111100111111000111110011111100011111001111', + '1111001111110011111100111111001111110011111100111111001111110011', + '1111001111111011111100111111101111110011111110111111001111111011', + '1111011111110111111101111111011111110111111101111111011111110111', + '1111011111111111111101111111111111110111111111111111011111111111', +] + +vas = [] +for ai,a in enumerate(amps): + v = 0 + for x in range(0,64): + al = a[x] + if al=='1': + v += 1 + vas.append(v) + # print('%d %d'%(ai,v)) +print(vas) + +vs = [] + +for ai,a in enumerate(amps): + vas = [] + for ei,e in enumerate(envs): + v = 0 + for x in range(0,64): + al = a[x] + el = e[x] + if al=='1' and el=='1': + v += 1 + vas.append(v) + print('{'+','.join([str(x) for x in vas])+'}') + vs.append(vas) + +#print(vs) diff --git a/src/engine/platform/saa.h b/src/engine/platform/saa.h index d82f6fca..9a73f90a 100644 --- a/src/engine/platform/saa.h +++ b/src/engine/platform/saa.h @@ -5,6 +5,12 @@ #include #include "sound/saa1099.h" +enum DivSAACores { + DIV_SAA_CORE_MAME=0, + DIV_SAA_CORE_SAASOUND, + DIV_SAA_CORE_E +}; + class DivPlatformSAA1099: public DivDispatch { protected: struct Channel { @@ -48,6 +54,10 @@ class DivPlatformSAA1099: public DivDispatch { unsigned char saaEnv[2]; unsigned char saaNoise[2]; friend void putDispatchChan(void*,int,int); + + void acquire_e(short* bufL, short* bufR, size_t start, size_t len); + void acquire_saaSound(short* bufL, short* bufR, size_t start, size_t len); + void acquire_mame(short* bufL, short* bufR, size_t start, size_t len); public: void acquire(short* bufL, short* bufR, size_t start, size_t len);