add SAASound as an alternate SAA1099 core

DOES NOT WORK YET
This commit is contained in:
tildearrow 2022-02-13 17:02:49 -05:00
parent 7c80a88bbf
commit 2f766553e8
59 changed files with 5815 additions and 0 deletions

View file

@ -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

15
extern/SAASound/.gitignore vendored Normal file
View file

@ -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

102
extern/SAASound/CMakeLists.txt vendored Normal file
View file

@ -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 ()

21
extern/SAASound/INSTALL vendored Executable file
View file

@ -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 <dave@beermex.com>
and Simon Owen <simon@simonowen.com>

31
extern/SAASound/LICENCE vendored Executable file
View file

@ -0,0 +1,31 @@
SAASound - a portable Phillips SAA 1099 sound chip emulator
-----------------------------------------------------------
Copyright (c) 1998-2004, Dave Hooper <dave@beermex.com>
Copyright (c) 2004-2018, Dave Hooper <dave@beermex.com> + Simon Owen <simon@simonowen.com>
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.

5
extern/SAASound/MODIFIED.md vendored Normal file
View file

@ -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.

7
extern/SAASound/README vendored Executable file
View file

@ -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 <dave@beermex.com>
Homepage: https://github.com/stripwax/SAASound

14
extern/SAASound/README.MD vendored Executable file
View file

@ -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

16
extern/SAASound/VS17/.gitignore vendored Normal file
View file

@ -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

43
extern/SAASound/VS17/SAASound.sln vendored Normal file
View file

