├── .github └── workflows │ ├── build_linux.yaml │ ├── build_macos.yaml │ └── build_windows.yaml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── README.md ├── ci ├── build.sh └── config_cmake.sh └── plugin ├── Resources └── logo.png └── Source ├── PluginEditor.cpp ├── PluginEditor.h ├── PluginProcessor.cpp ├── PluginProcessor.h └── sms ├── Blip_Buffer.cpp ├── Blip_Buffer.h ├── Blip_Synth.h ├── COPYING.TXT ├── Sms_Apu.cpp ├── Sms_Apu.h ├── Sms_Oscs.h ├── Stereo_Buffer.cpp ├── Stereo_Buffer.h ├── blargg_common.h ├── boost ├── array.hpp ├── config.hpp ├── cstdint.hpp ├── scoped_ptr.hpp └── static_assert.hpp ├── changes.txt ├── notes.txt └── readme.txt /.github/workflows/build_linux.yaml: -------------------------------------------------------------------------------- 1 | name: Build Linux 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - release 7 | jobs: 8 | build: 9 | name: Build Linux 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Fix up git URLs 13 | run: echo -e '[url "https://github.com/"]\n insteadOf = "git@github.com:"' >> ~/.gitconfig 14 | shell: bash 15 | - uses: actions/checkout@v1 16 | with: 17 | token: ${{ secrets.ACCESS_TOKEN }} 18 | submodules: true 19 | - name: "Run script" 20 | run: | 21 | export OS="linux" 22 | ./ci/build.sh 23 | shell: bash 24 | env: 25 | APIKEY: ${{ secrets.APIKEY }} 26 | -------------------------------------------------------------------------------- /.github/workflows/build_macos.yaml: -------------------------------------------------------------------------------- 1 | name: Build macOS 2 | on: [push] 3 | jobs: 4 | build: 5 | name: Build macOS 6 | runs-on: macos-latest 7 | steps: 8 | - name: Fix up git URLs 9 | run: echo -e '[url "https://github.com/"]\n insteadOf = "git@github.com:"' >> ~/.gitconfig 10 | shell: bash 11 | - uses: actions/checkout@v1 12 | with: 13 | token: ${{ secrets.ACCESS_TOKEN }} 14 | submodules: true 15 | - uses: maxim-lobanov/setup-xcode@v1 16 | with: 17 | xcode-version: latest-stable 18 | - name: "Run script" 19 | run: | 20 | export OS="mac" 21 | ./ci/build.sh 22 | shell: bash 23 | env: 24 | APPLICATION: ${{ secrets.APPLICATION }} 25 | INSTALLER: ${{ secrets.INSTALLER }} 26 | APPLE_PASS: ${{ secrets.APPLE_PASS }} 27 | APPLE_USER: ${{ secrets.APPLE_USER }} 28 | APIKEY: ${{ secrets.APIKEY }} 29 | -------------------------------------------------------------------------------- /.github/workflows/build_windows.yaml: -------------------------------------------------------------------------------- 1 | name: Build Windows 2 | on: [push] 3 | jobs: 4 | build: 5 | name: Build Windows 6 | runs-on: windows-latest 7 | steps: 8 | - name: Fix up git URLs 9 | run: echo -e '[url "https://github.com/"]\n insteadOf = "git@github.com:"' >> ~/.gitconfig 10 | shell: bash 11 | - uses: actions/checkout@v1 12 | with: 13 | token: ${{ secrets.ACCESS_TOKEN }} 14 | submodules: true 15 | - name: "Run script" 16 | run: | 17 | export OS="win" 18 | ./ci/build.sh 19 | shell: bash 20 | env: 21 | APIKEY: ${{ secrets.APIKEY }} 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | *.xcuserstate 35 | Debug 36 | Release 37 | Debug64 38 | Release64 39 | 40 | *.opendb 41 | *.db 42 | *.suo 43 | 44 | plugin/Builds/VisualStudio2017/x64 45 | plugin/Builds/VisualStudio2017/Win32 46 | plugin/Builds/VisualStudio2017/.vs 47 | 48 | xcuserdata 49 | Builds 50 | JuceLibraryCode 51 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "modules/dRowAudio"] 2 | path = modules/dRowAudio 3 | url = git@github.com:FigBug/drowaudio.git 4 | [submodule "modules/gin"] 5 | path = modules/gin 6 | url = git@github.com:FigBug/Gin.git 7 | [submodule "modules/juce"] 8 | path = modules/juce 9 | url = git@github.com:WeAreROLI/JUCE.git 10 | [submodule "modules/plugin_sdk"] 11 | path = modules/plugin_sdk 12 | url = https://github.com/TurnipHat/plugin_sdk.git 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.24.0 FATAL_ERROR) 2 | 3 | # 4 | # Set for each plugin 5 | # 6 | set (PLUGIN_NAME SN76489) 7 | set (PLUGIN_VERSION 1.1.0) 8 | set (BUNDLE_ID com.socalabs.SN76489) 9 | set (AU_ID SN76489AU) 10 | set (LV2_URI https://socalabs.com/sn76489/) 11 | set (PLUGIN_CODE Sn76) 12 | 13 | set (CMAKE_POLICY_DEFAULT_CMP0077 NEW) 14 | set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 15 | set (CMAKE_SUPPRESS_REGENERATION true) 16 | set (CMAKE_SKIP_INSTALL_RULES YES) 17 | set_property (GLOBAL PROPERTY DEBUG_CONFIGURATIONS "Debug") 18 | 19 | set (CMAKE_C_FLAGS_DEVELOPMENT ${CMAKE_C_FLAGS_RELEASE}) 20 | set (CMAKE_CXX_FLAGS_DEVELOPMENT ${CMAKE_CXX_FLAGS_RELEASE}) 21 | 22 | project (${PLUGIN_NAME} VERSION ${PLUGIN_VERSION} LANGUAGES CXX C HOMEPAGE_URL "https://socalabs.com/") 23 | 24 | include (CMakeDependentOption) 25 | 26 | set_property (DIRECTORY APPEND PROPERTY LABELS ${PLUGIN_NAME}) 27 | 28 | set_property (DIRECTORY APPEND PROPERTY LABELS SocaLabs) 29 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PLUGIN_NAME}_Standalone) 30 | 31 | set (CMAKE_OSX_DEPLOYMENT_TARGET 10.11) 32 | 33 | set (CMAKE_EXPORT_COMPILE_COMMANDS ON) 34 | set (CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION ON) 35 | set (CMAKE_CXX_STANDARD 20) 36 | set (CMAKE_CXX_STANDARD_REQUIRED ON) 37 | set (CMAKE_CXX_EXTENSIONS OFF) 38 | set (CMAKE_OBJCXX_STANDARD 20) 39 | set (CMAKE_OBJCXX_STANDARD_REQUIRED ON) 40 | set (CMAKE_CXX_VISIBILITY_PRESET hidden) 41 | set (CMAKE_VISIBILITY_INLINES_HIDDEN ON) 42 | set (CMAKE_MINSIZEREL_POSTFIX -rm) 43 | set (CMAKE_RELWITHDEBINFO_POSTFIX -rd) 44 | set (CMAKE_OPTIMIZE_DEPENDENCIES OFF) 45 | 46 | set (BUILD_SHARED_LIBS OFF) 47 | 48 | if(APPLE) 49 | set (CMAKE_OSX_ARCHITECTURES arm64 x86_64) 50 | endif() 51 | 52 | if (WIN32) 53 | set (FORMATS Standalone VST VST3 LV2) 54 | else() 55 | set (FORMATS Standalone VST VST3 AU LV2) 56 | endif() 57 | 58 | set_property (GLOBAL PROPERTY USE_FOLDERS YES) 59 | set_property (GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER utility) 60 | set_property (GLOBAL PROPERTY REPORT_UNDEFINED_PROPERTIES "${CMAKE_BINARY_DIR}/undefined_properties.log") 61 | set_property (GLOBAL PROPERTY JUCE_COPY_PLUGIN_AFTER_BUILD YES) 62 | 63 | set_property (DIRECTORY APPEND PROPERTY LABELS External) 64 | 65 | # JUCE 66 | 67 | set (JUCE_MODULES_ONLY OFF) 68 | set (JUCE_ENABLE_MODULE_SOURCE_GROUPS ON) 69 | set (JUCE_BUILD_EXTRAS OFF) 70 | set (JUCE_BUILD_EXAMPLES OFF) 71 | 72 | add_subdirectory (modules/juce) 73 | 74 | set_property (DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/modules/juce" APPEND PROPERTY LABELS JUCE) 75 | 76 | # 77 | 78 | # Gin modules 79 | 80 | foreach(module_name IN ITEMS gin gin_dsp gin_simd gin_graphics gin_gui gin_metadata gin_network gin_plugin gin_webp) 81 | juce_add_module ( 82 | "${CMAKE_CURRENT_LIST_DIR}/modules/gin/modules/${module_name}" 83 | ) 84 | 85 | set_property (TARGET "${module_name}" APPEND PROPERTY LABELS Gin) 86 | endforeach() 87 | 88 | # Binary Data 89 | 90 | juce_set_vst2_sdk_path (${CMAKE_SOURCE_DIR}/modules/plugin_sdk/vstsdk2.4) 91 | 92 | juce_add_plugin (${PLUGIN_NAME} 93 | PRODUCT_NAME ${PLUGIN_NAME} 94 | VERSION ${PLUGIN_VERSION} 95 | COMPANY_NAME SocaLabs 96 | COMPANY_WEBSITE "https://socalabs.com/" 97 | BUNDLE_ID ${BUNDLE_ID} 98 | FORMATS ${FORMATS} 99 | PLUGIN_MANUFACTURER_CODE Soca 100 | PLUGIN_CODE ${PLUGIN_CODE} 101 | IS_SYNTH ON 102 | NEEDS_MIDI_INPUT ON 103 | EDITOR_WANTS_KEYBOARD_FOCUS ON 104 | VST2_CATEGORY kPlugCategSynth 105 | VST3_CATEGORIES Instrument 106 | AU_MAIN_TYPE kAudioUnitType_MusicDevice 107 | AU_EXPORT_PREFIX ${AU_ID} 108 | AU_SANDBOX_SAFE FALSE 109 | LV2URI ${LV2_URI}) 110 | 111 | file (GLOB_RECURSE source_files CONFIGURE_DEPENDS 112 | ${CMAKE_CURRENT_SOURCE_DIR}/plugin/*.cpp 113 | ${CMAKE_CURRENT_SOURCE_DIR}/plugin/*.c 114 | ${CMAKE_CURRENT_SOURCE_DIR}/plugin/*.cc 115 | ${CMAKE_CURRENT_SOURCE_DIR}/plugin/*.h) 116 | 117 | target_sources (${PLUGIN_NAME} PRIVATE ${source_files}) 118 | source_group (TREE ${CMAKE_CURRENT_SOURCE_DIR}/plugin PREFIX Source FILES ${source_files}) 119 | 120 | target_link_libraries (${PLUGIN_NAME} PRIVATE 121 | gin 122 | gin_dsp 123 | gin_simd 124 | gin_graphics 125 | gin_gui 126 | gin_plugin 127 | 128 | juce::juce_audio_basics 129 | juce::juce_audio_devices 130 | juce::juce_audio_formats 131 | juce::juce_audio_plugin_client 132 | juce::juce_audio_processors 133 | juce::juce_audio_utils 134 | juce::juce_core 135 | juce::juce_cryptography 136 | juce::juce_data_structures 137 | juce::juce_events 138 | juce::juce_graphics 139 | juce::juce_gui_basics 140 | juce::juce_gui_extra 141 | 142 | juce::juce_recommended_config_flags 143 | ) 144 | 145 | target_include_directories (${PLUGIN_NAME} PRIVATE modules/fmt/include) 146 | target_include_directories (${PLUGIN_NAME} PRIVATE modules/ASIO/common) 147 | target_include_directories (${PLUGIN_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Source/Definitions) 148 | 149 | juce_generate_juce_header (${PLUGIN_NAME}) 150 | 151 | target_compile_definitions (${PLUGIN_NAME} PRIVATE 152 | JUCE_DISPLAY_SPLASH_SCREEN=0 153 | JUCE_COREGRAPHICS_DRAW_ASYNC=1 154 | JUCE_MODAL_LOOPS_PERMITTED=1 155 | JUCE_WEB_BROWSER=0 156 | JUCE_USE_FLAC=0 157 | JUCE_USE_CURL=1 158 | JUCE_USE_MP3AUDIOFORMAT=0 159 | JUCE_USE_LAME_AUDIO_FORMAT=0 160 | JUCE_USE_WINDOWS_MEDIA_FORMAT=0 161 | DONT_SET_USING_JUCE_NAMESPACE=1 162 | JucePlugin_PreferredChannelConfigurations={0,1} 163 | _CRT_SECURE_NO_WARNINGS 164 | ) 165 | 166 | if (APPLE) 167 | set_target_properties("juce_vst3_helper" PROPERTIES XCODE_ATTRIBUTE_CLANG_LINK_OBJC_RUNTIME NO) 168 | 169 | foreach(t ${FORMATS} "All" "") 170 | set(tgt ${CMAKE_PROJECT_NAME}) 171 | if (NOT t STREQUAL "") 172 | set(tgt ${tgt}_${t}) 173 | endif() 174 | if (TARGET ${tgt}) 175 | set_target_properties(${tgt} PROPERTIES 176 | XCODE_ATTRIBUTE_CLANG_LINK_OBJC_RUNTIME NO 177 | #XCODE_ATTRIBUTE_DEPLOYMENT_POSTPROCESSING[variant=Release] YES 178 | XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] "YES" 179 | ) 180 | if (NOT t STREQUAL "All") 181 | target_compile_options(${tgt} PRIVATE 182 | -Wall -Wstrict-aliasing -Wunused-parameter -Wconditional-uninitialized -Woverloaded-virtual -Wreorder -Wconstant-conversion -Wbool-conversion -Wextra-semi 183 | -Wunreachable-code -Winconsistent-missing-destructor-override -Wshift-sign-overflow -Wnullable-to-nonnull-conversion -Wuninitialized -Wno-missing-field-initializers 184 | -Wno-ignored-qualifiers -Wno-missing-braces -Wno-char-subscripts -Wno-unused-private-field -fno-aligned-allocation -Wunused-private-field -Wunreachable-code 185 | -Wenum-compare -Wshadow -Wfloat-conversion -Wshadow-uncaptured-local -Wshadow-field -Wsign-compare -Wdeprecated-this-capture -Wimplicit-float-conversion 186 | -ffast-math -fno-finite-math-only) 187 | endif() 188 | endif() 189 | endforeach() 190 | endif() 191 | 192 | if (WIN32) 193 | foreach(t ${FORMATS} "All" "") 194 | set(tgt ${CMAKE_PROJECT_NAME}) 195 | if (NOT t STREQUAL "") 196 | set(tgt ${tgt}_${t}) 197 | endif() 198 | if (TARGET ${tgt}) 199 | set_property(TARGET ${tgt} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /INCREMENTAL:NO") 200 | set_target_properties(${tgt} PROPERTIES LINK_FLAGS "/ignore:4099") 201 | endif() 202 | endforeach() 203 | endif() 204 | 205 | if(UNIX AND NOT APPLE) 206 | target_link_libraries (${PLUGIN_NAME} PRIVATE curl) 207 | endif() 208 | 209 | 210 | if(WIN32) 211 | set (dest "Program Files") 212 | else() 213 | set (dest "Applications") 214 | endif() 215 | 216 | install (TARGETS ${PLUGIN_NAME} DESTINATION "${dest}") 217 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmakeMinimumRequired": { 3 | "major": 3, 4 | "minor": 24, 5 | "patch": 0 6 | }, 7 | "version": 5, 8 | "include": [ 9 | "modules/gin/ci/toolchains/xcode.json", 10 | "modules/gin/ci/toolchains/vs.json", 11 | "modules/gin/ci/toolchains/gcc.json" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | (This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.) 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | {description} 474 | Copyright (C) {year} {fullname} 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | {signature of Ty Coon}, 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! 505 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SN76489 2 | 3 | VST / AU emulation of Sega Master System sound chip 4 | 5 | ![Build Windows](https://github.com/FigBug/SN76489/workflows/Build%20Windows/badge.svg "Build Windows") 6 | ![Build macOS](https://github.com/FigBug/SN76489/workflows/Build%20macOS/badge.svg "Build macOS") 7 | ![Build Linux](https://github.com/FigBug/SN76489/workflows/Build%20Linux/badge.svg "Build Linux") 8 | -------------------------------------------------------------------------------- /ci/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | PLUGIN="SN76489" 4 | 5 | # linux specific stiff 6 | if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then 7 | sudo apt-get update 8 | sudo apt-get install clang git ninja-build ladspa-sdk freeglut3-dev g++ libasound2-dev libcurl4-openssl-dev libfreetype6-dev libjack-jackd2-dev libx11-dev libxcomposite-dev libxcursor-dev libxinerama-dev libxrandr-dev mesa-common-dev webkit2gtk-4.0 juce-tools xvfb 9 | fi 10 | 11 | # mac specific stuff 12 | if [ "$(uname)" == "Darwin" ]; then 13 | # Create a temp keychain 14 | if [ -n "$GITHUB_ACTIONS" ]; then 15 | echo "Create a keychain" 16 | security create-keychain -p nr4aGPyz Keys.keychain 17 | 18 | echo $APPLICATION | base64 -D -o /tmp/Application.p12 19 | echo $INSTALLER | base64 -D -o /tmp/Installer.p12 20 | 21 | security import /tmp/Application.p12 -t agg -k Keys.keychain -P aym9PKWB -A -T /usr/bin/codesign 22 | security import /tmp/Installer.p12 -t agg -k Keys.keychain -P aym9PKWB -A -T /usr/bin/codesign 23 | 24 | security list-keychains -s Keys.keychain 25 | security default-keychain -s Keys.keychain 26 | security unlock-keychain -p nr4aGPyz Keys.keychain 27 | security set-keychain-settings -l -u -t 13600 Keys.keychain 28 | security set-key-partition-list -S apple-tool:,apple: -s -k nr4aGPyz Keys.keychain 29 | fi 30 | DEV_APP_ID="Developer ID Application: Roland Rabien (3FS7DJDG38)" 31 | DEV_INST_ID="Developer ID Installer: Roland Rabien (3FS7DJDG38)" 32 | fi 33 | 34 | ROOT=$(cd "$(dirname "$0")/.."; pwd) 35 | cd "$ROOT" 36 | echo "$ROOT" 37 | 38 | BRANCH=${GITHUB_REF##*/} 39 | echo "$BRANCH" 40 | 41 | cd "$ROOT/ci" 42 | rm -Rf bin 43 | mkdir bin 44 | 45 | # Build mac version 46 | if [ "$(uname)" == "Darwin" ]; then 47 | cd "$ROOT" 48 | cmake --preset xcode 49 | cmake --build --preset xcode --config Release 50 | 51 | cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/AU/$PLUGIN.component" "$ROOT/ci/bin" 52 | cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/VST/$PLUGIN.vst" "$ROOT/ci/bin" 53 | cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/VST3/$PLUGIN.vst3" "$ROOT/ci/bin" 54 | 55 | cd "$ROOT/ci/bin" 56 | codesign -s "$DEV_APP_ID" -v $PLUGIN.vst --options=runtime --timestamp --force 57 | codesign -s "$DEV_APP_ID" -v $PLUGIN.vst3 --options=runtime --timestamp --force 58 | codesign -s "$DEV_APP_ID" -v $PLUGIN.component --options=runtime --timestamp --force 59 | 60 | # Notarize 61 | cd "$ROOT/ci/bin" 62 | zip -r ${PLUGIN}_Mac.zip $PLUGIN.vst $PLUGIN.vst3 $PLUGIN.component 63 | 64 | if [[ -n "$APPLE_USER" ]]; then 65 | xcrun notarytool submit --verbose --apple-id "$APPLE_USER" --password "$APPLE_PASS" --team-id "3FS7DJDG38" --wait --timeout 30m ${PLUGIN}_Mac.zip 66 | fi 67 | 68 | rm ${PLUGIN}_Mac.zip 69 | xcrun stapler staple $PLUGIN.vst 70 | xcrun stapler staple $PLUGIN.vst3 71 | xcrun stapler staple $PLUGIN.component 72 | zip -r ${PLUGIN}_Mac.zip $PLUGIN.vst $PLUGIN.vst3 $PLUGIN.component 73 | 74 | if [ "$BRANCH" = "release" ]; then 75 | curl -F "files=@${PLUGIN}_Mac.zip" "https://socalabs.com/files/set.php?key=$APIKEY" 76 | fi 77 | fi 78 | 79 | # Build linux version 80 | if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then 81 | cd "$ROOT" 82 | 83 | cmake --preset ninja-gcc 84 | cmake --build --preset ninja-gcc --config Release 85 | 86 | cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/LV2/$PLUGIN.lv2" "$ROOT/ci/bin" 87 | cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/VST/lib$PLUGIN.so" "$ROOT/ci/bin/$PLUGIN.so" 88 | cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/VST3/$PLUGIN.vst3" "$ROOT/ci/bin" 89 | 90 | cd "$ROOT/ci/bin" 91 | 92 | # Upload 93 | cd "$ROOT/ci/bin" 94 | zip -r ${PLUGIN}_Linux.zip $PLUGIN.so $PLUGIN.vst3 $PLUGIN.lv2 95 | 96 | if [ "$BRANCH" = "release" ]; then 97 | curl -F "files=@${PLUGIN}_Linux.zip" "https://socalabs.com/files/set.php?key=$APIKEY" 98 | fi 99 | fi 100 | 101 | # Build Win version 102 | if [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then 103 | cd "$ROOT" 104 | 105 | cmake --preset vs 106 | cmake --build --preset vs --config Release 107 | 108 | cd "$ROOT/ci/bin" 109 | 110 | cp -R "$ROOT/Builds/vs/${PLUGIN}_artefacts/Release/VST/$PLUGIN.dll" "$ROOT/ci/bin" 111 | cp -R "$ROOT/Builds/vs/${PLUGIN}_artefacts/Release/VST3/$PLUGIN.vst3" "$ROOT/ci/bin" 112 | 113 | 7z a ${PLUGIN}_Win.zip $PLUGIN.dll $PLUGIN.vst3 114 | 115 | if [ "$BRANCH" = "release" ]; then 116 | curl -F "files=@${PLUGIN}_Win.zip" "https://socalabs.com/files/set.php?key=$APIKEY" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /ci/config_cmake.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | ROOT=$(cd "$(dirname "$0")/.."; pwd) 4 | cd "$ROOT" 5 | 6 | export PATH=$PATH:"/c/Program Files/CMake/bin" 7 | 8 | if [ "$(uname)" == "Darwin" ]; then 9 | TOOLCHAIN="xcode" 10 | elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then 11 | TOOLCHAIN="ninja-gcc" 12 | elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then 13 | TOOLCHAIN="vs" 14 | fi 15 | 16 | cmake --preset $TOOLCHAIN -D BUILD_EXTRAS=OFF -D JUCE_COPY_PLUGIN_AFTER_BUILD=ON 17 | -------------------------------------------------------------------------------- /plugin/Resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FigBug/SN76489/210e0c799727be07cb7f9559af40616e17f20302/plugin/Resources/logo.png -------------------------------------------------------------------------------- /plugin/Source/PluginEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "PluginProcessor.h" 2 | #include "PluginEditor.h" 3 | 4 | //============================================================================== 5 | SN76489AudioProcessorEditor::SN76489AudioProcessorEditor (SN76489AudioProcessor& p) 6 | : gin::ProcessorEditor (p), proc (p) 7 | { 8 | for (auto pp : p.getPluginParameters()) 9 | { 10 | auto c = pp->isOnOff() ? (gin::ParamComponent*)new gin::Switch (pp) : (gin::ParamComponent*)new gin::Knob (pp); 11 | 12 | addAndMakeVisible (c); 13 | controls.add (c); 14 | } 15 | 16 | addAndMakeVisible (&scope); 17 | 18 | setGridSize (6, 2); 19 | 20 | scope.setNumSamplesPerPixel (2); 21 | scope.setVerticalZoomFactor (3.0f); 22 | scope.setColour (gin::TriggeredScope::lineColourId, findColour (gin::PluginLookAndFeel::grey45ColourId)); 23 | scope.setColour (gin::TriggeredScope::traceColourId + 0, findColour (gin::PluginLookAndFeel::accentColourId)); 24 | scope.setColour (gin::TriggeredScope::envelopeColourId + 0, juce::Colours::transparentBlack); 25 | scope.setColour (gin::TriggeredScope::traceColourId + 1, findColour (gin::PluginLookAndFeel::accentColourId)); 26 | scope.setColour (gin::TriggeredScope::envelopeColourId + 1, juce::Colours::transparentBlack); 27 | } 28 | 29 | SN76489AudioProcessorEditor::~SN76489AudioProcessorEditor() 30 | { 31 | } 32 | 33 | //============================================================================== 34 | void SN76489AudioProcessorEditor::paint (juce::Graphics& g) 35 | { 36 | gin::ProcessorEditor::paint (g); 37 | } 38 | 39 | void SN76489AudioProcessorEditor::resized() 40 | { 41 | using AP = SN76489AudioProcessor; 42 | 43 | gin::ProcessorEditor::resized(); 44 | 45 | componentForId (AP::paramPulse1Level)->setBounds (getGridArea (0, 0)); 46 | componentForId (AP::paramPulse2Level)->setBounds (getGridArea (1, 0)); 47 | componentForId (AP::paramPulse3Level)->setBounds (getGridArea (0, 1)); 48 | componentForId (AP::paramNoiseLevel)->setBounds (getGridArea (1, 1)); 49 | componentForId (AP::paramNoiseWhite)->setBounds (getGridArea (5, 0)); 50 | componentForId (AP::paramNoiseShift)->setBounds (getGridArea (5, 1)); 51 | 52 | scope.setBounds (getGridArea (2, 0, 3, 2).reduced (5)); 53 | } 54 | -------------------------------------------------------------------------------- /plugin/Source/PluginEditor.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file was auto-generated by the Introjucer! 5 | 6 | It contains the basic framework code for a JUCE plugin editor. 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "../JuceLibraryCode/JuceHeader.h" 14 | #include "PluginProcessor.h" 15 | 16 | //============================================================================== 17 | /** 18 | */ 19 | class SN76489AudioProcessorEditor : public gin::ProcessorEditor 20 | { 21 | public: 22 | SN76489AudioProcessorEditor (SN76489AudioProcessor&); 23 | ~SN76489AudioProcessorEditor() override; 24 | 25 | //============================================================================== 26 | void resized() override; 27 | void paint (juce::Graphics& g) override; 28 | 29 | SN76489AudioProcessor& proc; 30 | 31 | gin::TriggeredScope scope {proc.fifo}; 32 | 33 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SN76489AudioProcessorEditor) 34 | }; 35 | -------------------------------------------------------------------------------- /plugin/Source/PluginProcessor.cpp: -------------------------------------------------------------------------------- 1 | #include "PluginProcessor.h" 2 | #include "PluginEditor.h" 3 | 4 | const char* SN76489AudioProcessor::paramPulse1Level = "pulse1Level"; 5 | const char* SN76489AudioProcessor::paramPulse2Level = "pulse2Level"; 6 | const char* SN76489AudioProcessor::paramPulse3Level = "pulse3Level"; 7 | const char* SN76489AudioProcessor::paramNoiseLevel = "noiseLevel"; 8 | const char* SN76489AudioProcessor::paramNoiseWhite = "noiseWhite"; 9 | const char* SN76489AudioProcessor::paramNoiseShift = "noiseShift"; 10 | 11 | //============================================================================== 12 | static juce::String percentTextFunction (const gin::Parameter& p, float v) 13 | { 14 | return juce::String::formatted ("%.0f%%", v / p.getUserRangeEnd() * 100); 15 | } 16 | 17 | static juce::String typeTextFunction (const gin::Parameter&, float v) 18 | { 19 | return v > 0.0f ? "White" : "Periodic"; 20 | } 21 | 22 | static juce::String speedTextFunction (const gin::Parameter&, float v) 23 | { 24 | switch (int (v)) 25 | { 26 | case 0: return "Fast"; 27 | case 1: return "Medium"; 28 | case 2: return "Slow"; 29 | case 3: return "Tone 2"; 30 | } 31 | return ""; 32 | } 33 | 34 | //============================================================================== 35 | SN76489AudioProcessor::SN76489AudioProcessor() 36 | : gin::Processor (false, gin::ProcessorOptions().withAdditionalCredits({"Shay Green"})) 37 | { 38 | addExtParam (paramPulse1Level, "Pulse 1 Level", "Pulse 1", "", { 0.0f, 1.0f, 0.0f, 1.0f }, 1.0f, 0.0f, percentTextFunction); 39 | addExtParam (paramPulse2Level, "Pulse 2 Level", "Pulse 2", "", { 0.0f, 1.0f, 0.0f, 1.0f }, 0.0f, 0.0f, percentTextFunction); 40 | addExtParam (paramPulse3Level, "Pulse 3 Level", "Pulse 3", "", { 0.0f, 1.0f, 0.0f, 1.0f }, 0.0f, 0.0f, percentTextFunction); 41 | addExtParam (paramNoiseLevel, "Noise Level", "Noise", "", { 0.0f, 1.0f, 0.0f, 1.0f }, 0.0f, 0.0f, percentTextFunction); 42 | addExtParam (paramNoiseWhite, "Noise Type", "Type", "", { 0.0f, 1.0f, 1.0f, 1.0f }, 0.0f, 0.0f, typeTextFunction); 43 | addExtParam (paramNoiseShift, "Noise Speed", "Speed", "", { 0.0f, 3.0f, 1.0f, 1.0f }, 0.0f, 0.0f, speedTextFunction); 44 | 45 | init(); 46 | } 47 | 48 | SN76489AudioProcessor::~SN76489AudioProcessor() 49 | { 50 | } 51 | 52 | //============================================================================== 53 | void SN76489AudioProcessor::prepareToPlay (double sampleRate, int) 54 | { 55 | outputSmoothed.reset (sampleRate, 0.05); 56 | 57 | buf.sample_rate (long (sampleRate)); 58 | buf.clock_rate (clocks_per_sec); 59 | 60 | apu.output (buf.center(), buf.left(), buf.right()); 61 | } 62 | 63 | void SN76489AudioProcessor::releaseResources() 64 | { 65 | } 66 | 67 | void SN76489AudioProcessor::runUntil (int& done, juce::AudioSampleBuffer& buffer, int pos) 68 | { 69 | int todo = std::min (pos, buffer.getNumSamples()) - done; 70 | 71 | while (todo > 0) 72 | { 73 | if (buf.samples_avail() > 0) 74 | { 75 | blip_sample_t out[1024]; 76 | 77 | int count = int (buf.read_samples (out, (size_t) std::min ({todo, 1024 / 2, (int) buf.samples_avail()}))); 78 | 79 | auto data = buffer.getWritePointer (0, done); 80 | for (int i = 0; i < count; i++) 81 | data[i] = (out[i * 2] + out[i * 2 + 1]) / 2.0f / 32768.0f; 82 | 83 | done += count; 84 | todo -= count; 85 | } 86 | else 87 | { 88 | apu.end_frame (1024); 89 | buf.end_frame (1024); 90 | } 91 | } 92 | } 93 | 94 | void SN76489AudioProcessor::processBlock (juce::AudioSampleBuffer& buffer, juce::MidiBuffer& midi) 95 | { 96 | buffer.clear(); 97 | 98 | const float p1Level = getParameter (paramPulse1Level)->getUserValue(); 99 | const float p2Level = getParameter (paramPulse2Level)->getUserValue(); 100 | const float p3Level = getParameter (paramPulse3Level)->getUserValue(); 101 | const float nLevel = getParameter (paramNoiseLevel)->getUserValue(); 102 | const bool nWhite = getParameter (paramNoiseWhite)->getUserValue() > 0.0f; 103 | const int nType = getParameter (paramNoiseShift)->getUserValueInt(); 104 | 105 | int done = 0; 106 | runUntil (done, buffer, 0); 107 | 108 | for (auto itr : midi) 109 | { 110 | auto msg = itr.getMessage(); 111 | int pos = itr.samplePosition; 112 | 113 | runUntil (done, buffer, pos); 114 | 115 | if (msg.isNoteOn()) 116 | { 117 | noteQueue.add (msg.getNoteNumber()); 118 | velocity = msg.getVelocity(); 119 | 120 | for (int i = 0; i < 3; i++) 121 | { 122 | if (channelInfo[i].note == -1) 123 | { 124 | channelInfo[i].note = msg.getNoteNumber(); 125 | channelInfo[i].velocity = msg.getVelocity(); 126 | channelInfo[i].dirty = true; 127 | break; 128 | } 129 | } 130 | } 131 | else if (msg.isNoteOff()) 132 | { 133 | noteQueue.removeFirstMatchingValue (msg.getNoteNumber()); 134 | 135 | for (int i = 0; i < 3; i++) 136 | { 137 | if (channelInfo[i].note == msg.getNoteNumber()) 138 | { 139 | channelInfo[i].note = -1; 140 | channelInfo[i].velocity = 0; 141 | channelInfo[i].dirty = true; 142 | } 143 | } 144 | } 145 | else if (msg.isAllNotesOff()) 146 | { 147 | noteQueue.clear(); 148 | 149 | for (int i = 0; i < 3; i++) 150 | { 151 | if (channelInfo[i].note != -1) 152 | { 153 | channelInfo[i].note = -1; 154 | channelInfo[i].velocity = 0; 155 | channelInfo[i].dirty = true; 156 | } 157 | } 158 | } 159 | 160 | blip_time_t time = 0; 161 | 162 | if (channelInfo[0].dirty) 163 | { 164 | int v = channelInfo[0].velocity; 165 | int curNote = channelInfo[0].note; 166 | 167 | // Tone 1 168 | apu.write_data (time, 0x80 | (0 << 5) | (1 << 4) | 0xF - int (p1Level * v / 127.0 * 0xF)); 169 | 170 | if (curNote != -1) 171 | { 172 | int period = int (3579545.0 / (juce::MidiMessage::getMidiNoteInHertz (curNote) * 2 * 16)); 173 | period = juce::jlimit (1, 0x3ff, period); 174 | 175 | apu.write_data (time, 0x80 | (0 << 5) | (0 << 4) | (period & 0xF)); 176 | apu.write_data (time, period >> 4); 177 | } 178 | } 179 | 180 | if (channelInfo[1].dirty) 181 | { 182 | int v = channelInfo[1].velocity; 183 | int curNote = channelInfo[1].note; 184 | 185 | // Tone 2 186 | apu.write_data (time, 0x80 | (1 << 5) | (1 << 4) | 0xF - int (p2Level * v / 127.0 * 0xF)); 187 | 188 | if (curNote != -1) 189 | { 190 | int period = int (3579545.0 / (juce::MidiMessage::getMidiNoteInHertz (curNote) * 2 * 16)); 191 | period = juce::jlimit (1, 0x3ff, period); 192 | 193 | apu.write_data (time, 0x80 | (1 << 5) | (0 << 4) | (period & 0xF)); 194 | apu.write_data (time, period >> 4); 195 | } 196 | } 197 | 198 | if (channelInfo[2].dirty) 199 | { 200 | int v = channelInfo[2].velocity; 201 | int curNote = channelInfo[2].note; 202 | 203 | // Tone 3 204 | apu.write_data (time, 0x80 | (2 << 5) | (1 << 4) | 0xF - int (p3Level * v / 127.0 * 0xF)); 205 | 206 | if (curNote != -1) 207 | { 208 | int period = int (3579545.0 / (juce::MidiMessage::getMidiNoteInHertz (curNote) * 2 * 16)); 209 | period = juce::jlimit (1, 0x3ff, period); 210 | 211 | apu.write_data (time, 0x80 | (2 << 5) | (0 << 4) | (period & 0xF)); 212 | apu.write_data (time, period >> 4); 213 | } 214 | } 215 | 216 | const int curNote = noteQueue.size() > 0 ? noteQueue.getFirst() : -1; 217 | 218 | if (curNote != lastNote) 219 | { 220 | int v = curNote == -1 ? 0 : velocity; 221 | 222 | // Noise 223 | apu.write_data (time, 0x80 | (3 << 5) | (1 << 4) | 0xF - int (nLevel * v / 127.0 * 0xF)); 224 | 225 | if (curNote != -1) 226 | { 227 | apu.write_data (time, 0x80 | (3 << 5) | (0 << 4) | ((nWhite ? 1 : 0) << 2) | nType); 228 | } 229 | 230 | lastNote = curNote; 231 | } 232 | } 233 | 234 | int numSamples = buffer.getNumSamples(); 235 | runUntil (done, buffer, numSamples); 236 | 237 | if (fifo.getFreeSpace() >= numSamples) 238 | { 239 | auto data = buffer.getReadPointer (0); 240 | fifo.writeMono (data, numSamples); 241 | } 242 | } 243 | 244 | //============================================================================== 245 | bool SN76489AudioProcessor::hasEditor() const 246 | { 247 | return true; 248 | } 249 | 250 | juce::AudioProcessorEditor* SN76489AudioProcessor::createEditor() 251 | { 252 | return new SN76489AudioProcessorEditor (*this); 253 | } 254 | 255 | //============================================================================== 256 | // This creates new instances of the plugin.. 257 | juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() 258 | { 259 | return new SN76489AudioProcessor(); 260 | } 261 | -------------------------------------------------------------------------------- /plugin/Source/PluginProcessor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "sms/Sms_Apu.h" 5 | #include "sms/Stereo_Buffer.h" 6 | 7 | //============================================================================== 8 | /** 9 | */ 10 | class SN76489AudioProcessorEditor; 11 | class SN76489AudioProcessor : public gin::Processor 12 | { 13 | public: 14 | //============================================================================== 15 | SN76489AudioProcessor(); 16 | ~SN76489AudioProcessor() override; 17 | 18 | //============================================================================== 19 | void prepareToPlay (double sampleRate, int samplesPerBlock) override; 20 | void releaseResources() override; 21 | 22 | void processBlock (juce::AudioSampleBuffer&, juce::MidiBuffer&) override; 23 | 24 | //============================================================================== 25 | juce::AudioProcessorEditor* createEditor() override; 26 | bool hasEditor() const override; 27 | 28 | //============================================================================== 29 | 30 | static const char* paramPulse1Level; 31 | static const char* paramPulse2Level; 32 | static const char* paramPulse3Level; 33 | static const char* paramNoiseLevel; 34 | static const char* paramNoiseWhite; 35 | static const char* paramNoiseShift; 36 | 37 | gin::AudioFifo fifo {1, 44100}; 38 | 39 | private: 40 | void runUntil (int& done, juce::AudioSampleBuffer& buffer, int pos); 41 | 42 | int lastNote = -1; 43 | int velocity = 0; 44 | juce::Array noteQueue; 45 | 46 | juce::LinearSmoothedValue outputSmoothed; 47 | 48 | Sms_Apu apu; 49 | Stereo_Buffer buf; 50 | const long clocks_per_sec = 3579545; 51 | 52 | struct ChannelInfo 53 | { 54 | int note = -1; 55 | int velocity = 0; 56 | bool dirty = false; 57 | }; 58 | 59 | ChannelInfo channelInfo[3]; 60 | 61 | //============================================================================== 62 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SN76489AudioProcessor) 63 | }; 64 | -------------------------------------------------------------------------------- /plugin/Source/sms/Blip_Buffer.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Blip_Buffer 0.3.0. http://www.slack.net/~ant/nes-emu/ 3 | 4 | #include "Blip_Buffer.h" 5 | 6 | #include 7 | #include 8 | 9 | /* Library Copyright (C) 2003-2004 Shay Green. Blip_Buffer is free 10 | software; you can redistribute it and/or modify it under the terms of the 11 | GNU General Public License as published by the Free Software Foundation; 12 | either version 2 of the License, or (at your option) any later version. 13 | Blip_Buffer is distributed in the hope that it will be useful, but WITHOUT 14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 | more details. You should have received a copy of the GNU General Public 17 | License along with Blip_Buffer; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 19 | 20 | Blip_Buffer::Blip_Buffer() 21 | { 22 | samples_per_sec = 44100; 23 | buffer_ = NULL; 24 | 25 | // try to cause assertion failure if buffer is used before these are set 26 | clocks_per_sec = 0; 27 | factor_ = -1; 28 | offset_ = 0; 29 | buffer_size_ = 0; 30 | 31 | bass_freq_ = 16; 32 | } 33 | 34 | void Blip_Buffer::clear() 35 | { 36 | offset_ = 0; 37 | reader_accum = 0; 38 | memset( buffer_, sample_offset & 0xFF, (buffer_size_ + widest_impulse_) * sizeof (buf_t_) ); 39 | } 40 | 41 | bool Blip_Buffer::sample_rate( long sps, int msec ) 42 | { 43 | samples_per_sec = sps; 44 | if ( clocks_per_sec ) 45 | clock_rate( clocks_per_sec ); // recalculate factor 46 | bass_freq( bass_freq_ ); // recalculate shift 47 | 48 | const size_t max_size_adj = widest_impulse_ + 64; 49 | const size_t max_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) + 1 - max_size_adj; 50 | 51 | // add 2 milliseconds to account for any roundoff error in user code. 52 | size_t new_size = msec ? samples_per_sec * (msec + 2) / 1000 : 65536 - max_size_adj; 53 | 54 | assert(( "Blip_Buffer::sample_rate(): Buffer length exceeds limit", 55 | new_size <= max_size )); 56 | if ( new_size > max_size ) 57 | new_size = max_size; 58 | 59 | if ( buffer_size_ != new_size ) 60 | { 61 | delete [] buffer_; 62 | buffer_ = NULL; 63 | buffer_size_ = 0; 64 | offset_ = 0; 65 | 66 | buffer_ = new buf_t_ [new_size + widest_impulse_]; 67 | if ( !buffer_ ) 68 | return false; 69 | 70 | buffer_size_ = unsigned (new_size); 71 | } 72 | 73 | clear(); 74 | 75 | return true; 76 | } 77 | 78 | void Blip_Buffer::clock_rate( long cps ) { 79 | clocks_per_sec = cps; 80 | factor_ = floor( (double) samples_per_sec / cps * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); 81 | assert(( "Blip_Buffer::clock_rate(): Clock rate to sample rate ratio exceeded 65536", factor_ > 0 )); 82 | } 83 | 84 | Blip_Buffer::~Blip_Buffer() { 85 | delete [] buffer_; 86 | } 87 | 88 | void Blip_Buffer::bass_freq( int freq ) 89 | { 90 | bass_freq_ = freq; 91 | if ( freq == 0 ) { 92 | bass_shift = 32; 93 | return; 94 | } 95 | bass_shift = 1 + (int) floor( 1.442695041 * log( 0.124 * samples_per_sec / freq ) ); 96 | if ( bass_shift < 0 ) 97 | bass_shift = 0; 98 | if ( bass_shift > 24 ) 99 | bass_shift = 24; 100 | } 101 | 102 | size_t Blip_Buffer::read_samples( blip_sample_t* out, size_t max_samples, bool stereo ) 103 | { 104 | assert(( "Blip_Buffer::read_samples(): Buffer sample rate not set", buffer_ )); 105 | 106 | size_t count = samples_avail(); 107 | if ( count > max_samples ) 108 | count = max_samples; 109 | 110 | if ( !count ) 111 | return 0; // optimization 112 | 113 | int sample_offset = this->sample_offset; 114 | int bass_shift = this->bass_shift; 115 | buf_t_* buf = buffer_; 116 | long accum = reader_accum; 117 | 118 | if ( !stereo ) { 119 | for ( unsigned n = unsigned (count); n--; ) { 120 | *out++ = accum >> accum_fract; 121 | accum -= accum >> bass_shift; 122 | accum += (long (*buf++) - sample_offset) << accum_fract; 123 | } 124 | } 125 | else { 126 | for ( unsigned n = unsigned (count); n--; ) { 127 | *out = accum >> accum_fract; 128 | out += 2; 129 | accum -= accum >> bass_shift; 130 | accum += (long (*buf++) - sample_offset) << accum_fract; 131 | } 132 | } 133 | 134 | reader_accum = accum; 135 | 136 | remove_samples( count ); 137 | 138 | return count; 139 | } 140 | 141 | void Blip_Buffer::remove_samples( size_t count ) 142 | { 143 | assert( buffer_ ); 144 | 145 | if ( !count ) // optimization 146 | return; 147 | 148 | remove_silence( count ); 149 | 150 | // copy remaining samples to beginning and clear old samples 151 | size_t remain = samples_avail() + widest_impulse_; 152 | if ( count >= remain ) 153 | memmove( buffer_, buffer_ + count, remain * sizeof (buf_t_) ); 154 | else 155 | memcpy( buffer_, buffer_ + count, remain * sizeof (buf_t_) ); 156 | memset( buffer_ + remain, sample_offset & 0xFF, count * sizeof (buf_t_) ); 157 | } 158 | 159 | size_t Blip_Buffer::count_samples( blip_time_t t ) const { 160 | return (resampled_time( t ) >> BLIP_BUFFER_ACCURACY) - (offset_ >> BLIP_BUFFER_ACCURACY); 161 | } 162 | 163 | void Blip_Buffer::mix_samples( const blip_sample_t* in, size_t count ) 164 | { 165 | buf_t_* buf = &buffer_ [(offset_ >> BLIP_BUFFER_ACCURACY) + (widest_impulse_ / 2 - 1)]; 166 | 167 | int prev = 0; 168 | while ( count-- ) { 169 | int s = *in++; 170 | *buf += s - prev; 171 | prev = s; 172 | ++buf; 173 | } 174 | *buf -= *--in; 175 | } 176 | 177 | void Blip_Impulse_::init( blip_pair_t_* imps, int w, int r, int fb ) 178 | { 179 | fine_bits = fb; 180 | width = w; 181 | impulses = (imp_t*) imps; 182 | generate = true; 183 | volume_unit_ = -1.0; 184 | res = r; 185 | buf = NULL; 186 | 187 | impulse = &impulses [width * res * 2 * (fine_bits ? 2 : 1)]; 188 | offset = 0; 189 | } 190 | 191 | const int impulse_bits = 15; 192 | const long impulse_amp = 1L << impulse_bits; 193 | const long impulse_offset = impulse_amp / 2; 194 | 195 | void Blip_Impulse_::scale_impulse( int unit, imp_t* imp_in ) const 196 | { 197 | long offset = ((long) unit << impulse_bits) - impulse_offset * unit + 198 | (1 << (impulse_bits - 1)); 199 | imp_t* imp = imp_in; 200 | imp_t* fimp = impulse; 201 | for ( int n = res / 2 + 1; n--; ) 202 | { 203 | long error = unit; 204 | for ( int nn = width; nn--; ) 205 | { 206 | long a = ((long) *fimp++ * unit + offset) >> impulse_bits; 207 | error -= a - unit; 208 | *imp++ = (imp_t) a; 209 | } 210 | 211 | // add error to middle 212 | imp [-width / 2 - 1] += error; 213 | } 214 | 215 | if ( res > 2 ) { 216 | // second half is mirror-image 217 | const imp_t* rev = imp - width - 1; 218 | for ( int nn = (res / 2 - 1) * width - 1; nn--; ) 219 | *imp++ = *--rev; 220 | *imp++ = unit; 221 | } 222 | 223 | // copy to odd offset 224 | *imp++ = unit; 225 | memcpy( imp, imp_in, (res * width - 1) * sizeof *imp ); 226 | } 227 | 228 | const int max_res = 1 << blip_res_bits_; 229 | 230 | void Blip_Impulse_::fine_volume_unit() 231 | { 232 | // to do: find way of merging in-place without temporary buffer 233 | 234 | imp_t temp [max_res * 2 * Blip_Buffer::widest_impulse_]; 235 | scale_impulse( (offset & 0xffff) << fine_bits, temp ); 236 | imp_t* imp2 = impulses + res * 2 * width; 237 | scale_impulse( offset & 0xffff, imp2 ); 238 | 239 | // merge impulses 240 | imp_t* imp = impulses; 241 | imp_t* src2 = temp; 242 | for ( int n = res / 2 * 2 * width; n--; ) { 243 | *imp++ = *imp2++; 244 | *imp++ = *imp2++; 245 | *imp++ = *src2++; 246 | *imp++ = *src2++; 247 | } 248 | } 249 | 250 | void Blip_Impulse_::volume_unit( double new_unit ) 251 | { 252 | if ( new_unit == volume_unit_ ) 253 | return; 254 | 255 | if ( generate ) 256 | treble_eq( blip_eq_t( -8.87, 8800, 44100 ) ); 257 | 258 | volume_unit_ = new_unit; 259 | 260 | offset = 0x10001 * floor( volume_unit_ * 0x10000 + 0.5 ); 261 | 262 | if ( fine_bits ) 263 | fine_volume_unit(); 264 | else 265 | scale_impulse( offset & 0xffff, impulses ); 266 | } 267 | 268 | const double pi = 3.1415926535897932384626433832795029L; 269 | 270 | void Blip_Impulse_::treble_eq( const blip_eq_t& new_eq ) 271 | { 272 | if ( !generate && new_eq.treble == eq.treble && new_eq.cutoff == eq.cutoff && 273 | new_eq.sample_rate == eq.sample_rate ) 274 | return; // already calculated with same parameters 275 | 276 | generate = false; 277 | eq = new_eq; 278 | 279 | double treble = pow( 10, 1.0 / 20 * eq.treble ); // dB (-6dB = 0.50) 280 | if ( treble < 0.000005 ) 281 | treble = 0.000005; 282 | 283 | const double treble_freq = 22050.0; // treble level at 22 kHz harmonic 284 | const double sample_rate = eq.sample_rate; 285 | const double pt = treble_freq * 2 / sample_rate; 286 | double cutoff = eq.cutoff * 2 / sample_rate; 287 | if ( cutoff >= pt * 0.95 || cutoff >= 0.95 ) { 288 | cutoff = 0.5; 289 | treble = 1.0; 290 | } 291 | 292 | // DSF Synthesis (See T. Stilson & J. Smith (1996), 293 | // Alias-free digital synthesis of classic analog waveforms) 294 | 295 | // reduce adjacent impulse interference by using small part of wide impulse 296 | const double n_harm = 4096; 297 | const double rolloff = pow( treble, 1.0 / (n_harm * pt - n_harm * cutoff) ); 298 | const double rescale = 1.0 / pow( rolloff, n_harm * cutoff ); 299 | 300 | const double pow_a_n = rescale * pow( rolloff, n_harm ); 301 | const double pow_a_nc = rescale * pow( rolloff, n_harm * cutoff ); 302 | 303 | double total = 0.0; 304 | const double to_angle = pi / 2 / n_harm / max_res; 305 | 306 | float buf [max_res * (Blip_Buffer::widest_impulse_ - 2) / 2]; 307 | const int size = max_res * (width - 2) / 2; 308 | for ( int i = size; i--; ) 309 | { 310 | double angle = (i * 2 + 1) * to_angle; 311 | 312 | // equivalent 313 | //double y = dsf( angle, n_harm * cutoff, 1.0 ); 314 | //y -= rescale * dsf( angle, n_harm * cutoff, rolloff ); 315 | //y += rescale * dsf( angle, n_harm, rolloff ); 316 | 317 | const double cos_angle = cos( angle ); 318 | const double cos_nc_angle = cos( n_harm * cutoff * angle ); 319 | const double cos_nc1_angle = cos( (n_harm * cutoff - 1.0) * angle ); 320 | 321 | double b = 2.0 - 2.0 * cos_angle; 322 | double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; 323 | 324 | double d = 1.0 + rolloff * (rolloff - 2.0 * cos_angle); 325 | double c = pow_a_n * rolloff * cos( (n_harm - 1.0) * angle ) - 326 | pow_a_n * cos( n_harm * angle ) - 327 | pow_a_nc * rolloff * cos_nc1_angle + 328 | pow_a_nc * cos_nc_angle; 329 | 330 | // optimization of a / b + c / d 331 | double y = (a * d + c * b) / (b * d); 332 | 333 | // fixed window which affects wider impulses more 334 | if ( width > 12 ) { 335 | double window = cos( n_harm / 1.25 / Blip_Buffer::widest_impulse_ * angle ); 336 | y *= window * window; 337 | } 338 | 339 | total += (float) y; 340 | buf [i] = (float) y; 341 | } 342 | 343 | // integrate runs of length 'max_res' 344 | double factor = impulse_amp * 0.5 / total; // 0.5 accounts for other mirrored half 345 | imp_t* imp = impulse; 346 | const int step = max_res / res; 347 | int offset = res > 1 ? max_res : max_res / 2; 348 | for ( int n = res / 2 + 1; n--; offset -= step ) 349 | { 350 | for ( int w = -width / 2; w < width / 2; w++ ) 351 | { 352 | double sum = 0; 353 | for ( int i = max_res; i--; ) 354 | { 355 | int index = w * max_res + offset + i; 356 | if ( index < 0 ) 357 | index = -index - 1; 358 | if ( index < size ) 359 | sum += buf [index]; 360 | } 361 | *imp++ = (int) floor( sum * factor + (impulse_offset + 0.5) ); 362 | } 363 | } 364 | 365 | // rescale 366 | double unit = volume_unit_; 367 | if ( unit >= 0 ) { 368 | volume_unit_ = -1; 369 | volume_unit( unit ); 370 | } 371 | } 372 | 373 | -------------------------------------------------------------------------------- /plugin/Source/sms/Blip_Buffer.h: -------------------------------------------------------------------------------- 1 | 2 | // Buffer of sound samples into which band-limited waveforms can be synthesized 3 | // using Blip_Wave or Blip_Synth. 4 | 5 | // Blip_Buffer 0.3.0. Copyright (C) 2003-2004 Shay Green. GNU GPL license. 6 | 7 | #ifndef BLIP_BUFFER_H 8 | #define BLIP_BUFFER_H 9 | 10 | #include "blargg_common.h" 11 | 12 | #pragma clang diagnostic ignored "-Wunused-value" 13 | 14 | class Blip_Reader; 15 | 16 | // Source time unit. 17 | typedef long blip_time_t; 18 | 19 | // Type of sample produced. Signed 16-bit format. 20 | typedef BOOST::int16_t blip_sample_t; 21 | 22 | class Blip_Buffer { 23 | public: 24 | // Construct an empty buffer. 25 | Blip_Buffer(); 26 | ~Blip_Buffer(); 27 | 28 | // Set output sample rate and buffer length in milliseconds (1/1000 sec), 29 | // then clear buffer. If length not specified, make as large as possible. 30 | // If there is insufficient memory for the buffer, set the buffer length 31 | // to 0 and return false (or propagate exception if compiler supports it). 32 | bool sample_rate( long samples_per_sec ); 33 | bool sample_rate( long samples_per_sec, int msec_length ); 34 | 35 | // Current output sample rate 36 | long sample_rate() const; 37 | 38 | // Number of source time units per second. 39 | void clock_rate( long ); 40 | long clock_rate() const; 41 | 42 | // Set frequency at which high-pass filter attenuation passes -3dB. 43 | void bass_freq( int frequency ); 44 | 45 | // Remove any available samples and clear buffer to silence. 46 | void clear(); 47 | 48 | 49 | // Number of raw samples that can be mixed within frame of specified duration 50 | size_t count_samples( blip_time_t duration ) const; 51 | 52 | // Mix 'count' samples from 'buf' into buffer. 53 | void mix_samples( const blip_sample_t* buf, size_t count ); 54 | 55 | 56 | // End current time frame of specified duration and make its samples available 57 | // (along with any still-unread samples) for reading with read_samples(). Begin 58 | // a new time frame at the end of the current frame. All transitions must have 59 | // been added before the end of the current frame. 60 | void end_frame( blip_time_t ); 61 | 62 | // Number of samples available for reading with read_samples(). 63 | size_t samples_avail() const; 64 | 65 | // Read at most 'max_samples' out of buffer into 'dest', removing them from from 66 | // the buffer. Return number of samples actually read and removed. If stereo is 67 | // true, increment 'dest' one extra time after writing each sample, to allow 68 | // easy interleving of two channels into a stereo output buffer. 69 | size_t read_samples( blip_sample_t* dest, size_t max_samples, bool stereo = false ); 70 | 71 | // Remove 'count' samples from those waiting to be read. 72 | void remove_samples( size_t count ); 73 | 74 | // Number of samples delay from synthesis to samples read out. 75 | int output_latency() const; 76 | 77 | // not documented yet 78 | 79 | void remove_silence( size_t count ); 80 | 81 | typedef unsigned long resampled_time_t; 82 | 83 | resampled_time_t resampled_time( blip_time_t t ) const { 84 | return t * resampled_time_t (factor_) + offset_; 85 | } 86 | 87 | resampled_time_t resampled_duration( int t ) const { 88 | return t * resampled_time_t (factor_); 89 | } 90 | 91 | private: 92 | // noncopyable 93 | Blip_Buffer( const Blip_Buffer& ); 94 | Blip_Buffer& operator = ( const Blip_Buffer& ); 95 | 96 | // Don't use the following members. They are public only for technical reasons. 97 | public: 98 | enum { widest_impulse_ = 24 }; 99 | typedef BOOST::uint16_t buf_t_; 100 | 101 | unsigned long factor_; 102 | resampled_time_t offset_; 103 | buf_t_* buffer_; 104 | unsigned buffer_size_; 105 | private: 106 | long reader_accum; 107 | int bass_shift; 108 | long samples_per_sec; 109 | long clocks_per_sec; 110 | int bass_freq_; 111 | 112 | enum { accum_fract = 15 }; // less than 16 to give extra sample range 113 | enum { sample_offset = 0x7F7F }; // repeated byte allows memset to clear buffer 114 | 115 | friend class Blip_Reader; 116 | }; 117 | 118 | // Low-pass equalization parameters (see notes.txt) 119 | class blip_eq_t { 120 | public: 121 | blip_eq_t( double treble = 0 ); 122 | blip_eq_t( double treble, long cutoff, long sample_rate ); 123 | private: 124 | double treble; 125 | long cutoff; 126 | long sample_rate; 127 | friend class Blip_Impulse_; 128 | }; 129 | 130 | // not documented yet (see Stereo_Buffer.cpp for an example of its use) 131 | class Blip_Reader { 132 | const Blip_Buffer::buf_t_* buf; 133 | long accum; 134 | #ifdef __MWERKS__ 135 | void operator = ( struct foobar ); // helps optimizer 136 | #endif 137 | public: 138 | // avoid anything which might cause optimizer to put object in memory 139 | 140 | int begin( Blip_Buffer& blip_buf ) { 141 | buf = blip_buf.buffer_; 142 | accum = blip_buf.reader_accum; 143 | return blip_buf.bass_shift; 144 | } 145 | 146 | void end( Blip_Buffer& blip_buf ) { 147 | blip_buf.reader_accum = accum; 148 | } 149 | 150 | int read() const { 151 | return int (accum >> Blip_Buffer::accum_fract); 152 | } 153 | 154 | void next( int bass_shift = 9 ) { 155 | accum -= accum >> bass_shift; 156 | accum += ((long) *buf++ - Blip_Buffer::sample_offset) << Blip_Buffer::accum_fract; 157 | } 158 | }; 159 | 160 | 161 | 162 | // End of public interface 163 | 164 | #define BLIP_BUFFER_ACCURACY 16 165 | 166 | const int blip_res_bits_ = 5; 167 | 168 | typedef BOOST::uint32_t blip_pair_t_; 169 | 170 | class Blip_Impulse_ { 171 | typedef BOOST::uint16_t imp_t; 172 | 173 | blip_eq_t eq; 174 | double volume_unit_; 175 | imp_t* impulses; 176 | imp_t* impulse; 177 | int width; 178 | int fine_bits; 179 | int res; 180 | bool generate; 181 | 182 | void fine_volume_unit(); 183 | void scale_impulse( int unit, imp_t* ) const; 184 | public: 185 | Blip_Buffer* buf; 186 | BOOST::uint32_t offset; 187 | 188 | void init( blip_pair_t_* impulses, int width, int res, int fine_bits = 0 ); 189 | void volume_unit( double ); 190 | void treble_eq( const blip_eq_t& ); 191 | }; 192 | 193 | inline blip_eq_t::blip_eq_t( double t ) : 194 | treble( t ), cutoff( 0 ), sample_rate( 44100 ) { 195 | } 196 | 197 | inline blip_eq_t::blip_eq_t( double t, long c, long sr ) : 198 | treble( t ), cutoff( c ), sample_rate( sr ) { 199 | } 200 | 201 | inline size_t Blip_Buffer::samples_avail() const { 202 | return offset_ >> BLIP_BUFFER_ACCURACY; 203 | } 204 | 205 | inline long Blip_Buffer::sample_rate() const { 206 | return samples_per_sec; 207 | } 208 | 209 | inline void Blip_Buffer::end_frame( blip_time_t t ) { 210 | offset_ += t * factor_; 211 | assert(( "Blip_Buffer::end_frame(): Frame went past end of buffer", 212 | samples_avail() <= buffer_size_ )); 213 | } 214 | 215 | inline void Blip_Buffer::remove_silence( size_t count ) { 216 | assert(( "Blip_Buffer::remove_silence(): Not that many samples available to remove", 217 | count <= samples_avail() )); 218 | offset_ -= resampled_time_t (count) << BLIP_BUFFER_ACCURACY; 219 | } 220 | 221 | inline bool Blip_Buffer::sample_rate( long samples_per_sec ) { 222 | return sample_rate( samples_per_sec, 0 ); 223 | } 224 | 225 | inline int Blip_Buffer::output_latency() const { 226 | return widest_impulse_ / 2; 227 | } 228 | 229 | inline long Blip_Buffer::clock_rate() const { 230 | return clocks_per_sec; 231 | } 232 | 233 | #include "Blip_Synth.h" 234 | 235 | #endif 236 | 237 | -------------------------------------------------------------------------------- /plugin/Source/sms/Blip_Synth.h: -------------------------------------------------------------------------------- 1 | 2 | // Blip_Synth and Blip_Wave are waveform transition synthesizers for adding 3 | // waveforms to a Blip_Buffer. 4 | 5 | // Blip_Buffer 0.3.0. Copyright (C) 2003-2004 Shay Green. GNU GPL license. 6 | 7 | #ifndef BLIP_SYNTH_H 8 | #define BLIP_SYNTH_H 9 | 10 | #ifndef BLIP_BUFFER_H 11 | #include "Blip_Buffer.h" 12 | #endif 13 | 14 | // Quality level. Higher levels are slower, and worse in a few cases. 15 | const int blip_low_quality = 1; 16 | const int blip_med_quality = 2; 17 | const int blip_good_quality = 3; 18 | const int blip_high_quality = 4; 19 | 20 | // Blip_Synth is a transition waveform synthesizer which adds band-limited 21 | // offsets (transitions) into a Blip_Buffer. For a simpler interface, use 22 | // Blip_Wave (below). 23 | // 24 | // Range specifies the greatest expected offset that will occur. For a 25 | // waveform that goes between +amp and -amp, range should be amp * 2 (half 26 | // that if it only goes between +amp and 0). When range is large, a higher 27 | // accuracy scheme is used; to force this even when range is small, pass 28 | // the negative of range (i.e. -range). 29 | template 30 | class Blip_Synth { 31 | BOOST_STATIC_ASSERT( 1 <= quality && quality <= 5 ); 32 | BOOST_STATIC_ASSERT( -32768 < range && range < 32767 ); 33 | enum { abs_range = (range < 0) ? -range : range }; 34 | enum { fine_mode = range > 512 || range < 0 }; 35 | enum { width = quality < 5 ? quality * 4 : Blip_Buffer::widest_impulse_ }; 36 | enum { res = 1 << blip_res_bits_ }; 37 | enum { fine_bits = fine_mode ? (abs_range <= 64 ? 2 : abs_range <= 128 ? 3 : 38 | abs_range <= 256 ? 4 : abs_range <= 512 ? 5 : abs_range <= 1024 ? 6 : 39 | abs_range <= 2048 ? 7 : 8) : 0 }; 40 | enum { impulse_size = width / 2 * (fine_mode + 1) }; 41 | enum { base_impulses_size = width / 2 * (res / 2 + 1) }; 42 | blip_pair_t_ impulses [impulse_size * res * 2 + base_impulses_size]; 43 | Blip_Impulse_ impulse; 44 | public: 45 | Blip_Synth() { impulse.init( impulses, width, res, fine_bits ); } 46 | 47 | // Configure low-pass filter (see notes.txt). Not optimized for real-time control 48 | void treble_eq( const blip_eq_t& eq ) { impulse.treble_eq( eq ); } 49 | 50 | // Set volume of a transition at amplitude 'range' by setting volume_unit 51 | // to v / range 52 | void volume( double v ) { impulse.volume_unit( v * (1.0 / abs_range) ); } 53 | 54 | // Set base volume unit of transitions, where 1.0 is a full swing between the 55 | // positive and negative extremes. Not optimized for real-time control. 56 | void volume_unit( double unit ) { impulse.volume_unit( unit ); } 57 | 58 | // Default Blip_Buffer used for output when none is specified for a given call 59 | Blip_Buffer* output() const { return impulse.buf; } 60 | void output( Blip_Buffer* b ) { impulse.buf = b; } 61 | 62 | // Add an amplitude offset (transition) with an amplitude of delta * volume_unit 63 | // into the specified buffer (default buffer if none specified) at the 64 | // specified source time. Amplitude can be positive or negative. To increase 65 | // performance by inlining code at the call site, use add_inline(). 66 | void offset( blip_time_t, int delta, Blip_Buffer* ) const; 67 | 68 | void offset( blip_time_t t, int delta ) const { 69 | offset( t, delta, impulse.buf ); 70 | } 71 | void offset_inline( blip_time_t time, int delta, Blip_Buffer* buf ) const { 72 | offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); 73 | } 74 | void offset_inline( blip_time_t time, int delta ) const { 75 | offset_inline( time, delta, impulse.buf ); 76 | } 77 | 78 | 79 | // not documented 80 | void offset_resampled( Blip_Buffer::resampled_time_t time, int delta, 81 | Blip_Buffer* blip_buf ) const; 82 | void offset_resampled( Blip_Buffer::resampled_time_t t, int o ) const 83 | { offset_resampled( t, o, impulse.buf ); } 84 | }; 85 | 86 | // Blip_Wave is a synthesizer for adding a *single* waveform to a Blip_Buffer. 87 | // A wave is built from a series of delays and new amplitudes. It provides a 88 | // simpler interface than Blip_Synth. 89 | template 90 | class Blip_Wave { 91 | Blip_Synth synth; 92 | blip_time_t time_; 93 | int last_amp; 94 | public: 95 | // Start wave at time 0 and amplitude 0 96 | Blip_Wave() : time_( 0 ), last_amp( 0 ) { } 97 | 98 | // See Blip_Synth for description 99 | void volume( double v ) { synth.volume( v ); } 100 | void volume_unit( double v ) { synth.volume_unit( v ); } 101 | void treble_eq( const blip_eq_t& eq){ synth.treble_eq( eq ); } 102 | Blip_Buffer* output() const { return synth.output(); } 103 | void output( Blip_Buffer* b ) { synth.output( b ); if ( !b ) time_ = last_amp = 0; } 104 | 105 | // Current time in frame 106 | blip_time_t time() const { return time_; } 107 | void time( blip_time_t t ) { time_ = t; } 108 | 109 | // Current amplitude of wave 110 | int amplitude() const { return last_amp; } 111 | void amplitude( int ); 112 | 113 | // Move forward by 't' time units 114 | void delay( blip_time_t t ) { time_ += t; } 115 | 116 | // End time frame of specified duration. Localize time to new frame. 117 | void end_frame( blip_time_t duration ) { 118 | assert(( "Blip_Wave::end_frame(): Wave hadn't yet been run for entire frame", 119 | duration <= time_ )); 120 | time_ -= duration; 121 | } 122 | }; 123 | 124 | 125 | 126 | 127 | // End of public interface 128 | 129 | template 130 | inline void Blip_Synth::offset_resampled( 131 | Blip_Buffer::resampled_time_t time, int delta, Blip_Buffer* blip_buf ) const 132 | { 133 | typedef blip_pair_t_ pair_t; 134 | 135 | unsigned sample_index = unsigned ((time >> BLIP_BUFFER_ACCURACY) & ~1); 136 | assert(( "Blip_Synth/Blip_wave: Went past end of buffer", 137 | sample_index < blip_buf->buffer_size_ )); 138 | enum { const_offset = Blip_Buffer::widest_impulse_ / 2 - width / 2 }; 139 | pair_t* buf = (pair_t*) &blip_buf->buffer_ [const_offset + sample_index]; 140 | 141 | enum { shift = BLIP_BUFFER_ACCURACY - blip_res_bits_ }; 142 | enum { mask = res * 2 - 1 }; 143 | const pair_t* imp = &impulses [((time >> shift) & mask) * impulse_size]; 144 | 145 | pair_t offset = impulse.offset * delta; 146 | 147 | if ( !fine_bits ) 148 | { 149 | // normal mode 150 | for ( int n = width / 4; n; --n ) 151 | { 152 | pair_t t0 = buf [0] - offset; 153 | pair_t t1 = buf [1] - offset; 154 | 155 | t0 += imp [0] * delta; 156 | t1 += imp [1] * delta; 157 | imp += 2; 158 | 159 | buf [0] = t0; 160 | buf [1] = t1; 161 | buf += 2; 162 | } 163 | } 164 | else 165 | { 166 | // fine mode 167 | enum { sub_range = 1 << fine_bits }; 168 | delta += sub_range / 2; 169 | int delta2 = (delta & (sub_range - 1)) - sub_range / 2; 170 | delta >>= fine_bits; 171 | 172 | for ( int n = width / 4; n; --n ) 173 | { 174 | pair_t t0 = buf [0] - offset; 175 | pair_t t1 = buf [1] - offset; 176 | 177 | t0 += imp [0] * delta2; 178 | t0 += imp [1] * delta; 179 | 180 | t1 += imp [2] * delta2; 181 | t1 += imp [3] * delta; 182 | 183 | imp += 4; 184 | 185 | buf [0] = t0; 186 | buf [1] = t1; 187 | buf += 2; 188 | } 189 | } 190 | } 191 | 192 | template 193 | void Blip_Wave::amplitude( int amp ) { 194 | int delta = amp - last_amp; 195 | last_amp = amp; 196 | synth.offset_inline( time_, delta ); 197 | } 198 | 199 | template 200 | void Blip_Synth::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const { 201 | offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); 202 | } 203 | 204 | #endif 205 | 206 | -------------------------------------------------------------------------------- /plugin/Source/sms/COPYING.TXT: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /plugin/Source/sms/Sms_Apu.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Sms_Snd_Emu 0.1.1. http://www.slack.net/~ant/nes-emu/ 3 | 4 | #include "Sms_Apu.h" 5 | 6 | /* Library Copyright (C) 2003-2004 Shay Green. Sms_Snd_Emu is free 7 | software; you can redistribute it and/or modify it under the terms of the 8 | GNU General Public License as published by the Free Software Foundation; 9 | either version 2 of the License, or (at your option) any later version. 10 | Sms_Snd_Emu is distributed in the hope that it will be useful, but WITHOUT 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | more details. You should have received a copy of the GNU General Public 14 | License along with Sms_Snd_Emu; if not, write to the Free Software 15 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 16 | 17 | // Sms_Osc 18 | 19 | Sms_Osc::Sms_Osc() { 20 | output = NULL; 21 | output_select = 3; 22 | outputs [0] = NULL; // always stays NULL 23 | outputs [1] = NULL; 24 | outputs [2] = NULL; 25 | outputs [3] = NULL; 26 | } 27 | 28 | void Sms_Osc::reset() { 29 | delay = 0; 30 | last_amp = 0; 31 | volume = 0; 32 | output_select = 3; 33 | output = outputs [3]; 34 | } 35 | 36 | // Sms_Square 37 | 38 | Sms_Square::Sms_Square() { 39 | } 40 | 41 | void Sms_Square::reset() { 42 | period = 0; 43 | phase = 0; 44 | Sms_Osc::reset(); 45 | } 46 | 47 | void Sms_Square::run( sms_time_t time, sms_time_t end_time ) 48 | { 49 | if ( !volume || period < 100 ) { // ignore 16kHz and higher 50 | if ( last_amp ) { 51 | synth->offset( time, -last_amp, output ); 52 | last_amp = 0; 53 | } 54 | time += delay; 55 | if ( !period ) { 56 | time = end_time; 57 | } 58 | else if ( time < end_time ) { 59 | // keep calculating phase 60 | int count = int ((end_time - time + period - 1) / period); 61 | phase = (phase + count) & 1; 62 | time += count * period; 63 | } 64 | } 65 | else 66 | { 67 | int amp = phase ? volume : -volume; 68 | if ( amp != last_amp ) { 69 | synth->offset( time, amp - last_amp, output ); 70 | last_amp = amp; 71 | } 72 | 73 | time += delay; 74 | if ( time < end_time ) 75 | { 76 | Blip_Buffer* const output = this->output; 77 | amp *= 2; 78 | do { 79 | amp = -amp; // amp always alternates 80 | synth->offset_inline( time, amp, output ); 81 | time += period; 82 | phase ^= 1; 83 | } 84 | while ( time < end_time ); 85 | this->last_amp = phase ? volume : -volume; 86 | } 87 | } 88 | delay = int (time - end_time); 89 | } 90 | 91 | // Sms_Noise 92 | 93 | static const int noise_periods [3] = { 0x100, 0x200, 0x400 }; 94 | 95 | inline Sms_Noise::Sms_Noise() { 96 | } 97 | 98 | inline void Sms_Noise::reset() { 99 | period = &noise_periods [0]; 100 | shifter = 0x8000; 101 | tap = 12; 102 | Sms_Osc::reset(); 103 | } 104 | 105 | void Sms_Noise::run( sms_time_t time, sms_time_t end_time ) 106 | { 107 | int period = *this->period * 2; 108 | if ( !volume ) { 109 | if ( last_amp ) { 110 | synth.offset( time, -last_amp, output ); 111 | last_amp = 0; 112 | } 113 | delay = 0; 114 | } 115 | else 116 | { 117 | int amp = (shifter & 1) ? -volume : volume; 118 | if ( !period ) 119 | period = 16; 120 | if ( amp != last_amp ) { 121 | synth.offset( time, amp - last_amp, output ); 122 | last_amp = amp; 123 | } 124 | 125 | time += delay; 126 | if ( time < end_time ) 127 | { 128 | Blip_Buffer* const output = this->output; 129 | unsigned shifter = this->shifter; 130 | amp *= 2; 131 | 132 | do { 133 | int changed = 1 & (shifter ^ (shifter >> 1)); 134 | shifter = (((shifter << 15) ^ (shifter << tap)) & 0x8000) | (shifter >> 1); 135 | if ( changed ) { // prev and next bits differ 136 | amp = -amp; 137 | synth.offset_inline( time, amp, output ); 138 | } 139 | time += period; 140 | } 141 | while ( time < end_time ); 142 | 143 | this->shifter = shifter; 144 | this->last_amp = amp >> 1; 145 | } 146 | delay = int (time - end_time); 147 | } 148 | } 149 | 150 | // Sms_Apu 151 | 152 | Sms_Apu::Sms_Apu() 153 | { 154 | for ( int i = 0; i < 3; i++ ) { 155 | squares [i].synth = &square_synth; 156 | oscs [i] = &squares [i]; 157 | } 158 | oscs [3] = &noise; 159 | 160 | volume( 1.0 ); 161 | reset(); 162 | } 163 | 164 | Sms_Apu::~Sms_Apu() { 165 | } 166 | 167 | void Sms_Apu::treble_eq( const blip_eq_t& eq ) { 168 | square_synth.treble_eq( eq ); 169 | noise.synth.treble_eq( eq ); 170 | } 171 | 172 | void Sms_Apu::volume( double vol ) { 173 | vol *= 0.85 / osc_count; 174 | square_synth.volume( vol ); 175 | noise.synth.volume( vol ); 176 | } 177 | 178 | void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) { 179 | for ( int i = 0; i < osc_count; i++ ) 180 | osc_output( i, center, left, right ); 181 | } 182 | 183 | void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, 184 | Blip_Buffer* right ) 185 | { 186 | assert(( "Sms_Apu::osc_output(): Index out of range", 0 <= index && index < osc_count )); 187 | 188 | Sms_Osc& osc = *oscs [index]; 189 | if ( center && !left && !right ) { 190 | // mono 191 | left = center; 192 | right = center; 193 | } 194 | else { 195 | // must be silenced or stereo 196 | assert( (!left && !right) || (left && right) ); 197 | } 198 | osc.outputs [1] = right; 199 | osc.outputs [2] = left; 200 | osc.outputs [3] = center; 201 | osc.output = osc.outputs [osc.output_select]; 202 | } 203 | 204 | void Sms_Apu::reset() { 205 | stereo_found = false; 206 | last_time = 0; 207 | latch = 0; 208 | 209 | squares [0].reset(); 210 | squares [1].reset(); 211 | squares [2].reset(); 212 | noise.reset(); 213 | } 214 | 215 | void Sms_Apu::run_until( sms_time_t end_time ) 216 | { 217 | if ( end_time == last_time ) 218 | return; 219 | assert(( "Sms_Apu::run_until(): End time is before current time", last_time < end_time )); 220 | 221 | // run oscillators 222 | for ( int i = 0; i < osc_count; ++i ) { 223 | Sms_Osc& osc = *oscs [i]; 224 | if ( osc.output ) { 225 | if ( osc.output != osc.outputs [3] ) 226 | stereo_found = true; // playing on side output 227 | osc.run( last_time, end_time ); 228 | } 229 | } 230 | 231 | last_time = end_time; 232 | } 233 | 234 | bool Sms_Apu::end_frame( sms_time_t end_time ) 235 | { 236 | run_until( end_time ); 237 | last_time = 0; 238 | 239 | bool result = stereo_found; 240 | stereo_found = false; 241 | return result; 242 | } 243 | 244 | void Sms_Apu::write_ggstereo( sms_time_t time, int data ) 245 | { 246 | assert(( "Sms_Apu::write_ggstereo(): Data out of range", 0 <= data && data <= 0xFF )); 247 | 248 | run_until( time ); 249 | 250 | // left/right assignments 251 | for ( int i = 0; i < osc_count; i++ ) { 252 | Sms_Osc& osc = *oscs [i]; 253 | int flags = data >> i; 254 | Blip_Buffer* old_output = osc.output; 255 | osc.output_select = ((flags >> 3) & 2) | (flags & 1); 256 | osc.output = osc.outputs [osc.output_select]; 257 | if ( osc.output != old_output && osc.last_amp ) { 258 | if ( old_output ) 259 | square_synth.offset( time, -osc.last_amp, old_output ); 260 | osc.last_amp = 0; 261 | } 262 | } 263 | } 264 | 265 | static const char volumes [16] = { 266 | // volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 ) 267 | 64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0 268 | }; 269 | 270 | void Sms_Apu::write_data( sms_time_t time, int data ) 271 | { 272 | assert(( "Sms_Apu::write_data(): Data out of range", 0 <= data && data <= 0xFF )); 273 | 274 | run_until( time ); 275 | 276 | if ( data & 0x80 ) 277 | latch = data; 278 | 279 | int index = (latch >> 5) & 3; 280 | if ( latch & 0x10 ) { 281 | // volume 282 | oscs [index]->volume = volumes [data & 15]; 283 | } 284 | else if ( index < 3 ) { 285 | // square period 286 | Sms_Square& sq = squares [index]; 287 | if ( data & 0x80 ) 288 | sq.period = (sq.period & ~0xff) | ((data << 4) & 0xff); 289 | else 290 | sq.period = (sq.period & 0xff) | ((data << 8) & 0x3f00); 291 | } 292 | else { 293 | // noise period/mode 294 | int select = data & 3; 295 | if ( select < 3 ) 296 | noise.period = &noise_periods [select]; 297 | else 298 | noise.period = &squares [2].period; 299 | 300 | noise.tap = (data & 0x04) ? 12 : 16; // 16 disables tap 301 | noise.shifter = 0x8000; 302 | } 303 | } 304 | 305 | -------------------------------------------------------------------------------- /plugin/Source/sms/Sms_Apu.h: -------------------------------------------------------------------------------- 1 | 2 | // SEGA Master System SN76489 PSG sound chip emulator 3 | 4 | // Sms_Snd_Emu 0.1.1. Copyright (C) 2003-2004 Shay Green. GNU GPL license. 5 | 6 | #ifndef SMS_APU_H 7 | #define SMS_APU_H 8 | 9 | typedef long sms_time_t; // clock cycle count 10 | 11 | #include "Sms_Oscs.h" 12 | 13 | class Sms_Apu { 14 | public: 15 | Sms_Apu(); 16 | ~Sms_Apu(); 17 | 18 | // Overall volume of all oscillators, where 1.0 is full volume. 19 | void volume( double ); 20 | 21 | // Treble equalization (see notes.txt). 22 | void treble_eq( const blip_eq_t& ); 23 | 24 | // Assign all oscillator outputs to specified buffer(s). If buffer 25 | // is NULL, silence all oscillators. 26 | void output( Blip_Buffer* mono ); 27 | void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); 28 | 29 | // Assign oscillator output to buffer(s). Valid indicies are 0 to 30 | // osc_count - 1, which refer to Square 1, Square 2, Square 3, and 31 | // Noise, respectively. If buffer is NULL, silence oscillator. 32 | enum { osc_count = 4 }; 33 | void osc_output( int index, Blip_Buffer* mono ); 34 | void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); 35 | 36 | // Reset oscillators 37 | void reset(); 38 | 39 | // Write GameGear left/right assignment byte 40 | void write_ggstereo( sms_time_t, int ); 41 | 42 | // Write to data port 43 | void write_data( sms_time_t, int ); 44 | 45 | // Run all oscillators up to specified time, end current frame, then 46 | // start a new frame at time 0. Return true if any oscillators added 47 | // sound to one of the left/right buffers, false if they only added 48 | // to the center buffer. 49 | bool end_frame( sms_time_t ); 50 | 51 | private: 52 | // noncopyable 53 | Sms_Apu( const Sms_Apu& ); 54 | Sms_Apu& operator = ( const Sms_Apu& ); 55 | 56 | Sms_Osc* oscs [osc_count]; 57 | Sms_Square squares [3]; 58 | Sms_Noise noise; 59 | Sms_Square::Synth square_synth; // shared between squares 60 | sms_time_t last_time; 61 | int latch; 62 | bool stereo_found; 63 | 64 | void run_until( sms_time_t ); 65 | }; 66 | 67 | inline void Sms_Apu::output( Blip_Buffer* mono ) { 68 | output( mono, NULL, NULL ); 69 | } 70 | 71 | inline void Sms_Apu::osc_output( int index, Blip_Buffer* mono ) { 72 | osc_output( index, mono, NULL, NULL ); 73 | } 74 | 75 | #endif 76 | 77 | -------------------------------------------------------------------------------- /plugin/Source/sms/Sms_Oscs.h: -------------------------------------------------------------------------------- 1 | 2 | // Private oscillators used by Sms_Apu 3 | 4 | // Sms_Snd_Emu 0.1.1. Copyright (C) 2003-2004 Shay Green. GNU GPL license. 5 | 6 | #ifndef SMS_OSCS_H 7 | #define SMS_OSCS_H 8 | 9 | #include "Blip_Buffer.h" 10 | 11 | struct Sms_Osc 12 | { 13 | virtual ~Sms_Osc() {} 14 | 15 | Blip_Buffer* outputs [4]; // NULL, right, left, center 16 | Blip_Buffer* output; 17 | int output_select; 18 | 19 | int delay; 20 | int last_amp; 21 | int volume; 22 | 23 | Sms_Osc(); 24 | void reset(); 25 | virtual void run( sms_time_t start, sms_time_t end ) = 0; 26 | }; 27 | 28 | struct Sms_Square : Sms_Osc 29 | { 30 | int period; 31 | int phase; 32 | 33 | typedef Blip_Synth Synth; 34 | const Synth* synth; 35 | 36 | Sms_Square(); 37 | void reset(); 38 | void run( sms_time_t, sms_time_t ); 39 | }; 40 | 41 | struct Sms_Noise : Sms_Osc 42 | { 43 | const int* period; 44 | unsigned shifter; 45 | unsigned tap; 46 | 47 | typedef Blip_Synth Synth; 48 | Synth synth; 49 | 50 | Sms_Noise(); 51 | void reset(); 52 | void run( sms_time_t, sms_time_t ); 53 | }; 54 | 55 | #endif 56 | 57 | -------------------------------------------------------------------------------- /plugin/Source/sms/Stereo_Buffer.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Blip_Buffer 0.3.0. http://www.slack.net/~ant/nes-emu/ 3 | 4 | #include "Stereo_Buffer.h" 5 | 6 | /* Library Copyright (C) 2004 Shay Green. Blip_Buffer is free software; 7 | you can redistribute it and/or modify it under the terms of the GNU 8 | General Public License as published by the Free Software Foundation; 9 | either version 2 of the License, or (at your option) any later version. 10 | Stereo_Buffer is distributed in the hope that it will be useful, but 11 | WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 | for more details. You should have received a copy of the GNU General 14 | Public License along with Stereo_Buffer; if not, write to the Free Software 15 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 16 | 17 | Stereo_Buffer::Stereo_Buffer() { 18 | } 19 | 20 | Stereo_Buffer::~Stereo_Buffer() { 21 | } 22 | 23 | bool Stereo_Buffer::sample_rate( long rate, int msec ) 24 | { 25 | for ( int i = 0; i < buf_count; i++ ) { 26 | if ( !bufs [i].sample_rate( rate, msec ) ) 27 | return false; 28 | } 29 | 30 | return true; 31 | } 32 | 33 | void Stereo_Buffer::clock_rate( long rate ) 34 | { 35 | for ( int i = 0; i < buf_count; i++ ) 36 | bufs [i].clock_rate( rate ); 37 | } 38 | 39 | void Stereo_Buffer::bass_freq( int bass ) 40 | { 41 | for ( unsigned i = 0; i < buf_count; i++ ) 42 | bufs [i].bass_freq( bass ); 43 | } 44 | 45 | void Stereo_Buffer::clear() 46 | { 47 | stereo_added = false; 48 | was_stereo = false; 49 | for ( int i = 0; i < buf_count; i++ ) 50 | bufs [i].clear(); 51 | } 52 | 53 | void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo ) 54 | { 55 | for ( unsigned i = 0; i < buf_count; i++ ) 56 | bufs [i].end_frame( clock_count ); 57 | 58 | stereo_added |= stereo; 59 | } 60 | 61 | size_t Stereo_Buffer::read_samples( blip_sample_t* out, size_t max_samples ) 62 | { 63 | size_t count = bufs [0].samples_avail(); 64 | if ( count > max_samples) 65 | count = max_samples; 66 | if ( count ) 67 | { 68 | if ( stereo_added || was_stereo ) 69 | { 70 | mix_stereo( out, count ); 71 | 72 | bufs [0].remove_samples( count ); 73 | bufs [1].remove_samples( count ); 74 | bufs [2].remove_samples( count ); 75 | } 76 | else 77 | { 78 | mix_mono( out, count ); 79 | 80 | bufs [0].remove_samples( count ); 81 | 82 | bufs [1].remove_silence( count ); 83 | bufs [2].remove_silence( count ); 84 | } 85 | 86 | // to do: this might miss opportunities for optimization 87 | if ( !bufs [0].samples_avail() ) { 88 | was_stereo = stereo_added; 89 | stereo_added = false; 90 | } 91 | } 92 | 93 | return count; 94 | } 95 | 96 | void Stereo_Buffer::mix_stereo( blip_sample_t* out, size_t count ) 97 | { 98 | Blip_Reader left; 99 | Blip_Reader right; 100 | Blip_Reader center; 101 | 102 | left.begin( bufs [1] ); 103 | right.begin( bufs [2] ); 104 | int bass = center.begin( bufs [0] ); 105 | 106 | while ( count-- ) 107 | { 108 | int c = center.read(); 109 | out [0] = c + left.read(); 110 | out [1] = c + right.read(); 111 | out += 2; 112 | 113 | center.next( bass ); 114 | left.next( bass ); 115 | right.next( bass ); 116 | } 117 | 118 | center.end( bufs [0] ); 119 | right.end( bufs [2] ); 120 | left.end( bufs [1] ); 121 | } 122 | 123 | void Stereo_Buffer::mix_mono( blip_sample_t* out, size_t count ) 124 | { 125 | Blip_Reader in; 126 | int bass = in.begin( bufs [0] ); 127 | 128 | while ( count-- ) 129 | { 130 | int sample = in.read(); 131 | out [0] = sample; 132 | out [1] = sample; 133 | out += 2; 134 | in.next( bass ); 135 | } 136 | 137 | in.end( bufs [0] ); 138 | } 139 | 140 | -------------------------------------------------------------------------------- /plugin/Source/sms/Stereo_Buffer.h: -------------------------------------------------------------------------------- 1 | 2 | // Simple stereo Blip_Buffer for sound emulators whose oscillators output 3 | // either on the left only, center, or right only. 4 | 5 | // Blip_Buffer 0.3.0. Copyright (C) 2003-2004 Shay Green. GNU GPL license. 6 | 7 | #ifndef STEREO_BUFFER_H 8 | #define STEREO_BUFFER_H 9 | 10 | #include "Blip_Buffer.h" 11 | 12 | class Stereo_Buffer { 13 | public: 14 | Stereo_Buffer(); 15 | ~Stereo_Buffer(); 16 | 17 | // Same as in Blip_Buffer (see Blip_Buffer.h) 18 | bool sample_rate( long, int msec = 0 ); 19 | void clock_rate( long ); 20 | void bass_freq( int ); 21 | void clear(); 22 | 23 | // Buffers to output synthesis to 24 | Blip_Buffer* left(); 25 | Blip_Buffer* center(); 26 | Blip_Buffer* right(); 27 | 28 | // Same as in Blip_Buffer. For more efficient operation, pass false 29 | // for was_stereo if the left and right buffers had nothing added 30 | // to them for this frame. 31 | void end_frame( blip_time_t, bool was_stereo = true ); 32 | 33 | // Output is stereo with channels interleved, left before right. Counts 34 | // are in samples, *not* pairs. 35 | size_t samples_avail() const; 36 | size_t read_samples( blip_sample_t*, size_t ); 37 | 38 | private: 39 | // noncopyable 40 | Stereo_Buffer( const Stereo_Buffer& ); 41 | Stereo_Buffer& operator = ( const Stereo_Buffer& ); 42 | 43 | enum { buf_count = 3 }; 44 | Blip_Buffer bufs [buf_count]; 45 | bool stereo_added; 46 | bool was_stereo; 47 | 48 | void mix_stereo( blip_sample_t*, size_t ); 49 | void mix_mono( blip_sample_t*, size_t ); 50 | }; 51 | 52 | inline Blip_Buffer* Stereo_Buffer::left() { 53 | return &bufs [1]; 54 | } 55 | 56 | inline Blip_Buffer* Stereo_Buffer::center() { 57 | return &bufs [0]; 58 | } 59 | 60 | inline Blip_Buffer* Stereo_Buffer::right() { 61 | return &bufs [2]; 62 | } 63 | 64 | inline size_t Stereo_Buffer::samples_avail() const { 65 | return bufs [0].samples_avail(); 66 | } 67 | 68 | #endif 69 | 70 | -------------------------------------------------------------------------------- /plugin/Source/sms/blargg_common.h: -------------------------------------------------------------------------------- 1 | 2 | // Common headers used by Shay Green's libraries 3 | 4 | #ifndef BLARGG_COMMON_H 5 | #define BLARGG_COMMON_H 6 | 7 | // allow prefix configuration file 8 | #ifdef HAVE_CONFIG_H 9 | #include "config.h" 10 | #endif 11 | 12 | // check for boost availability 13 | #include "boost/config.hpp" 14 | #ifndef BOOST 15 | #define BOOST boost 16 | #endif 17 | 18 | // BLARGG_BIG_ENDIAN and BLARGG_LITTLE_ENDIAN 19 | #if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) 20 | #if defined (__powerc) || defined (macintosh) 21 | #define BLARGG_BIG_ENDIAN 1 22 | 23 | #elif defined (_MSC_VER) && defined (_M_IX86) 24 | #define BLARGG_LITTLE_ENDIAN 1 25 | #endif 26 | #endif 27 | 28 | // BLARGG_CPU_* 29 | #if !defined (BLARGG_CPU_POWERPC) && !defined (BLARGG_CPU_X86) 30 | #if defined (__powerc) 31 | #define BLARGG_CPU_POWERPC 1 32 | 33 | #elif defined (_MSC_VER) && defined (_M_IX86) 34 | #define BLARGG_CPU_X86 1 35 | #endif 36 | #endif 37 | 38 | // BLARGG_MOST_PORTABLE (use most portable version when there's a choice) 39 | #ifndef BLARGG_MOST_PORTABLE 40 | #define BLARGG_MOST_PORTABLE 0 41 | #endif 42 | 43 | // BLARGG_DEBUG 44 | #ifndef BLARGG_DEBUG 45 | #ifdef NDEBUG 46 | #define BLARGG_DEBUG 0 47 | #else 48 | #define BLARGG_DEBUG 1 49 | #endif 50 | #endif 51 | 52 | // BOOST_STATIC_ASSERT( expr ) 53 | #include "boost/static_assert.hpp" 54 | 55 | // BOOST::uint8_t, BOOST::int16_t, etc. 56 | #include "boost/cstdint.hpp" 57 | 58 | // bool, true, false 59 | #ifndef BLARGG_COMPILER_HAS_BOOL 60 | #if !BOOST_MINIMAL 61 | #define BLARGG_COMPILER_HAS_BOOL 1 62 | 63 | #elif defined (__MWERKS__) 64 | #if !__option(bool) 65 | #define BLARGG_COMPILER_HAS_BOOL 0 66 | #endif 67 | 68 | #elif defined (_MSC_VER) 69 | #if _MSC_VER < 1100 70 | #define BLARGG_COMPILER_HAS_BOOL 0 71 | #endif 72 | 73 | #elif __cplusplus < 199711 74 | #define BLARGG_COMPILER_HAS_BOOL 0 75 | 76 | #endif 77 | #endif 78 | #ifndef BLARGG_COMPILER_HAS_BOOL 79 | #define BLARGG_COMPILER_HAS_BOOL 1 80 | #endif 81 | 82 | #if !BLARGG_COMPILER_HAS_BOOL 83 | typedef int bool; 84 | const bool true = 1; 85 | const bool false = 0; 86 | #endif 87 | 88 | #include 89 | #include 90 | 91 | #endif 92 | 93 | -------------------------------------------------------------------------------- /plugin/Source/sms/boost/array.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Boost substitute. For full boost library see http://boost.org 3 | 4 | #ifndef BOOST_ARRAY_HPP 5 | #define BOOST_ARRAY_HPP 6 | 7 | #include 8 | 9 | template 10 | struct array 11 | { 12 | T elems [N]; 13 | 14 | size_t size() const { return N; } 15 | 16 | T * begin() { return elems; } 17 | const T* begin() const { return elems; } 18 | 19 | T * end() { return elems + N; } 20 | const T* end() const { return elems + N; } 21 | 22 | T& operator [] ( size_t i ) { 23 | assert( i < N ); 24 | return elems [i]; 25 | } 26 | 27 | const T& operator [] ( size_t i ) const { 28 | assert( i < N ); 29 | return elems [i]; 30 | } 31 | 32 | void assign( const T& value ) { 33 | for ( size_t i = 0; i < N; i++ ) 34 | elems [i] = value; 35 | } 36 | }; 37 | 38 | #endif 39 | 40 | -------------------------------------------------------------------------------- /plugin/Source/sms/boost/config.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Boost substitute. For full boost library see http://boost.org 3 | 4 | #ifndef BOOST_CONFIG_HPP 5 | #define BOOST_CONFIG_HPP 6 | 7 | #define BOOST_MINIMAL 1 8 | 9 | #define BOOST 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /plugin/Source/sms/boost/cstdint.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Boost substitute. For full boost library see http://boost.org 3 | 4 | #ifndef BOOST_CSTDINT_HPP 5 | #define BOOST_CSTDINT_HPP 6 | 7 | #include 8 | #include 9 | 10 | #endif 11 | 12 | -------------------------------------------------------------------------------- /plugin/Source/sms/boost/scoped_ptr.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Boost substitute. For full boost library see http://boost.org 3 | 4 | #ifndef BOOST_SCOPED_PTR_HPP 5 | #define BOOST_SCOPED_PTR_HPP 6 | 7 | template 8 | class scoped_ptr { 9 | T* ptr; 10 | 11 | // noncopyable 12 | scoped_ptr( const scoped_ptr& ); 13 | scoped_ptr& operator = ( const scoped_ptr& ); 14 | public: 15 | scoped_ptr( T* p = 0 ) : ptr( p ) { 16 | } 17 | 18 | ~scoped_ptr() { 19 | delete (ptr + 0); // ensure T is defined 20 | } 21 | 22 | void reset( T* p = 0 ) { 23 | assert( !p || p != ptr ); 24 | T* old = ptr; 25 | ptr = p; 26 | delete (old + 0); // ensure T is defined 27 | } 28 | 29 | T& operator * () const { 30 | assert( ptr ); 31 | return *ptr; 32 | } 33 | 34 | T* operator -> () const { 35 | assert( ptr ); 36 | return ptr; 37 | } 38 | 39 | T* get () const { 40 | return ptr; 41 | } 42 | 43 | T* release() { 44 | T* old = ptr; 45 | ptr = NULL; 46 | return old; 47 | } 48 | 49 | typedef struct undefined_type* bool_type; 50 | 51 | operator bool_type () const { 52 | return (bool_type) ptr; 53 | } 54 | 55 | bool operator ! () const { 56 | return !ptr; 57 | } 58 | 59 | void swap( scoped_ptr& other ) { 60 | T* p = ptr; 61 | ptr = other.ptr; 62 | other.ptr = p; 63 | } 64 | }; 65 | 66 | #endif 67 | 68 | -------------------------------------------------------------------------------- /plugin/Source/sms/boost/static_assert.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Boost substitute. For full boost library see http://boost.org 3 | 4 | #ifndef BOOST_STATIC_ASSERT_HPP 5 | #define BOOST_STATIC_ASSERT_HPP 6 | 7 | #define BOOST_STATIC_ASSERT3( expr, line ) \ 8 | typedef int boost_static_assert_##line [1 / ((expr) ? 1 : 0)] 9 | 10 | #define BOOST_STATIC_ASSERT2( expr, line ) BOOST_STATIC_ASSERT3( expr, line ) 11 | 12 | #define BOOST_STATIC_ASSERT( expr ) BOOST_STATIC_ASSERT2( expr, __LINE__ ) 13 | 14 | #endif 15 | 16 | -------------------------------------------------------------------------------- /plugin/Source/sms/changes.txt: -------------------------------------------------------------------------------- 1 | Sms_Snd_Emu Change Log 2 | 3 | 4 | Sms_Snd_Emu 0.1.1 5 | ----------------- 6 | 7 | - Moved VGM player-related modules to Game_Music_Emu library 8 | 9 | - Added note about compiler optimizer bugs 10 | 11 | - Replaced Blip_Buffer::buffer_size() and Blip_Buffer::units_per_sample() with 12 | simpler functions (see Blip_Buffer.h and updated demo) 13 | 14 | // old way 15 | if ( !buf.buffer_size( sample_count ) ) 16 | out_of_memory(); 17 | buf.units_per_sample( clocks_per_sec / samples_per_sec ); 18 | 19 | // new way 20 | int length = 1000 / 2; // 1/2 second buffer length 21 | if ( !buf.sample_rate( samples_per_sec, length ) ) 22 | out_of_memory(); 23 | buf.clock_rate( clocks_per_sec ); 24 | 25 | - Renamed Sms_Apu::master_volume() to volume() 26 | 27 | - Added Sms_Apu::output() to assign all oscillators in one call. 28 | 29 | - Expanded notes 30 | 31 | - Updated to Blip_Buffer 0.3.0 32 | 33 | - Improved demo and changed it to use new Stereo_Buffer 34 | 35 | - Made assertions more descriptive when they fail 36 | 37 | - Changed noise oscillator to handle a period of 0 so it would work in a 38 | Genesis emulator or GYM player. 39 | 40 | 41 | Sms_Snd_Emu 0.1.0 42 | ----------------- 43 | 44 | - First release 45 | 46 | -------------------------------------------------------------------------------- /plugin/Source/sms/notes.txt: -------------------------------------------------------------------------------- 1 | Sms_Snd_Emu Notes 2 | 3 | 4 | Blip_Buffer operation 5 | --------------------- 6 | 7 | The demo shows basic Blip_Buffer use. For more information and examples of 8 | using Blip_Buffer, download the full Blip_Buffer library package from 9 | http://www.slack.net/~ant/nes-emu/ 10 | 11 | 12 | Stereo output 13 | ------------- 14 | 15 | Stereo output is handled in a different manner than usual; rather than have the 16 | common left and right buffers and add to both buffers for sounds in the center, 17 | three buffers are used: left, center, right. Mono output is achieved simply and 18 | efficiently by setting all three outputs to the same buffer. This yields a much 19 | simpler and higher-performance implementation, since most sounds are in the 20 | center. 21 | 22 | The included Stereo_Mixer provides one Blip_Buffer for each of the center, 23 | left, and right channels. It generates output by adding the center buffer to 24 | both the left and right buffers, and optimizes for the common case where no 25 | stereo output was made for a particular frame. 26 | 27 | left = center buffer + left buffer 28 | right = center buffer + right buffer 29 | 30 | 31 | Sound chip accuracy 32 | ------------------- 33 | 34 | I didn't see mention of the volume non-linearity in "SN79489 notes", but found 35 | a step multiplier of 1.26 in an emulator source file which matches the output 36 | of a SEGA Genesis Powerbase Convertor fairly well. I found another table 37 | measured from a SEGA Master System 2 but it sounded too linear and loud. 38 | 39 | 40 | Using the APU in an emulator 41 | ---------------------------- 42 | 43 | The following code skeleton shows basic use of the APU in an emulator: 44 | 45 | #include "Sms_Apu.h" 46 | 47 | Blip_Buffer buf; 48 | Sms_Apu apu; 49 | 50 | sms_time_t total_cycles; 51 | sms_time_t cycles_remain; 52 | 53 | sms_time_t cpu_time() { 54 | return total_cycles - cycles_remain; 55 | } 56 | 57 | void cpu_write_port( int port, int data ) 58 | { 59 | if ( (port & 0xff) == 0x06 ) 60 | apu.write_ggstereo( cpu_time(), data ); 61 | else if ( ((port & 0xc0) == 0x40 ) 62 | apu.write_data( cpu_time(), data ); 63 | } 64 | 65 | void emulate_cpu( sms_time_t cycle_count ) 66 | { 67 | total_cycles += cycle_count; 68 | cycles_remain += cycle_count; 69 | 70 | while ( cycles_remain > 0 ) 71 | { 72 | // emulate opcode 73 | // ... 74 | cycles_remain -= cycle_table [opcode]; 75 | } 76 | } 77 | 78 | void output_samples( const blip_sample_t*, size_t count ); 79 | const size_t out_size = 4096; 80 | blip_sample_t out_buf [out_size]; 81 | 82 | void end_time_frame( sms_time_t length ) 83 | { 84 | apu.end_frame( length ); 85 | buf.end_frame( length ); 86 | total_cycles -= length; 87 | 88 | // Read some samples out of Blip_Buffer if there are enough to 89 | // fill our output buffer 90 | if ( buf.samples_avail() >= out_size ) 91 | { 92 | size_t count = buf.read_samples( out_buf, out_size ); 93 | output_samples( out_buf, count ); 94 | } 95 | } 96 | 97 | void render_frame() 98 | { 99 | // ... 100 | end_time_frame( elapsed() ); 101 | } 102 | 103 | void init() 104 | { 105 | if ( !buf.sample_rate( 44100 ) ) 106 | error( "Out of memory" ); 107 | buf.clock_rate( 3579545 ); 108 | apu.output( &buf ); 109 | } 110 | 111 | Time frames 112 | ----------- 113 | 114 | Synthesis is usually of greater duration than the length of the sample buffer 115 | and limited range of the time type. Because of this, synthesis can be broken 116 | into individual frames with time specified relative to the beginning of the 117 | frame. 118 | 119 | A frame of sound is made with Blip_Buffer by adding transitions and ended by 120 | specifying its duration to end_frame(); this begins a new frame after the old 121 | one. 122 | 123 | The resulting samples of previous frames can be read out of the buffer with 124 | read_samples(). Once read the samples are removed. Unread samples reduce buffer 125 | space available to the current frame. 126 | 127 | Time frames can be any length, so long as they fit into the buffer. Successive 128 | time frames don't need to be the same length; each time frame can be a 129 | different length. 130 | 131 | 132 | Output sample rate 133 | ------------------ 134 | 135 | The library works best with an output sample rate around 44-48 kHz. Because the 136 | output is band-limited, there is little reason to use a higher sampling rate 137 | (i.e. 96 kHz) unless it benefits the output hardware. 138 | 139 | 140 | Problems due to compiler's optimizer 141 | ------------------------------------ 142 | 143 | If you are having problems with the library first try lowering or turning off 144 | the compiler's optimizer. If this fixes the problem, contact me so I can add a 145 | workaround in the next version. 146 | 147 | 148 | Limited Blip_Buffer length 149 | -------------------------- 150 | 151 | A Blip_Buffer can't be larger than approximately 65000 samples. At a 44100 152 | sample rate, the length is limited to 1500 milliseconds. This is unlikely to be 153 | a problem since synthesis is usually done in much smaller time segments. 154 | 155 | 156 | Thread-safety 157 | ------------- 158 | 159 | When using multiple threads, except where noted you must ensure that only one 160 | thread invokes a function at a time; the library is not written to handle 161 | concurrent operations. Most functions fall into either one-time setup or 162 | ongoing operations, so this won't be much of an issue. 163 | 164 | 165 | Treble equalization (low-pass filtering) 166 | ---------------------------------------- 167 | 168 | Blip_Synth and Blip_Wave have flexible high-frequency equalization to allow 169 | matching the characteristics of sound hardware. Blip_Eq stores the parameters. 170 | 171 | Blip_Eq( treble ) specifies an exponential rolloff beginning at 0 Hz and 172 | attenuating by 'treble' dB at half the sample rate. For example, with treble = 173 | -12 dB, the following results: 174 | 175 | 0dB ---___ 176 | ~~~---___ 177 | ~~~---___ 178 | ~~~---___ 179 | ~~~---_* treble = -12dB 180 | 181 | -18dB - - - - - - - - - - - - - - - - - - - - - 182 | 0 sample rate / 2 183 | 184 | 185 | Blip_Eq( treble, cutoff, sample_rate ) specifies an exponential rolloff 186 | beginning at 'cutoff' Hz and attenuating by 'treble' dB at 22kHz. For example, 187 | with cutoff = 8000 Hz and treble = -6 dB, the following results: 188 | 189 | cutoff = 8kHz 190 | 0dB -------*__ 191 | ~~--__ treble = -6dB 192 | ~~-*__ 193 | ~~--__ 194 | ~~--__ 195 | ~~--__ 196 | -18dB - - - - - - - - - - - - - - - - - - - - - - 197 | 0 8kHz 22kHz 44kHz ... 198 | 199 | 200 | 201 | Bass frequency (high-pass filtering) 202 | ------------------------------------ 203 | 204 | Blip_Buffer::bass_freq( breakpoint ) results in a steep rolloff which passes 205 | -3dB attenuation at 'breakpoint' Hz. For example, with breakpoint = 1000 Hz, 206 | the following results: 207 | 208 | breakpoint = 1000 Hz 209 | 0dB ___________ 210 | -3dB ,_*---~~~~~ 211 | _~ 212 | / 213 | / 214 | | 215 | | 216 | -21dB - - - - - - - - - - - - - 217 | 0 1000 Hz 4000 Hz 218 | 219 | The primary purpose of Blip_Buffer::bass_freq() is to remove any DC component 220 | (an unchanging offset from center level) from the output since it would reduce 221 | the available volume range. The default value of 15 Hz works well in general. 222 | The breakpoint can be increased to simulate a smaller speaker. Since DC removal 223 | can hide erroneous Blip_Synth-based oscillators which drift, it can be useful 224 | to disable low-pass filtering by setting the breakpoint to 0 Hz and examine the 225 | output with a sound program. Blip_Wave oscillators are immune since they deal 226 | in absolute amplitudes rather than differences. 227 | 228 | 229 | Compatibility/Performance 230 | ------------------------- 231 | 232 | Little compatibility checking and performance tuning have been done for common 233 | platforms. Any assistance in this would be appreciated. If you have *any* 234 | trouble or come up with improvements, contact me. 235 | 236 | 237 | Configuration 238 | ------------- 239 | 240 | The header "blargg_common.h" is used to establish a common environment. It 241 | attempts to automatically determine the features of the environment, but might 242 | need assistance. 243 | 244 | If HAVE_CONFIG_H is defined, the file "config.h" is included at the beginning 245 | of each library header file, allowing configuration options for the library to 246 | be set. It's fine if other libraries also use this scheme, as they won't 247 | conflict. 248 | 249 | Some libraries depend on the byte ordering of multibyte types. If this can't be 250 | determined and the library requires it, a compilation error will result. For 251 | big-endian (most significant byte first, i.e. Motorola 68000, PowerPC), #define 252 | BLARGG_BIG_ENDIAN to 1. For little-endian (least significant byte first, i.e. 253 | Intel x86), #define BLARGG_LITTLE_ENDIAN to 1. 254 | 255 | Pre-ISO C++ compilers might not support bool. Support is provided where bool is 256 | not available, but the compiler's support of bool might not be properly 257 | determined. If errors occur in "blargg_common.h" in the bool section, #define 258 | BLARGG_COMPILER_HAS_BOOL to 1 or 0 depending on whether your compiler supports 259 | bool or not. 260 | 261 | If you have any problems with "blargg_common.h", contact me. 262 | 263 | 264 | Boost Compatibility 265 | ------------------- 266 | 267 | Boost is a collection of useful libraries which provide basic services. If it's 268 | not installed in your environment or your environment isn't supported, a small 269 | substitute is included in the "boost/" directory. This substitute implements a 270 | small subset of boost in a way that will work with most compilers. If boost is 271 | already installed, delete the included "boost/" directory. For more information 272 | about boost, see http://boost.org/ 273 | 274 | 275 | Error handling 276 | -------------- 277 | 278 | To alloc compatibility with older (ARM) C++ compilers, no exceptions are thrown 279 | by any of the libraries. The library is exception-safe, and any exceptions 280 | which occur are not intercepted. 281 | 282 | Memory allocation is kept to an absolute minimum. If the C++ compiler's memory 283 | allocator throws an exception when no more memory is available, this will be 284 | allowed to propagate, otherwise if no exceptions are used a lack of memory will 285 | be reported via the return value. 286 | 287 | Significant violations of the documented interface are flagged with debug-only 288 | assertions. Failure of these usually indicates a caller error rather than a 289 | defect in the library. 290 | 291 | 292 | Support for 16-bit ints 293 | ----------------------- 294 | 295 | Some compilers for smaller architectures use 16-bits for the 'int' type because 296 | it improves performance. For the most part 16-bit ints work fine with these 297 | libraries, though I haven't thoroughly checked for reliance on 32 bits. If 298 | you'd like well-tested support, contact me. I'm not sure whether supporting 299 | 16-bit ints provides much value. 300 | 301 | 302 | Naming conventions 303 | ------------------ 304 | 305 | Multi-word names have an underscore '_' separator between individual words. 306 | 307 | Functions are named with lowercase words. Functions which perform an action 308 | with side-effects are named with a verb phrase (i.e. load, move, run). 309 | Functions which set or return the value of a piece of state are named using a 310 | noun phrase (i.e. loaded, moved, running). 311 | 312 | Classes are named with capitalized words. Only the first letter of an acronym 313 | is capitalized. Class names are nouns, sometimes suggestive of what they do 314 | (i.e. File_Scanner). 315 | 316 | Structure, enumeration, and typedefs to these and built-in types are named 317 | using lowercase words with a _t suffix. 318 | 319 | Macros are named with all-uppercase words. 320 | 321 | Internal names which can't be hidden due to technical reasons have an 322 | underscore '_' suffix. 323 | 324 | 325 | -- 326 | Shay Green 327 | -------------------------------------------------------------------------------- /plugin/Source/sms/readme.txt: -------------------------------------------------------------------------------- 1 | Sms_Snd_Emu 0.1.1: SEGA Master System Sound Emulation Library 2 | 3 | This is a portable SEGA Master System SN76489 PSG sound chip emulator library 4 | for use in a SMS emulator or a VGM music file player. Licensed under the GNU 5 | General Public License (GPL); see COPYING.TXT. Copyright (C) 2003-2004 Shay 6 | Green. 7 | 8 | Website: http://www.slack.net/~ant/nes-emu/ 9 | Contact: blargg@mail.com 10 | 11 | 12 | Getting Started 13 | --------------- 14 | 15 | This library is written in somewhat conservative C++ that should compile with 16 | current and older compilers (ISO and ARM). 17 | 18 | If the Boost library is installed in your environment, delete the included 19 | "boost" compatibility directory, otherwise add the included "boost" directory 20 | to your compiler's search paths. 21 | 22 | Build a program consisting of the included source files and any necessary 23 | system libraries. It should generate an AIFF sound file "out.aif" of random 24 | tones. 25 | 26 | See notes.txt for more information, and respective header (.h) files for 27 | reference. 28 | 29 | 30 | Files 31 | ----- 32 | 33 | notes.txt Collection of notes about the library 34 | changes.txt Changes made since previous releases 35 | COPYING.txt GNU General Public License 36 | 37 | demo.cpp How to use the Sms_Apu sound chip emulator 38 | 39 | Sms_Apu.h Master System SN76489 PSG sound chip emulator 40 | Stereo_Buffer.h Stereo sound synthesis buffer 41 | Blip_Buffer.h Sound synthesis buffer 42 | 43 | Sound_Writer.hpp AIFF sound file writer used for demo output 44 | Sound_Writer.cpp 45 | blargg_common.h Library implementation source code 46 | Blip_Buffer.cpp 47 | Blip_Synth.h 48 | Sms_Apu.cpp 49 | Sms_Oscs.h 50 | Stereo_Buffer.cpp 51 | boost/ Substitute for boost library if it's unavailable 52 | 53 | -- 54 | Shay Green 55 | --------------------------------------------------------------------------------