@ -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

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,665 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugWithSymbols|Win32">
<Configuration>DebugWithSymbols</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="DebugWithSymbols|x64">
<Configuration>DebugWithSymbols</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release-SSE|Win32">
<Configuration>Release-SSE</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release-SSE|x64">
<Configuration>Release-SSE</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleaseWithSymbols|Win32">
<Configuration>ReleaseWithSymbols</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleaseWithSymbols|x64">
<Configuration>ReleaseWithSymbols</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\include\SAASound.h" />
<ClInclude Include="..\..\src\defns.h" />
<ClInclude Include="..\..\src\minIni\minGlue-ccs.h" />
<ClInclude Include="..\..\src\minIni\minGlue-efsl.h" />
<ClInclude Include="..\..\src\minIni\minGlue-FatFs.h" />
<ClInclude Include="..\..\src\minIni\minGlue-ffs.h" />
<ClInclude Include="..\..\src\minIni\minGlue-Linux.h" />
<ClInclude Include="..\..\src\minIni\minGlue-mdd.h" />
<ClInclude Include="..\..\src\minIni\minGlue-stdio.h" />
<ClInclude Include="..\..\src\minIni\minGlue.h" />
<ClInclude Include="..\..\src\minIni\minGlue_stdio_tchar.h" />
<ClInclude Include="..\..\src\minIni\minIni.h" />
<ClInclude Include="..\..\src\SAAAmp.h" />
<ClInclude Include="..\..\src\SAAConfig.h" />
<ClInclude Include="..\..\src\SAADevice.h" />
<ClInclude Include="..\..\src\SAAEnv.h" />
<ClInclude Include="..\..\src\SAAFreq.h" />
<ClInclude Include="..\..\src\SAAImpl.h" />
<ClInclude Include="..\..\src\SAANoise.h" />
<ClInclude Include="..\..\src\SAASndC.h" />
<ClInclude Include="..\..\src\types.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\minIni\minIni.c" />
<ClCompile Include="..\..\src\SAAAmp.cpp" />
<ClCompile Include="..\..\src\SAAConfig.cpp" />
<ClCompile Include="..\..\src\SAADevice.cpp" />
<ClCompile Include="..\..\src\SAAEnv.cpp" />
<ClCompile Include="..\..\src\SAAFreq.cpp" />
<ClCompile Include="..\..\src\SAAImpl.cpp" />
<ClCompile Include="..\..\src\SAANoise.cpp" />
<ClCompile Include="..\..\src\SAASndC.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\INSTALL" />
<None Include="..\..\LICENCE" />
<None Include="..\..\README" />
<None Include="..\..\resources\SAASound.cfg" />
<None Include="..\..\resources\SAASound.def" />
<None Include="..\..\src\SAAFreq.dat" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="SAASound.rc" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{EDEC064F-E383-4824-8892-EF4B47C9841E}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>SAASound</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithSymbols|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugWithSymbols|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-SSE|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithSymbols|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugWithSymbols|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-SSE|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithSymbols|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugWithSymbols|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release-SSE|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithSymbols|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugWithSymbols|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release-SSE|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(SolutionDir)\..\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(SolutionDir)\..\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(SolutionDir)\..\include;$(IncludePath)</IncludePath>
<CodeAnalysisRuleSet>NativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithSymbols|Win32'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(SolutionDir)\..\include;$(IncludePath)</IncludePath>
<CodeAnalysisRuleSet>NativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugWithSymbols|Win32'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(SolutionDir)\..\include;$(IncludePath)</IncludePath>
<CodeAnalysisRuleSet>NativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-SSE|Win32'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(SolutionDir)\..\include;$(IncludePath)</IncludePath>
<CodeAnalysisRuleSet>NativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(SolutionDir)\..\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithSymbols|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(SolutionDir)\..\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugWithSymbols|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(SolutionDir)\..\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-SSE|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(SolutionDir)\..\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>false</StringPooling>
<SmallerTypeCheck>false</SmallerTypeCheck>
<ControlFlowGuard>Guard</ControlFlowGuard>
<FunctionLevelLinking>false</FunctionLevelLinking>
<EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
<CreateHotpatchableImage>false</CreateHotpatchableImage>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
<IntrinsicFunctions>false</IntrinsicFunctions>
<WholeProgramOptimization>false</WholeProgramOptimization>
<AssemblerOutput>NoListing</AssemblerOutput>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<ExpandAttributedSource>false</ExpandAttributedSource>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FloatingPointModel>Strict</FloatingPointModel>
<FloatingPointExceptions>true</FloatingPointExceptions>
<CompileAs>CompileAsCpp</CompileAs>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<ModuleDefinitionFile>$(SolutionDir)..\resources\SAASound.def</ModuleDefinitionFile>
<AdditionalDependencies />
<OptimizeReferences>false</OptimizeReferences>
<EnableCOMDATFolding>false</EnableCOMDATFolding>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<AssemblyDebug>true</AssemblyDebug>
<GenerateMapFile>true</GenerateMapFile>
<MapExports>true</MapExports>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>false</StringPooling>
<SmallerTypeCheck>false</SmallerTypeCheck>
<ControlFlowGuard>Guard</ControlFlowGuard>
<FunctionLevelLinking>false</FunctionLevelLinking>
<EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
<CreateHotpatchableImage>false</CreateHotpatchableImage>
<IntrinsicFunctions>false</IntrinsicFunctions>
<OmitFramePointers>false</OmitFramePointers>
<WholeProgramOptimization>false</WholeProgramOptimization>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
<AssemblerOutput>NoListing</AssemblerOutput>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<ExpandAttributedSource>true</ExpandAttributedSource>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FloatingPointModel>Strict</FloatingPointModel>
<FloatingPointExceptions>true</FloatingPointExceptions>
<CompileAs>CompileAsCpp</CompileAs>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<AdditionalDependencies />
<ModuleDefinitionFile>$(SolutionDir)..\resources\SAASound.def</ModuleDefinitionFile>
<OptimizeReferences>false</OptimizeReferences>
<EnableCOMDATFolding>false</EnableCOMDATFolding>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<AssemblyDebug>true</AssemblyDebug>
<GenerateMapFile>true</GenerateMapFile>
<MapExports>true</MapExports>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>false</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
<RemoveUnreferencedCodeData>false</RemoveUnreferencedCodeData>
<DebugInformationFormat>None</DebugInformationFormat>
<OmitFramePointers>true</OmitFramePointers>
<EnablePREfast>false</EnablePREfast>
<StringPooling>true</StringPooling>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FloatingPointModel>Fast</FloatingPointModel>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<WholeProgramOptimization>true</WholeProgramOptimization>
<EnforceTypeConversionRules>false</EnforceTypeConversionRules>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<ControlFlowGuard>false</ControlFlowGuard>
<EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
<AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
<MapExports>false</MapExports>
<ModuleDefinitionFile>$(SolutionDir)..\resources\SAASound.def</ModuleDefinitionFile>
<AdditionalDependencies />
<LinkTimeCodeGeneration>PGOptimization</LinkTimeCodeGeneration>
<ProfileGuidedDatabase>$(ProjectDir)Profiles\$(PlatformTarget)\$(ConfigurationName)\$(TargetName).pgd</ProfileGuidedDatabase>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithSymbols|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>false</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
<RemoveUnreferencedCodeData>false</RemoveUnreferencedCodeData>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<OmitFramePointers>true</OmitFramePointers>
<EnablePREfast>false</EnablePREfast>
<StringPooling>true</StringPooling>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FloatingPointModel>Fast</FloatingPointModel>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<WholeProgramOptimization>true</WholeProgramOptimization>
<EnforceTypeConversionRules>false</EnforceTypeConversionRules>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<ExpandAttributedSource>true</ExpandAttributedSource>
<AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
<BrowseInformation>true</BrowseInformation>
<EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
<ControlFlowGuard>false</ControlFlowGuard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<MapExports>true</MapExports>
<ModuleDefinitionFile>$(SolutionDir)..\resources\SAASound.def</ModuleDefinitionFile>
<AdditionalDependencies>
</AdditionalDependencies>
<GenerateMapFile>true</GenerateMapFile>
<LinkTimeCodeGeneration>PGOptimization</LinkTimeCodeGeneration>
<ProfileGuidedDatabase>$(ProjectDir)Profiles\$(PlatformTarget)\$(ConfigurationName)\$(TargetName).pgd</ProfileGuidedDatabase>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugWithSymbols|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<FunctionLevelLinking>false</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>AdvancedVectorExtensions512</EnableEnhancedInstructionSet>
<RemoveUnreferencedCodeData>false</RemoveUnreferencedCodeData>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<OmitFramePointers>true</OmitFramePointers>
<EnablePREfast>false</EnablePREfast>
<StringPooling>false</StringPooling>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FloatingPointModel>Fast</FloatingPointModel>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
<WholeProgramOptimization>false</WholeProgramOptimization>
<EnforceTypeConversionRules>false</EnforceTypeConversionRules>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<ExpandAttributedSource>true</ExpandAttributedSource>
<AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
<BrowseInformation>true</BrowseInformation>
<ControlFlowGuard>Guard</ControlFlowGuard>
<EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
<FloatingPointExceptions>true</FloatingPointExceptions>
<CreateHotpatchableImage>false</CreateHotpatchableImage>
<SmallerTypeCheck>true</SmallerTypeCheck>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>false</EnableCOMDATFolding>
<OptimizeReferences>false</OptimizeReferences>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<MapExports>true</MapExports>
<ModuleDefinitionFile>$(SolutionDir)..\resources\SAASound.def</ModuleDefinitionFile>
<AdditionalDependencies>
</AdditionalDependencies>
<GenerateMapFile>true</GenerateMapFile>
<LinkTimeCodeGeneration>PGInstrument</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release-SSE|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>false</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>AdvancedVectorExtensions512</EnableEnhancedInstructionSet>
<RemoveUnreferencedCodeData>false</RemoveUnreferencedCodeData>
<DebugInformationFormat>None</DebugInformationFormat>
<OmitFramePointers>true</OmitFramePointers>
<EnablePREfast>false</EnablePREfast>
<StringPooling>true</StringPooling>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FloatingPointModel>Fast</FloatingPointModel>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<EnforceTypeConversionRules>false</EnforceTypeConversionRules>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<WholeProgramOptimization>true</WholeProgramOptimization>
<EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
<ControlFlowGuard>false</ControlFlowGuard>
<AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
<MapExports>false</MapExports>
<ModuleDefinitionFile>$(SolutionDir)..\resources\SAASound.def</ModuleDefinitionFile>
<AdditionalDependencies>
</AdditionalDependencies>
<LinkTimeCodeGeneration>PGOptimization</LinkTimeCodeGeneration>
<ProfileGuidedDatabase>$(ProjectDir)Profiles\$(PlatformTarget)\$(ConfigurationName)\$(TargetName).pgd</ProfileGuidedDatabase>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>false</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>
</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DebugInformationFormat>None</DebugInformationFormat>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<StringPooling>true</StringPooling>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RemoveUnreferencedCodeData>false</RemoveUnreferencedCodeData>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<EnforceTypeConversionRules>false</EnforceTypeConversionRules>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<ControlFlowGuard>false</ControlFlowGuard>
<EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
<AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
<ModuleDefinitionFile>$(SolutionDir)..\resources\SAASound.def</ModuleDefinitionFile>
<AdditionalDependencies />
<LinkTimeCodeGeneration>PGOptimization</LinkTimeCodeGeneration>
<ProfileGuidedDatabase>$(ProjectDir)Profiles\$(PlatformTarget)\$(ConfigurationName)\$(TargetName).pgd</ProfileGuidedDatabase>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithSymbols|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>false</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>
</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<EnableEnhancedInstructionSet>AdvancedVectorExtensions512</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<StringPooling>true</StringPooling>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RemoveUnreferencedCodeData>false</RemoveUnreferencedCodeData>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<EnforceTypeConversionRules>false</EnforceTypeConversionRules>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<ExpandAttributedSource>true</ExpandAttributedSource>
<AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
<BrowseInformation>true</BrowseInformation>
<WholeProgramOptimization>true</WholeProgramOptimization>
<EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
<ControlFlowGuard>false</ControlFlowGuard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<ModuleDefinitionFile>$(SolutionDir)..\resources\SAASound.def</ModuleDefinitionFile>
<AdditionalDependencies>
</AdditionalDependencies>
<LinkTimeCodeGeneration>PGOptimization</LinkTimeCodeGeneration>
<GenerateMapFile>true</GenerateMapFile>
<MapExports>true</MapExports>
<ProfileGuidedDatabase>$(ProjectDir)Profiles\$(PlatformTarget)\$(ConfigurationName)\$(TargetName).pgd</ProfileGuidedDatabase>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugWithSymbols|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<FunctionLevelLinking>false</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>
</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<EnableEnhancedInstructionSet>AdvancedVectorExtensions512</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<StringPooling>true</StringPooling>
<BufferSecurityCheck>true</BufferSecurityCheck>
<RemoveUnreferencedCodeData>false</RemoveUnreferencedCodeData>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<EnforceTypeConversionRules>false</EnforceTypeConversionRules>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<ExpandAttributedSource>true</ExpandAttributedSource>
<AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
<BrowseInformation>true</BrowseInformation>
<WholeProgramOptimization>false</WholeProgramOptimization>
<ControlFlowGuard>Guard</ControlFlowGuard>
<EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
<FloatingPointExceptions>true</FloatingPointExceptions>
<CreateHotpatchableImage>false</CreateHotpatchableImage>
<GuardEHContMetadata>true</GuardEHContMetadata>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>false</EnableCOMDATFolding>
<OptimizeReferences>false</OptimizeReferences>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<ModuleDefinitionFile>$(SolutionDir)..\resources\SAASound.def</ModuleDefinitionFile>
<AdditionalDependencies>
</AdditionalDependencies>
<LinkTimeCodeGeneration>PGInstrument</LinkTimeCodeGeneration>
<GenerateMapFile>true</GenerateMapFile>
<MapExports>true</MapExports>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release-SSE|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>false</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>
</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;SAASOUND_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DebugInformationFormat>None</DebugInformationFormat>
<EnableEnhancedInstructionSet>AdvancedVectorExtensions512</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RemoveUnreferencedCodeData>false</RemoveUnreferencedCodeData>
<EnforceTypeConversionRules>false</EnforceTypeConversionRules>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
<ControlFlowGuard>false</ControlFlowGuard>
<AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
<AdditionalDependencies />
<ModuleDefinitionFile>$(SolutionDir)..\resources\SAASound.def</ModuleDefinitionFile>
<LinkTimeCodeGeneration>PGOptimization</LinkTimeCodeGeneration>
<ProfileGuidedDatabase>$(ProjectDir)Profiles\$(PlatformTarget)\$(ConfigurationName)\$(TargetName).pgd</ProfileGuidedDatabase>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="C++ Source Code">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="C++ Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resources">
<UniqueIdentifier>{b1405cde-b230-4c13-915e-159136ba7d5a}</UniqueIdentifier>
</Filter>
<Filter Include="Data">
<UniqueIdentifier>{3a21d264-bd0a-4975-b6ca-7aacd2d92334}</UniqueIdentifier>
</Filter>
<Filter Include="C++ Source Code\minIni">
<UniqueIdentifier>{a8d6ad5a-de03-49c0-ade1-f28bb9655645}</UniqueIdentifier>
</Filter>
<Filter Include="C++ Header Files\minIni">
<UniqueIdentifier>{b0edb4ea-6878-4f31-9442-ad3542ff27c5}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\include\SAASound.h">
<Filter>C++ Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\SAAAmp.h">
<Filter>C++ Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\SAAEnv.h">
<Filter>C++ Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\SAAFreq.h">
<Filter>C++ Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\SAAImpl.h">
<Filter>C++ Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\SAANoise.h">
<Filter>C++ Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\SAASndC.h">
<Filter>C++ Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\types.h">
<Filter>C++ Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>C++ Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\SAADevice.h">
<Filter>C++ Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\defns.h">
<Filter>C++ Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\minIni\minGlue.h">
<Filter>C++ Header Files\minIni</Filter>
</ClInclude>
<ClInclude Include="..\..\src\minIni\minGlue-ccs.h">
<Filter>C++ Header Files\minIni</Filter>
</ClInclude>
<ClInclude Include="..\..\src\minIni\minGlue-efsl.h">
<Filter>C++ Header Files\minIni</Filter>
</ClInclude>
<ClInclude Include="..\..\src\minIni\minGlue-FatFs.h">
<Filter>C++ Header Files\minIni</Filter>
</ClInclude>
<ClInclude Include="..\..\src\minIni\minGlue-ffs.h">
<Filter>C++ Header Files\minIni</Filter>
</ClInclude>
<ClInclude Include="..\..\src\minIni\minGlue-Linux.h">
<Filter>C++ Header Files\minIni</Filter>
</ClInclude>
<ClInclude Include="..\..\src\minIni\minGlue-mdd.h">
<Filter>C++ Header Files\minIni</Filter>
</ClInclude>
<ClInclude Include="..\..\src\minIni\minGlue-stdio.h">
<Filter>C++ Header Files\minIni</Filter>
</ClInclude>
<ClInclude Include="..\..\src\minIni\minIni.h">
<Filter>C++ Header Files\minIni</Filter>
</ClInclude>
<ClInclude Include="..\..\src\SAAConfig.h">
<Filter>C++ Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\minIni\minGlue_stdio_tchar.h">
<Filter>C++ Header Files\minIni</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\SAAAmp.cpp">
<Filter>C++ Source Code</Filter>
</ClCompile>
<ClCompile Include="..\..\src\SAAEnv.cpp">
<Filter>C++ Source Code</Filter>
</ClCompile>
<ClCompile Include="..\..\src\SAAFreq.cpp">
<Filter>C++ Source Code</Filter>
</ClCompile>
<ClCompile Include="..\..\src\SAAImpl.cpp">
<Filter>C++ Source Code</Filter>
</ClCompile>
<ClCompile Include="..\..\src\SAANoise.cpp">
<Filter>C++ Source Code</Filter>
</ClCompile>
<ClCompile Include="..\..\src\SAASndC.cpp">
<Filter>C++ Source Code</Filter>
</ClCompile>
<ClCompile Include="..\..\src\SAADevice.cpp">
<Filter>C++ Source Code</Filter>
</ClCompile>
<ClCompile Include="..\..\src\minIni\minIni.c">
<Filter>C++ Source Code\minIni</Filter>
</ClCompile>
<ClCompile Include="..\..\src\SAAConfig.cpp">
<Filter>C++ Source Code</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\INSTALL" />
<None Include="..\..\LICENCE" />
<None Include="..\..\README" />
<None Include="..\..\src\SAAFreq.dat">
<Filter>Data</Filter>
</None>
<None Include="..\..\resources\SAASound.def">
<Filter>Resources</Filter>
</None>
<None Include="..\..\resources\SAASound.cfg">
<Filter>Resources</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="SAASound.rc">
<Filter>Resources</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LocalDebuggerCommand>$(TargetDir)\SimCoupe.exe</LocalDebuggerCommand>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerWorkingDirectory>$(TargetDir)</LocalDebuggerWorkingDirectory>
</PropertyGroup>
</Project>

View file

@ -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

42
extern/SAASound/resources/SAASound.cfg vendored Normal file
View file

@ -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

52
extern/SAASound/resources/SAASound.def vendored Normal file
View file

@ -0,0 +1,52 @@
; Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
;
; 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 <dave@beermex.com>
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

203
extern/SAASound/src/SAAAmp.cpp vendored Executable file
View file

@ -0,0 +1,203 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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;
}
}

44
extern/SAASound/src/SAAAmp.h vendored Executable file
View file

@ -0,0 +1,44 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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

113
extern/SAASound/src/SAAConfig.cpp vendored Normal file
View file

@ -0,0 +1,113 @@
// Part of SAASound copyright 2020 Dave Hooper <dave@beermex.com>
//
// 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 <codecvt>
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<std::codecvt_utf8<wchar_t>> 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

41
extern/SAASound/src/SAAConfig.h vendored Normal file
View file

@ -0,0 +1,41 @@
// Part of SAASound copyright 2020 Dave Hooper <dave@beermex.com>
//
// 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

391
extern/SAASound/src/SAADevice.cpp vendored Normal file
View file

@ -0,0 +1,391 @@
// Part of SAASound copyright 2020 Dave Hooper <dave@beermex.com>
//
// 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;
}

68
extern/SAASound/src/SAADevice.h vendored Normal file
View file

@ -0,0 +1,68 @@
// Part of SAASound copyright 2020 Dave Hooper <dave@beermex.com>
//
// 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

380
extern/SAASound/src/SAAEnv.cpp vendored Executable file
View file

@ -0,0 +1,380 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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();
}

51
extern/SAASound/src/SAAEnv.h vendored Executable file
View file

@ -0,0 +1,51 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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

280
extern/SAASound/src/SAAFreq.cpp vendored Executable file
View file

@ -0,0 +1,280 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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<<oversample;
m_nOversample = oversample;
}
#ifdef SAAFREQ_FIXED_CLOCKRATE
void CSAAFreq::_SetClockRate(int nClockRate)
{
// if SAAFREQ clock rate is hardcoded, then we don't support dynamically
// adjusting the SAA clock rate, so this is a no-op
}
#else
void CSAAFreq::_SetClockRate(int nClockRate)
{
// initialise the frequency table based on the SAA clockrate
// Each item in m_FreqTable corresponds to the frequency calculated by
// the standard formula (15625 << octave) / (511 - offset)
// then multiplied by 8192 (and represented as a long integer value).
// We are therefore using 12 bits (i.e. 2^12 = 4096) as fractional part.
// The reason we multiply by 8192, not 4096, is that we use this as a counter
// to toggle the oscillator state, so we need to count half-waves (i.e. twice
// the frequency)
//
// Finally, note that the standard formula corresponds to a 8MHz base clock
// so we rescale the final result by the ratio nClockRate/8000000
if (nClockRate != (int)m_nClockRate)
{
m_nClockRate = nClockRate;
int ix = 0;
for (int nOctave = 0; nOctave < 8; nOctave++)
for (int nOffset = 0; nOffset < 256; nOffset++)
m_FreqTable[ix++] = (unsigned long)((8192.0 * 15625.0 * double(1 << nOctave) * (double(nClockRate) / 8000000.0)) / (511.0 - double(nOffset)));
}
}
#endif
int CSAAFreq::Tick(void)
{
// set to the absolute level (0 or 1)
if (m_bSync)
return 1;
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)
{
// 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();
}
}

141
extern/SAASound/src/SAAFreq.dat vendored Executable file
View file

@ -0,0 +1,141 @@
/*
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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

72
extern/SAASound/src/SAAFreq.h vendored Executable file
View file

@ -0,0 +1,72 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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

484
extern/SAASound/src/SAAImpl.cpp vendored Normal file
View file

@ -0,0 +1,484 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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 << "<!ENVO!>";
}
else if (nReg==25)
{
m_dbgfile << "<!ENV1!>";
}
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
*/

75
extern/SAASound/src/SAAImpl.h vendored Executable file
View file

@ -0,0 +1,75 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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 <ios>
#include <iostream>
#include <fstream>
#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

180
extern/SAASound/src/SAANoise.cpp vendored Executable file
View file

@ -0,0 +1,180 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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<<oversample;
m_nOversample = oversample;
}
inline void CSAANoise::ChangeLevel(void)
{
/*
https://www.vogons.org/viewtopic.php?f=9&t=51695
SAA1099P noise generator as documented by Jepael
18-bit Galois LFSR
Feedback polynomial = x^18 + x^11 + x^1
Period = 2^18-1 = 262143 bits
Verified to match recorded noise from my SAA1099P
*/
if (m_nRand & 1)
{
m_nRand = (m_nRand >> 1) ^ 0x20400;
}
else
{
m_nRand >>= 1;
}
}

54
extern/SAASound/src/SAANoise.h vendored Executable file
View file

@ -0,0 +1,54 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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

95
extern/SAASound/src/SAASndC.cpp vendored Executable file
View file

@ -0,0 +1,95 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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);
}

102
extern/SAASound/src/SAASndC.h vendored Normal file
View file

@ -0,0 +1,102 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// **********
// * 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

13
extern/SAASound/src/SAASound.cpp vendored Executable file
View file

@ -0,0 +1,13 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// SAASound.cpp - dummy function
//
//////////////////////////////////////////////////////////////////////
#include <stdio.h>
// Provide something so the compiler doesn't optimise us out of existance
int SomeFunction ()
{
return 42;
}

129
extern/SAASound/src/SAASound.h vendored Normal file
View file

@ -0,0 +1,129 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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

14
extern/SAASound/src/config.h.in vendored Normal file
View file

@ -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@"

59
extern/SAASound/src/defns.h vendored Normal file
View file

@ -0,0 +1,59 @@
// Part of SAASound copyright 2020 Dave Hooper <dave@beermex.com>
//
// 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

View file

@ -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);
}

View file

@ -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 <stdio.h>
#include <unistd.h>
#include <sys/file.h>
#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)

View file

@ -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

View file

@ -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

View file

@ -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 <mem-ffs.h>
#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)

View file

@ -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 <string.h>
#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

View file

@ -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 <stdio.h>
#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)

34
extern/SAASound/src/minIni/minGlue.h vendored Normal file
View file

@ -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 <stdio.h>
#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)

View file

@ -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 <stdio.h>
#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)

862
extern/SAASound/src/minIni/minIni.c vendored Normal file
View file

@ -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 <assert.h>
#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, &quotes); /* 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, &quotes); /* 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 */

228
extern/SAASound/src/minIni/minIni.h vendored Normal file
View file

@ -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 <tchar.h>
#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 <ctype.h>
#include <string.h>
#include <stdlib.h>
#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 <string>
/* 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<int>(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 */

15
extern/SAASound/src/resource.h vendored Executable file
View file

@ -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

34
extern/SAASound/src/types.h vendored Executable file
View file

@ -0,0 +1,34 @@
// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
//
// 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

Binary file not shown.

21
extern/SAASound/tools/freqdat.py vendored Normal file
View file

@ -0,0 +1,21 @@
# Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
#
# 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), ',')

65
extern/SAASound/tools/levels.py vendored Normal file
View file

@ -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)

View file

@ -5,6 +5,12 @@
#include <queue>
#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 {
@ -49,6 +55,10 @@ class DivPlatformSAA1099: public DivDispatch {
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);
int dispatch(DivCommand c);