├── .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 └── icon.png └── Source ├── PadPluginEditor.cpp ├── PadPluginEditor.h ├── PluginEditor.cpp ├── PluginEditor.h ├── PluginProcessor.cpp ├── PluginProcessor.h ├── boost ├── config.hpp ├── cstdint.hpp └── static_assert.hpp └── gb_apu ├── Blip_Buffer.cpp ├── Blip_Buffer.h ├── Blip_Synth.h ├── Gb_Apu.cpp ├── Gb_Apu.h ├── Gb_Oscs.cpp ├── Gb_Oscs.h ├── LGPL.txt ├── Multi_Buffer.cpp ├── Multi_Buffer.h ├── blargg_common.h └── blargg_source.h /.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 | - name: Upload Artifact 27 | uses: actions/upload-artifact@v3 28 | with: 29 | name: Binaries 30 | path: bin 31 | retention-days: 1 32 | -------------------------------------------------------------------------------- /.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 | - name: Upload Artifact 30 | uses: actions/upload-artifact@v3 31 | with: 32 | name: Binaries 33 | path: bin 34 | -------------------------------------------------------------------------------- /.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 | - name: Upload Artifact 23 | uses: actions/upload-artifact@v3 24 | with: 25 | name: Binaries 26 | path: bin 27 | -------------------------------------------------------------------------------- /.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 | Debug 35 | Release 36 | 37 | *.suo 38 | *.db 39 | *.opendb 40 | *.xcuserstate 41 | *.xcscheme 42 | *.xcbkptlist 43 | 44 | plugin/Builds/VisualStudio2015/x64 45 | plugin/Builds/VisualStudio2017/x64 46 | plugin/Builds/VisualStudio2017/Win32 47 | plugin/Builds/VisualStudio2017/.vs 48 | *.user 49 | 50 | xcuserdata 51 | Builds 52 | JuceLibraryCode 53 | .DS_Store 54 | ci/bin 55 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "modules/drowaudio"] 2 | path = modules/dRowAudio 3 | url = https://github.com/FigBug/drowaudio.git 4 | [submodule "modules/gin"] 5 | path = modules/gin 6 | url = https://github.com/FigBug/Gin.git 7 | [submodule "modules/juce"] 8 | path = modules/juce 9 | url = https://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 PAPU) 7 | set (PLUGIN_VERSION 1.2.0) 8 | set (BUNDLE_ID com.socalabs.PAPU) 9 | set (AU_ID PAPUAU) 10 | set (LV2_URI https://socalabs.com/papu/) 11 | set (PLUGIN_CODE Papu) 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_plugin) 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 | set_property (DIRECTORY APPEND PROPERTY LABELS Assets) 91 | 92 | juce_add_binary_data (${PLUGIN_NAME}_Assets SOURCES 93 | "plugin/Resources/icon.png" 94 | ) 95 | 96 | set_target_properties(${PLUGIN_NAME}_Assets PROPERTIES UNITY_BUILD ON UNITY_BUILD_MODE BATCH UNITY_BUILD_BATCH_SIZE 50) 97 | 98 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 99 | 100 | juce_set_vst2_sdk_path (${CMAKE_SOURCE_DIR}/modules/plugin_sdk/vstsdk2.4) 101 | 102 | juce_add_plugin (${PLUGIN_NAME} 103 | PRODUCT_NAME ${PLUGIN_NAME} 104 | VERSION ${PLUGIN_VERSION} 105 | COMPANY_NAME SocaLabs 106 | COMPANY_WEBSITE "https://socalabs.com/" 107 | BUNDLE_ID ${BUNDLE_ID} 108 | FORMATS ${FORMATS} 109 | PLUGIN_MANUFACTURER_CODE Soca 110 | PLUGIN_CODE ${PLUGIN_CODE} 111 | IS_SYNTH ON 112 | NEEDS_MIDI_INPUT ON 113 | EDITOR_WANTS_KEYBOARD_FOCUS ON 114 | VST2_CATEGORY kPlugCategSynth 115 | VST3_CATEGORIES Instrument 116 | AU_MAIN_TYPE kAudioUnitType_MusicDevice 117 | AU_EXPORT_PREFIX ${AU_ID} 118 | AU_SANDBOX_SAFE FALSE 119 | LV2URI ${LV2_URI}) 120 | 121 | file (GLOB_RECURSE source_files CONFIGURE_DEPENDS 122 | ${CMAKE_CURRENT_SOURCE_DIR}/plugin/*.cpp 123 | ${CMAKE_CURRENT_SOURCE_DIR}/plugin/*.c 124 | ${CMAKE_CURRENT_SOURCE_DIR}/plugin/*.cc 125 | ${CMAKE_CURRENT_SOURCE_DIR}/plugin/*.h) 126 | 127 | target_sources (${PLUGIN_NAME} PRIVATE ${source_files}) 128 | source_group (TREE ${CMAKE_CURRENT_SOURCE_DIR}/plugin PREFIX Source FILES ${source_files}) 129 | 130 | file (GLOB_RECURSE asset_files CONFIGURE_DEPENDS 131 | ${CMAKE_CURRENT_SOURCE_DIR}/Assets/*) 132 | 133 | target_sources (${PLUGIN_NAME} PRIVATE ${asset_files}) 134 | source_group (TREE ${CMAKE_CURRENT_SOURCE_DIR}/Assets PREFIX Assets FILES ${asset_files}) 135 | 136 | target_link_libraries (${PLUGIN_NAME} PRIVATE 137 | ${PLUGIN_NAME}_Assets 138 | 139 | gin 140 | gin_dsp 141 | gin_simd 142 | gin_graphics 143 | gin_gui 144 | gin_plugin 145 | 146 | juce::juce_audio_basics 147 | juce::juce_audio_devices 148 | juce::juce_audio_formats 149 | juce::juce_audio_plugin_client 150 | juce::juce_audio_processors 151 | juce::juce_audio_utils 152 | juce::juce_core 153 | juce::juce_cryptography 154 | juce::juce_data_structures 155 | juce::juce_events 156 | juce::juce_graphics 157 | juce::juce_gui_basics 158 | juce::juce_gui_extra 159 | 160 | juce::juce_recommended_config_flags 161 | ) 162 | 163 | target_include_directories (${PLUGIN_NAME} PRIVATE modules/fmt/include) 164 | target_include_directories (${PLUGIN_NAME} PRIVATE modules/ASIO/common) 165 | target_include_directories (${PLUGIN_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Source/Definitions) 166 | 167 | juce_generate_juce_header (${PLUGIN_NAME}) 168 | 169 | target_compile_definitions (${PLUGIN_NAME} PRIVATE 170 | JUCE_DISPLAY_SPLASH_SCREEN=0 171 | JUCE_COREGRAPHICS_DRAW_ASYNC=1 172 | JUCE_MODAL_LOOPS_PERMITTED=1 173 | JUCE_WEB_BROWSER=0 174 | JUCE_USE_FLAC=0 175 | JUCE_USE_CURL=1 176 | JUCE_USE_MP3AUDIOFORMAT=0 177 | JUCE_USE_LAME_AUDIO_FORMAT=0 178 | JUCE_USE_WINDOWS_MEDIA_FORMAT=0 179 | JucePlugin_PreferredChannelConfigurations={0,2} 180 | _CRT_SECURE_NO_WARNINGS 181 | ) 182 | 183 | if (APPLE) 184 | set_target_properties("juce_vst3_helper" PROPERTIES XCODE_ATTRIBUTE_CLANG_LINK_OBJC_RUNTIME NO) 185 | 186 | foreach(t ${FORMATS} "Assets" "All" "") 187 | set(tgt ${CMAKE_PROJECT_NAME}) 188 | if (NOT t STREQUAL "") 189 | set(tgt ${tgt}_${t}) 190 | endif() 191 | if (TARGET ${tgt}) 192 | set_target_properties(${tgt} PROPERTIES 193 | XCODE_ATTRIBUTE_CLANG_LINK_OBJC_RUNTIME NO 194 | #XCODE_ATTRIBUTE_DEPLOYMENT_POSTPROCESSING[variant=Release] YES 195 | XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] "YES" 196 | ) 197 | if (NOT t STREQUAL "All") 198 | target_compile_options(${tgt} PRIVATE 199 | -Wall -Wstrict-aliasing -Wunused-parameter -Wconditional-uninitialized -Woverloaded-virtual -Wreorder -Wconstant-conversion -Wbool-conversion -Wextra-semi 200 | -Wunreachable-code -Winconsistent-missing-destructor-override -Wshift-sign-overflow -Wnullable-to-nonnull-conversion -Wuninitialized -Wno-missing-field-initializers 201 | -Wno-ignored-qualifiers -Wno-missing-braces -Wno-char-subscripts -Wno-unused-private-field -fno-aligned-allocation -Wunused-private-field -Wunreachable-code 202 | -Wenum-compare -Wshadow -Wfloat-conversion -Wshadow-uncaptured-local -Wshadow-field -Wsign-compare -Wdeprecated-this-capture -Wimplicit-float-conversion 203 | -ffast-math -fno-finite-math-only) 204 | endif() 205 | endif() 206 | endforeach() 207 | endif() 208 | 209 | if (WIN32) 210 | foreach(t ${FORMATS} "Assets" "All" "") 211 | set(tgt ${CMAKE_PROJECT_NAME}) 212 | if (NOT t STREQUAL "") 213 | set(tgt ${tgt}_${t}) 214 | endif() 215 | if (TARGET ${tgt}) 216 | set_property(TARGET ${tgt} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /INCREMENTAL:NO") 217 | set_target_properties(${tgt} PROPERTIES LINK_FLAGS "/ignore:4099") 218 | endif() 219 | endforeach() 220 | endif() 221 | 222 | if(UNIX AND NOT APPLE) 223 | target_link_libraries (${PLUGIN_NAME} PRIVATE curl) 224 | endif() 225 | 226 | 227 | if(WIN32) 228 | set (dest "Program Files") 229 | else() 230 | set (dest "Applications") 231 | endif() 232 | 233 | install (TARGETS ${PLUGIN_NAME} DESTINATION "${dest}") 234 | -------------------------------------------------------------------------------- /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 GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 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 | 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 Lesser 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 | {description} 294 | Copyright (C) {year} {fullname} 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 along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PAPU 2 | 3 | VST / AU Gameboy PAPU emulation 4 | 5 | ![Build Windows](https://github.com/FigBug/PAPU/workflows/Build%20Windows/badge.svg "Build Windows") 6 | ![Build macOS](https://github.com/FigBug/PAPU/workflows/Build%20macOS/badge.svg "Build macOS") 7 | ![Build Linux](https://github.com/FigBug/PAPU/workflows/Build%20Linux/badge.svg "Build Linux") 8 | 9 | -------------------------------------------------------------------------------- /ci/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | PLUGIN="PAPU" 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/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FigBug/PAPU/e7c42c7d9056f21ec5bbdcb101908969effe9db0/plugin/Resources/icon.png -------------------------------------------------------------------------------- /plugin/Source/PadPluginEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "PluginProcessor.h" 2 | #include "PadPluginEditor.h" 3 | #include "BinaryData.h" 4 | 5 | #if JUCE_IOS 6 | 7 | //============================================================================== 8 | PAPUAudioProcessorEditor::PAPUAudioProcessorEditor (PAPUAudioProcessor& p) 9 | : ProcessorEditor (p, 80, 120), proc (p) 10 | { 11 | additionalProgramming = "Shay Green"; 12 | 13 | addAndMakeVisible (scope); 14 | addAndMakeVisible (keyboard); 15 | 16 | for (auto pp : p.getPluginParameters()) 17 | { 18 | gin::ParamComponent* c; 19 | if (pp->getUid().contains ("tune") || pp->getUid().contains ("fine") || pp->getUid().contains ("sweep")) 20 | c = new gin::Knob (pp, true); 21 | else 22 | c = pp->isOnOff() ? (gin::ParamComponent*)new gin::Switch (pp) : (gin::ParamComponent*)new gin::Knob (pp); 23 | 24 | addAndMakeVisible (c); 25 | controls.add (c); 26 | } 27 | 28 | setGridSize (13, 4); 29 | 30 | scope.setNumSamplesPerPixel (2); 31 | scope.setVerticalZoomFactor (3.0f); 32 | scope.setColour (gin::TriggeredScope::lineColourId, findColour (gin::PluginLookAndFeel::grey45ColourId)); 33 | scope.setColour (gin::TriggeredScope::traceColourId + 0, findColour (gin::PluginLookAndFeel::accentColourId)); 34 | scope.setColour (gin::TriggeredScope::envelopeColourId + 0, juce::Colours::transparentBlack); 35 | scope.setColour (gin::TriggeredScope::traceColourId + 1, findColour (gin::PluginLookAndFeel::accentColourId)); 36 | scope.setColour (gin::TriggeredScope::envelopeColourId + 1, juce::Colours::transparentBlack); 37 | 38 | keyboard.setKeyWidth (80); 39 | keyboard.setLowestVisibleKey (60); 40 | keyboard.setScrollButtonWidth (15); 41 | } 42 | 43 | PAPUAudioProcessorEditor::~PAPUAudioProcessorEditor() 44 | { 45 | } 46 | 47 | //============================================================================== 48 | void PAPUAudioProcessorEditor::paint (juce::Graphics& g) 49 | { 50 | ProcessorEditor::paint (g); 51 | } 52 | 53 | void PAPUAudioProcessorEditor::resized() 54 | { 55 | ProcessorEditor::resized(); 56 | 57 | for (int i = 0; i < 11; i++) 58 | { 59 | auto c = controls[i]; 60 | if (i == 0) 61 | c->setBounds (getGridArea (0, 0).removeFromTop (cy / 2).translated (0, 7)); 62 | else if (i == 1) 63 | c->setBounds (getGridArea (0, 0).removeFromBottom (cy / 2)); 64 | else 65 | c->setBounds (getGridArea (i - 1, 0)); 66 | 67 | } 68 | for (int i = 0; i < 9; i++) 69 | { 70 | auto c = controls[i + 11]; 71 | if (i == 0) 72 | c->setBounds (getGridArea (0, 1).removeFromTop (cy / 2).translated (0, 7)); 73 | else if (i == 1) 74 | c->setBounds (getGridArea (0, 1).removeFromBottom (cy / 2)); 75 | else if (i >= 7) 76 | c->setBounds (getGridArea (i + 1, 1)); 77 | else 78 | c->setBounds (getGridArea (i - 1, 1)); 79 | } 80 | for (int i = 0; i < 7; i++) 81 | { 82 | auto c = controls[i + 11 + 9]; 83 | if (i == 0) 84 | c->setBounds (getGridArea (0, 3).removeFromTop (cy / 2).translated (0, 7)); 85 | else if (i == 1) 86 | c->setBounds (getGridArea (0, 3).removeFromBottom (cy / 2)); 87 | else 88 | c->setBounds (getGridArea (i - 1, 3)); 89 | } 90 | for (int i = 0; i < 7; i++) 91 | { 92 | auto c = controls[i + 11 + 9 + 7]; 93 | if (i == 0) 94 | c->setBounds (getGridArea (0, 2).removeFromTop (cy / 2).translated (0, 7)); 95 | else if (i == 1) 96 | c->setBounds (getGridArea (0, 2).removeFromBottom (cy / 2)); 97 | else if (i >= 5) 98 | c->setBounds (getGridArea (i + 3, 2)); 99 | else if (i >= 3) 100 | c->setBounds (getGridArea (i + 1, 2)); 101 | else 102 | c->setBounds (getGridArea (i - 1, 2)); 103 | } 104 | 105 | int n = controls.size(); 106 | 107 | controls[n - 1]->setBounds (getGridArea (12, 3)); 108 | controls[n - 2]->setBounds (getGridArea (13, 3)); 109 | controls[n - 3]->setBounds (getGridArea (10, 3)); 110 | controls[n - 4]->setBounds (getGridArea (11, 3)); 111 | 112 | auto rcScope = getGridArea (10, 0, 3, 3).reduced (5); 113 | scope.setBounds (rcScope.withRight (getWidth() - inset - 5)); 114 | 115 | auto rcKeyboard = getLocalBounds().reduced (5); 116 | keyboard.setBounds (rcKeyboard.withTop (rcScope.getBottom() + 130)); 117 | } 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /plugin/Source/PadPluginEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "PluginProcessor.h" 5 | 6 | #if JUCE_IOS 7 | 8 | //============================================================================== 9 | /** 10 | */ 11 | class PAPUAudioProcessorEditor : public gin::ProcessorEditor 12 | { 13 | public: 14 | PAPUAudioProcessorEditor (PAPUAudioProcessor&); 15 | ~PAPUAudioProcessorEditor() override; 16 | 17 | //============================================================================== 18 | void resized() override; 19 | void paint (juce::Graphics& g) override; 20 | 21 | PAPUAudioProcessor& proc; 22 | 23 | gin::TriggeredScope scope { proc.fifo }; 24 | 25 | juce::MidiKeyboardComponent keyboard { proc.keyState, juce::MidiKeyboardComponent::horizontalKeyboard }; 26 | 27 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PAPUAudioProcessorEditor) 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /plugin/Source/PluginEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "PluginProcessor.h" 2 | #include "PluginEditor.h" 3 | #include "BinaryData.h" 4 | 5 | #if ! JUCE_IOS 6 | 7 | //============================================================================== 8 | PAPUAudioProcessorEditor::PAPUAudioProcessorEditor (PAPUAudioProcessor& p) 9 | : ProcessorEditor (p), proc (p) 10 | { 11 | addAndMakeVisible (&scope); 12 | 13 | for (auto pp : p.getPluginParameters()) 14 | { 15 | gin::ParamComponent* c; 16 | if (pp->getUid().contains ("tune") || pp->getUid().contains ("fine") || pp->getUid().contains ("sweep")) 17 | c = new gin::Knob (pp, true); 18 | else 19 | c = pp->isOnOff() ? (gin::ParamComponent*)new gin::Switch (pp) : (gin::ParamComponent*)new gin::Knob (pp); 20 | 21 | addAndMakeVisible (c); 22 | controls.add (c); 23 | } 24 | 25 | setGridSize (15, 4); 26 | 27 | scope.setNumSamplesPerPixel (2); 28 | scope.setVerticalZoomFactor (3.0f); 29 | scope.setColour (gin::TriggeredScope::lineColourId, findColour (gin::PluginLookAndFeel::grey45ColourId)); 30 | scope.setColour (gin::TriggeredScope::traceColourId + 0, findColour (gin::PluginLookAndFeel::accentColourId)); 31 | scope.setColour (gin::TriggeredScope::envelopeColourId + 0, juce::Colours::transparentBlack); 32 | scope.setColour (gin::TriggeredScope::traceColourId + 1, findColour (gin::PluginLookAndFeel::accentColourId)); 33 | scope.setColour (gin::TriggeredScope::envelopeColourId + 1, juce::Colours::transparentBlack); 34 | } 35 | 36 | PAPUAudioProcessorEditor::~PAPUAudioProcessorEditor() 37 | { 38 | } 39 | 40 | //============================================================================== 41 | void PAPUAudioProcessorEditor::paint (juce::Graphics& g) 42 | { 43 | ProcessorEditor::paint (g); 44 | } 45 | 46 | void PAPUAudioProcessorEditor::resized() 47 | { 48 | ProcessorEditor::resized(); 49 | 50 | for (int i = 0; i < 11; i++) 51 | { 52 | auto c = controls[i]; 53 | if (i == 0) 54 | c->setBounds (getGridArea (0, 0).removeFromTop (cy / 2).translated (0, 7)); 55 | else if (i == 1) 56 | c->setBounds (getGridArea (0, 0).removeFromBottom (cy / 2)); 57 | else 58 | c->setBounds (getGridArea (i - 1, 0)); 59 | 60 | } 61 | for (int i = 0; i < 9; i++) 62 | { 63 | auto c = controls[i + 11]; 64 | if (i == 0) 65 | c->setBounds (getGridArea (0, 1).removeFromTop (cy / 2).translated (0, 7)); 66 | else if (i == 1) 67 | c->setBounds (getGridArea (0, 1).removeFromBottom (cy / 2)); 68 | else if (i >= 7) 69 | c->setBounds (getGridArea (i + 1, 1)); 70 | else 71 | c->setBounds (getGridArea (i - 1, 1)); 72 | } 73 | for (int i = 0; i < 7; i++) 74 | { 75 | auto c = controls[i + 11 + 9]; 76 | if (i == 0) 77 | c->setBounds (getGridArea (0, 3).removeFromTop (cy / 2).translated (0, 7)); 78 | else if (i == 1) 79 | c->setBounds (getGridArea (0, 3).removeFromBottom (cy / 2)); 80 | else 81 | c->setBounds (getGridArea (i - 1, 3)); 82 | } 83 | for (int i = 0; i < 7; i++) 84 | { 85 | auto c = controls[i + 11 + 9 + 7]; 86 | if (i == 0) 87 | c->setBounds (getGridArea (0, 2).removeFromTop (cy / 2).translated (0, 7)); 88 | else if (i == 1) 89 | c->setBounds (getGridArea (0, 2).removeFromBottom (cy / 2)); 90 | else if (i >= 5) 91 | c->setBounds (getGridArea (i + 3, 2)); 92 | else if (i >= 3) 93 | c->setBounds (getGridArea (i + 1, 2)); 94 | else 95 | c->setBounds (getGridArea (i - 1, 2)); 96 | } 97 | 98 | int n = controls.size(); 99 | 100 | controls[n - 1]->setBounds (getGridArea (13, 3)); 101 | controls[n - 2]->setBounds (getGridArea (14, 3)); 102 | controls[n - 3]->setBounds (getGridArea (11, 3)); 103 | controls[n - 4]->setBounds (getGridArea (12, 3)); 104 | controls[n - 5]->setBounds (getGridArea (10, 3)); 105 | 106 | scope.setBounds (getGridArea (10, 0, 5, 3).reduced (5)); 107 | } 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /plugin/Source/PluginEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "PluginProcessor.h" 5 | 6 | #if ! JUCE_IOS 7 | 8 | //============================================================================== 9 | /** 10 | */ 11 | class PAPUAudioProcessorEditor : public gin::ProcessorEditor 12 | { 13 | public: 14 | PAPUAudioProcessorEditor (PAPUAudioProcessor&); 15 | ~PAPUAudioProcessorEditor() override; 16 | 17 | //============================================================================== 18 | void resized() override; 19 | void paint (juce::Graphics& g) override; 20 | 21 | PAPUAudioProcessor& proc; 22 | 23 | gin::TriggeredScope scope { proc.fifo }; 24 | 25 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PAPUAudioProcessorEditor) 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /plugin/Source/PluginProcessor.cpp: -------------------------------------------------------------------------------- 1 | #include "PluginProcessor.h" 2 | #include "PluginEditor.h" 3 | #include "PadPluginEditor.h" 4 | 5 | juce::String PAPUAudioProcessor::paramPulse1Sweep = "sweep1"; 6 | juce::String PAPUAudioProcessor::paramPulse1Shift = "shift1"; 7 | juce::String PAPUAudioProcessor::paramPulse1Duty = "duty1"; 8 | juce::String PAPUAudioProcessor::paramPulse1A = "A1"; 9 | juce::String PAPUAudioProcessor::paramPulse1R = "R1"; 10 | juce::String PAPUAudioProcessor::paramPulse1OL = "OL1"; 11 | juce::String PAPUAudioProcessor::paramPulse1OR = "OR1"; 12 | juce::String PAPUAudioProcessor::paramPulse1Tune = "tune1"; 13 | juce::String PAPUAudioProcessor::paramPulse1Fine = "fine1"; 14 | juce::String PAPUAudioProcessor::paramPulse1VibRate = "rate1"; 15 | juce::String PAPUAudioProcessor::paramPulse1VibAmt = "amt1"; 16 | juce::String PAPUAudioProcessor::paramPulse2Duty = "duty2"; 17 | juce::String PAPUAudioProcessor::paramPulse2A = "A2"; 18 | juce::String PAPUAudioProcessor::paramPulse2R = "R2"; 19 | juce::String PAPUAudioProcessor::paramPulse2OL = "OL2"; 20 | juce::String PAPUAudioProcessor::paramPulse2OR = "OR2"; 21 | juce::String PAPUAudioProcessor::paramPulse2Tune = "tune2"; 22 | juce::String PAPUAudioProcessor::paramPulse2Fine = "fine2"; 23 | juce::String PAPUAudioProcessor::paramPulse2VibRate = "rate2"; 24 | juce::String PAPUAudioProcessor::paramPulse2VibAmt = "amt2"; 25 | juce::String PAPUAudioProcessor::paramNoiseOL = "OLN"; 26 | juce::String PAPUAudioProcessor::paramNoiseOR = "ORL"; 27 | juce::String PAPUAudioProcessor::paramNoiseShift = "shiftN"; 28 | juce::String PAPUAudioProcessor::paramNoiseStep = "stepN"; 29 | juce::String PAPUAudioProcessor::paramNoiseRatio = "ratioN"; 30 | juce::String PAPUAudioProcessor::paramNoiseA = "AN"; 31 | juce::String PAPUAudioProcessor::paramNoiseR = "AR"; 32 | juce::String PAPUAudioProcessor::paramWaveOL = "OLW"; 33 | juce::String PAPUAudioProcessor::paramWaveOR = "ORW"; 34 | juce::String PAPUAudioProcessor::paramWaveWfm = "waveform"; 35 | juce::String PAPUAudioProcessor::paramWaveTune = "tunewave"; 36 | juce::String PAPUAudioProcessor::paramWaveFine = "finewave"; 37 | juce::String PAPUAudioProcessor::paramWaveVibRate = "ratewave"; 38 | juce::String PAPUAudioProcessor::paramWaveVibAmt = "amtwave"; 39 | juce::String PAPUAudioProcessor::paramChannelSplit = "channelsplit"; 40 | juce::String PAPUAudioProcessor::paramTreble = "trebeq"; 41 | juce::String PAPUAudioProcessor::paramBass = "bassf"; 42 | juce::String PAPUAudioProcessor::paramOutput = "output"; 43 | juce::String PAPUAudioProcessor::paramVoices = "param"; 44 | 45 | //============================================================================== 46 | PAPUEngine::PAPUEngine (PAPUAudioProcessor& p) 47 | : processor (p) 48 | { 49 | } 50 | 51 | void PAPUEngine::prepareToPlay (double sampleRate) 52 | { 53 | apu.treble_eq( -20.0 ); // lower values muffle it more 54 | buf.bass_freq( 461 ); // higher values simulate smaller speaker 55 | 56 | buf.clock_rate (4194304); 57 | buf.set_sample_rate (long (sampleRate)); 58 | 59 | apu.output (buf.center(), buf.left(), buf.right()); 60 | 61 | writeReg (0xff1A, 0x00, true); // reset 62 | // set pattern 63 | for (uint8_t s = 0; s < 16; s++) { 64 | uint8_t high = (wave_samples[waveIndex][s * 2]) & 0xff; 65 | uint8_t low = (wave_samples[waveIndex][(s * 2) + 1]) & 0xff; 66 | writeReg (0xff30 + s, (low | (high << 4)), true); 67 | } 68 | writeReg (0xff1A, 0x80, true); // enable 69 | 70 | writeReg (0xff26, 0x8f, true); 71 | 72 | vib1.setSampleRate(sampleRate); 73 | vib1Parameters.waveShape = gin::LFO::WaveShape::sine; 74 | vib1Parameters.frequency = 5.0; 75 | vib1Parameters.phase = 0.0; 76 | vib1Parameters.offset = 0.0; 77 | vib1Parameters.depth = 0.0; 78 | vib1Parameters.fade = 0.0; 79 | vib1Parameters.delay = 0.0; 80 | 81 | vib2.setParameters (vib2Parameters); 82 | vib2.reset(); 83 | vib2.setSampleRate(sampleRate); 84 | vib2Parameters.waveShape = gin::LFO::WaveShape::sine; 85 | vib2Parameters.frequency = 5.0; 86 | vib2Parameters.phase = 0.0; 87 | vib2Parameters.offset = 0.0; 88 | vib2Parameters.depth = 0.0; 89 | vib2Parameters.fade = 0.0; 90 | vib2Parameters.delay = 0.0; 91 | 92 | vib2.setParameters (vib2Parameters); 93 | vib2.reset(); 94 | 95 | vib3.setSampleRate(sampleRate); 96 | vib3Parameters.waveShape = gin::LFO::WaveShape::sine; 97 | vib3Parameters.frequency = 5.0; 98 | vib3Parameters.phase = 0.0; 99 | vib3Parameters.offset = 0.0; 100 | vib3Parameters.depth = 0.0; 101 | vib3Parameters.fade = 0.0; 102 | vib3Parameters.delay = 0.0; 103 | 104 | vib3.setParameters (vib3Parameters); 105 | vib3.reset(); 106 | } 107 | 108 | int PAPUEngine::parameterIntValue (const juce::String& uid) 109 | { 110 | return processor.parameterIntValue (uid); 111 | } 112 | 113 | void PAPUEngine::runVibrato(int todo) 114 | { 115 | vib1.process(todo); 116 | vib2.process(todo); 117 | vib3.process(todo); 118 | 119 | bool trigger1 = regCache[0xff14] & 0x80; 120 | freq1 = float (gin::getMidiNoteInHertz (vibNote1 + pitchBend + parameterIntValue (PAPUAudioProcessor::paramPulse1Tune) + (parameterIntValue (PAPUAudioProcessor::paramPulse1Fine) / 100.0f) + (vib1.getOutput() * 12.0))); 121 | uint16_t period1 = uint16_t (((4194304 / freq1) - 65536) / -32); 122 | writeReg (0xff13, period1 & 0xff, false); 123 | writeReg (0xff14, (trigger1 ? 0x80 : 0x00) | ((period1 >> 8) & 0x07), false); 124 | 125 | bool trigger2 = regCache[0xff19] & 0x80; 126 | freq2 = float (gin::getMidiNoteInHertz (vibNote2 + pitchBend + parameterIntValue (PAPUAudioProcessor::paramPulse2Tune) + (parameterIntValue (PAPUAudioProcessor::paramPulse2Fine) / 100.0f) + (vib2.getOutput() * 12.0))); 127 | uint16_t period2 = uint16_t (((4194304 / freq2) - 65536) / -32); 128 | writeReg (0xff18, period2 & 0xff, false); 129 | writeReg (0xff19, (trigger2 ? 0x80 : 0x00) | ((period2 >> 8) & 0x07), false); 130 | 131 | bool trigger3 = regCache[0xff1E] & 0x80; 132 | freq3 = float (gin::getMidiNoteInHertz (vibNote3 + pitchBend + parameterIntValue (PAPUAudioProcessor::paramWaveTune) + (parameterIntValue (PAPUAudioProcessor::paramWaveFine) / 100.0f) + (vib3.getOutput() * 12.0))); 133 | uint16_t period3 = uint16_t (((4194304 / freq3) - 65536) / -32); 134 | writeReg (0xff1D, period3 & 0xff, false); 135 | writeReg (0xff1E, (trigger3 ? 0x80 : 0x00) | ((period3 >> 8) & 0x07), false); 136 | } 137 | 138 | void PAPUEngine::runUntil (int& done, juce::AudioSampleBuffer& buffer, int pos) 139 | { 140 | int todo = juce::jmin (pos, buffer.getNumSamples()) - done; 141 | 142 | runVibrato(todo); 143 | 144 | while (todo > 0) 145 | { 146 | if (buf.samples_avail() > 0) 147 | { 148 | blip_sample_t out[1024]; 149 | 150 | int count = int (buf.read_samples (out, juce::jmin (todo, 1024 / 2, (int) buf.samples_avail()))); 151 | 152 | auto data0 = buffer.getWritePointer (0, done); 153 | auto data1 = buffer.getWritePointer (1, done); 154 | 155 | for (int i = 0; i < count; i++) 156 | { 157 | data0[i] += out[i * 2 + 0] / 32768.0f; 158 | data1[i] += out[i * 2 + 1] / 32768.0f; 159 | } 160 | 161 | done += count; 162 | todo -= count; 163 | } 164 | else 165 | { 166 | time = 0; 167 | 168 | 169 | bool stereo = apu.end_frame (1024); 170 | buf.end_frame (1024, stereo); 171 | } 172 | } 173 | } 174 | 175 | void PAPUEngine::runOscs (int curNote1, int curNote2, int curNote3, int curNote4, bool trigger1, bool trigger2, bool trigger3, bool trigger4) 176 | { 177 | // Ch 1 178 | if (curNote1 != -1) 179 | { 180 | vibNote1 = curNote1; 181 | 182 | uint8_t sweep = uint8_t (std::abs (parameterIntValue (PAPUAudioProcessor::paramPulse1Sweep))); 183 | uint8_t neg = parameterIntValue (PAPUAudioProcessor::paramPulse1Sweep) < 0; 184 | uint8_t shift = uint8_t (parameterIntValue (PAPUAudioProcessor::paramPulse1Shift)); 185 | 186 | writeReg (0xff10, (sweep << 4) | ((neg ? 1 : 0) << 3) | shift, trigger1); 187 | writeReg (0xff11, (parameterIntValue (PAPUAudioProcessor::paramPulse1Duty) << 6), trigger1); 188 | 189 | freq1 = float (gin::getMidiNoteInHertz (curNote1 + pitchBend + parameterIntValue (PAPUAudioProcessor::paramPulse1Tune) + parameterIntValue (PAPUAudioProcessor::paramPulse1Fine) / 100.0f)); 190 | uint16_t period1 = uint16_t (((4194304 / freq1) - 65536) / -32); 191 | writeReg (0xff13, period1 & 0xff, trigger1); 192 | uint8_t a1 = uint8_t (parameterIntValue (PAPUAudioProcessor::paramPulse1A)); 193 | writeReg (0xff12, a1 ? (0x00 | (1 << 3) | a1) : 0xf0, trigger1); 194 | writeReg (0xff14, (trigger1 ? 0x80 : 0x00) | ((period1 >> 8) & 0x07), trigger1); 195 | } 196 | else if (trigger1) 197 | { 198 | uint8_t r1 = uint8_t (parameterIntValue (PAPUAudioProcessor::paramPulse1R)); 199 | uint8_t a1 = uint8_t (parameterIntValue (PAPUAudioProcessor::paramPulse1A)); 200 | 201 | if (a1 == 0 && r1 != 0) 202 | { 203 | uint16_t period1 = uint16_t (((4194304 / freq1) - 65536) / -32); 204 | 205 | writeReg (0xff13, period1 & 0xff, trigger1); 206 | writeReg (0xff12, r1 ? (0xf0 | (0 << 3) | r1) : 0, trigger1); 207 | writeReg (0xff14, (trigger1 ? 0x80 : 0x00) | ((period1 >> 8) & 0x07), trigger1); 208 | } 209 | else 210 | { 211 | writeReg (0xff12, r1 ? (0xf0 | (0 << 3) | r1) : 0, trigger1); 212 | } 213 | } 214 | 215 | // Ch 2 216 | if (curNote2 != -1) 217 | { 218 | vibNote2 = curNote2; 219 | 220 | writeReg (0xff16, (parameterIntValue (PAPUAudioProcessor::paramPulse2Duty) << 6), trigger2); 221 | 222 | freq2 = float (gin::getMidiNoteInHertz (curNote2 + pitchBend + parameterIntValue (PAPUAudioProcessor::paramPulse2Tune) + parameterIntValue (PAPUAudioProcessor::paramPulse2Fine) / 100.0f)); 223 | uint16_t period2 = uint16_t (((4194304 / freq2) - 65536) / -32); 224 | writeReg (0xff18, period2 & 0xff, trigger2); 225 | uint8_t a2 = uint8_t (parameterIntValue (PAPUAudioProcessor::paramPulse2A)); 226 | writeReg (0xff17, a2 ? (0x00 | (1 << 3) | a2) : 0xf0, trigger2); 227 | writeReg (0xff19, (trigger2 ? 0x80 : 0x00) | ((period2 >> 8) & 0x07), trigger2); 228 | } 229 | else if (trigger2) 230 | { 231 | uint8_t r2 = uint8_t (parameterIntValue (PAPUAudioProcessor::paramPulse2R)); 232 | uint8_t a2 = uint8_t (parameterIntValue (PAPUAudioProcessor::paramPulse2A)); 233 | 234 | if (a2 == 0 && r2 != 0) 235 | { 236 | uint16_t period2 = uint16_t (((4194304 / freq2) - 65536) / -32); 237 | 238 | writeReg (0xff18, period2 & 0xff, trigger2); 239 | writeReg (0xff17, r2 ? (0xf0 | (0 << 3) | r2) : 0, trigger2); 240 | writeReg (0xff19, (trigger2 ? 0x80 : 0x00) | ((period2 >> 8) & 0x07), trigger2); 241 | } 242 | else 243 | { 244 | writeReg (0xff17, r2 ? (0xf0 | (0 << 3) | r2) : 0, trigger2); 245 | } 246 | } 247 | 248 | // Ch 3 249 | if (curNote3 != -1) 250 | { 251 | // printf("Trigger! chan2\n"); 252 | vibNote3 = curNote3; 253 | 254 | apu.resetStopWave(); 255 | freq3 = float (gin::getMidiNoteInHertz (curNote3 + pitchBend + parameterIntValue (PAPUAudioProcessor::paramWaveTune) + parameterIntValue (PAPUAudioProcessor::paramWaveFine) / 100.0f)); 256 | uint16_t period3 = uint16_t (-((65536 - 2048 * freq3)/freq3)); 257 | writeReg ( 0xff1D, period3 & 0xff, trigger3); // lower freq bits 258 | writeReg ( 0xff1C, 0x20, trigger3); 259 | writeReg ( 0xff1E, (trigger3 ? 0x80 : 0x00) | ((period3 >> 8) & 0x07), trigger3); // trigger, high freq bits 260 | } 261 | else if (trigger3) 262 | { 263 | apu.stopWave(); 264 | } 265 | 266 | // Noise 267 | if (curNote4 != -1) 268 | { 269 | uint8_t aN = uint8_t (parameterIntValue (PAPUAudioProcessor::paramNoiseA)); 270 | writeReg (0xff21, aN ? (0x00 | (1 << 3) | aN) : 0xf0, trigger4); 271 | writeReg (0xff22, (parameterIntValue (PAPUAudioProcessor::paramNoiseShift) << 4) | 272 | (parameterIntValue (PAPUAudioProcessor::paramNoiseStep) << 3) | 273 | (parameterIntValue (PAPUAudioProcessor::paramNoiseRatio)), trigger4); 274 | writeReg (0xff23, trigger4 ? 0x80 : 0x00, trigger4); 275 | } 276 | else if (trigger4) 277 | { 278 | uint8_t rN = uint8_t (parameterIntValue (PAPUAudioProcessor::paramNoiseR)); 279 | uint8_t aN = uint8_t (parameterIntValue (PAPUAudioProcessor::paramNoiseA)); 280 | 281 | if (aN == 0 && rN != 0) 282 | { 283 | writeReg (0xff21, rN ? (0xf0 | (0 << 3) | rN) : 0, trigger4); 284 | writeReg (0xff23, trigger4 ? 0x80 : 0x00, trigger4); 285 | } 286 | else 287 | { 288 | writeReg (0xff21, rN ? (0xf0 | (0 << 3) | rN) : 0, trigger4); 289 | } 290 | } 291 | } 292 | 293 | void PAPUEngine::writeReg (int reg, int value, bool force) 294 | { 295 | auto itr = regCache.find (reg); 296 | if (force || itr == regCache.end() || itr->second != value) 297 | { 298 | regCache[reg] = value; 299 | apu.write_register (clock(), gb_addr_t (reg), value); 300 | } 301 | } 302 | 303 | void PAPUEngine::processBlock (juce::AudioSampleBuffer& buffer, juce::MidiBuffer& midi) 304 | { 305 | uint16_t reg; 306 | 307 | reg = uint16_t (0x08 | parameterIntValue (PAPUAudioProcessor::paramOutput)); 308 | writeReg (0xff24, reg, false); 309 | 310 | reg = (parameterIntValue (PAPUAudioProcessor::paramPulse1OL) ? 0x10 : 0x00) | 311 | (parameterIntValue (PAPUAudioProcessor::paramPulse1OR) ? 0x01 : 0x00) | 312 | (parameterIntValue (PAPUAudioProcessor::paramPulse2OL) ? 0x20 : 0x00) | 313 | (parameterIntValue (PAPUAudioProcessor::paramPulse2OR) ? 0x02 : 0x00) | 314 | (parameterIntValue (PAPUAudioProcessor::paramWaveOL) ? 0x40 : 0x00) | 315 | (parameterIntValue (PAPUAudioProcessor::paramWaveOR) ? 0x04 : 0x00) | 316 | (parameterIntValue (PAPUAudioProcessor::paramNoiseOL) ? 0x80 : 0x00) | 317 | (parameterIntValue (PAPUAudioProcessor::paramNoiseOR) ? 0x08 : 0x00); 318 | 319 | writeReg (0xff25, reg, false); 320 | 321 | bool new_channelsplit = parameterIntValue (PAPUAudioProcessor::paramChannelSplit) ? true : false; 322 | if (new_channelsplit != channelsplit) 323 | { 324 | channelsplit = new_channelsplit; 325 | noteQueue1.clear(); 326 | noteQueue2.clear(); 327 | noteQueue3.clear(); 328 | noteQueue4.clear(); 329 | } 330 | 331 | int done = 0; 332 | runOscs (lastNote1, lastNote2, lastNote3, lastNote4, false, false, false, false); 333 | runUntil (done, buffer, 0); 334 | 335 | for (auto itr : midi) 336 | { 337 | auto msg = itr.getMessage(); 338 | int pos = itr.samplePosition; 339 | 340 | bool updateBend = false; 341 | runUntil (done, buffer, pos); 342 | 343 | if (msg.isNoteOn()) 344 | { 345 | if (msg.getChannel() == 1 || !channelsplit) noteQueue1.add (msg.getNoteNumber()); 346 | else if (msg.getChannel() == 2) noteQueue2.add (msg.getNoteNumber()); 347 | else if (msg.getChannel() == 3) noteQueue3.add (msg.getNoteNumber()); 348 | else if (msg.getChannel() == 4) noteQueue4.add (msg.getNoteNumber()); 349 | } 350 | else if (msg.isNoteOff()) 351 | { 352 | if (msg.getChannel() == 1 || !channelsplit) noteQueue1.removeFirstMatchingValue (msg.getNoteNumber()); 353 | else if (msg.getChannel() == 2) noteQueue2.removeFirstMatchingValue (msg.getNoteNumber()); 354 | else if (msg.getChannel() == 3) noteQueue3.removeFirstMatchingValue (msg.getNoteNumber()); 355 | else if (msg.getChannel() == 4) noteQueue4.removeFirstMatchingValue (msg.getNoteNumber()); 356 | } 357 | else if (msg.isAllNotesOff()) 358 | { 359 | noteQueue1.clear(); 360 | noteQueue2.clear(); 361 | noteQueue3.clear(); 362 | noteQueue4.clear(); 363 | } 364 | else if (msg.isPitchWheel()) 365 | { 366 | updateBend = true; 367 | pitchBend = (msg.getPitchWheelValue() - 8192) / 8192.0f * 2; 368 | } 369 | const int curNote1 = noteQueue1.size() > 0 ? noteQueue1.getLast() : -1; 370 | const int curNote2 = !channelsplit ? curNote1 : noteQueue2.size() > 0 ? noteQueue2.getLast() : -1; 371 | const int curNote3 = !channelsplit ? curNote1 : noteQueue3.size() > 0 ? noteQueue3.getLast() : -1; 372 | const int curNote4 = !channelsplit ? curNote1 : noteQueue4.size() > 0 ? noteQueue4.getLast() : -1; 373 | 374 | if (updateBend || 375 | lastNote1 != curNote1 || 376 | lastNote2 != curNote2 || 377 | lastNote3 != curNote3 || 378 | lastNote4 != curNote4) 379 | { 380 | if (!updateBend && (curNote1 != -1)) vib1.reset(); 381 | if (!updateBend && (curNote2 != -1)) vib2.reset(); 382 | if (!updateBend && (curNote3 != -1)) vib3.reset(); 383 | 384 | runOscs (curNote1, curNote2, curNote3, curNote4, 385 | lastNote1 != curNote1, 386 | lastNote2 != curNote2, 387 | lastNote3 != curNote3, 388 | lastNote4 != curNote4); 389 | lastNote1 = curNote1; 390 | lastNote2 = curNote2; 391 | lastNote3 = curNote3; 392 | lastNote4 = curNote4; 393 | } 394 | } 395 | 396 | int numSamples = buffer.getNumSamples(); 397 | runUntil (done, buffer, numSamples); 398 | } 399 | 400 | void PAPUEngine::prepareBlock (juce::AudioSampleBuffer& buffer) 401 | { 402 | uint16_t reg; 403 | 404 | reg = uint16_t (0x08 | parameterIntValue (PAPUAudioProcessor::paramOutput)); 405 | writeReg (0xff24, reg, false); 406 | 407 | reg = (parameterIntValue (PAPUAudioProcessor::paramPulse1OL) ? 0x10 : 0x00) | 408 | (parameterIntValue (PAPUAudioProcessor::paramPulse1OR) ? 0x01 : 0x00) | 409 | (parameterIntValue (PAPUAudioProcessor::paramPulse2OL) ? 0x20 : 0x00) | 410 | (parameterIntValue (PAPUAudioProcessor::paramPulse2OR) ? 0x02 : 0x00) | 411 | (parameterIntValue (PAPUAudioProcessor::paramWaveOL) ? 0x40 : 0x00) | 412 | (parameterIntValue (PAPUAudioProcessor::paramWaveOR) ? 0x04 : 0x00) | 413 | (parameterIntValue (PAPUAudioProcessor::paramNoiseOL) ? 0x80 : 0x00) | 414 | (parameterIntValue (PAPUAudioProcessor::paramNoiseOR) ? 0x08 : 0x00); 415 | 416 | writeReg (0xff25, reg, false); 417 | 418 | int done = 0; 419 | runOscs (lastNote1, lastNote2, lastNote3, lastNote4, false, false, false, false); 420 | runUntil (done, buffer, 0); 421 | 422 | jassert (done == 0); 423 | } 424 | 425 | void PAPUEngine::handleMessage (const juce::MidiMessage& msg) 426 | { 427 | bool updateBend = false; 428 | 429 | bool new_channelsplit = parameterIntValue (PAPUAudioProcessor::paramChannelSplit) ? true : false; 430 | if (new_channelsplit != channelsplit) 431 | { 432 | channelsplit = new_channelsplit; 433 | noteQueue1.clear(); 434 | noteQueue2.clear(); 435 | noteQueue3.clear(); 436 | noteQueue4.clear(); 437 | } 438 | 439 | if (msg.isNoteOn()) 440 | { 441 | if (msg.getChannel() == 1 || !channelsplit) noteQueue1.add (msg.getNoteNumber()); 442 | else if (msg.getChannel() == 2) noteQueue2.add (msg.getNoteNumber()); 443 | else if (msg.getChannel() == 3) noteQueue3.add (msg.getNoteNumber()); 444 | else if (msg.getChannel() == 4) noteQueue4.add (msg.getNoteNumber()); 445 | } 446 | else if (msg.isNoteOff()) 447 | { 448 | if (msg.getChannel() == 1 || !channelsplit) noteQueue1.removeFirstMatchingValue (msg.getNoteNumber()); 449 | else if (msg.getChannel() == 2) noteQueue2.removeFirstMatchingValue (msg.getNoteNumber()); 450 | else if (msg.getChannel() == 3) noteQueue3.removeFirstMatchingValue (msg.getNoteNumber()); 451 | else if (msg.getChannel() == 4) noteQueue4.removeFirstMatchingValue (msg.getNoteNumber()); 452 | } 453 | else if (msg.isAllNotesOff()) 454 | { 455 | noteQueue1.clear(); 456 | noteQueue2.clear(); 457 | noteQueue3.clear(); 458 | noteQueue4.clear(); 459 | } 460 | else if (msg.isPitchWheel()) 461 | { 462 | updateBend = true; 463 | pitchBend = (msg.getPitchWheelValue() - 8192) / 8192.0f * 2; 464 | } 465 | const int curNote1 = noteQueue1.size() > 0 ? noteQueue1.getLast() : -1; 466 | const int curNote2 = !channelsplit ? curNote1 : noteQueue2.size() > 0 ? noteQueue2.getLast() : -1; 467 | const int curNote3 = !channelsplit ? curNote1 : noteQueue3.size() > 0 ? noteQueue3.getLast() : -1; 468 | const int curNote4 = !channelsplit ? curNote1 : noteQueue4.size() > 0 ? noteQueue4.getLast() : -1; 469 | 470 | if (updateBend || 471 | lastNote1 != curNote1 || 472 | lastNote2 != curNote2 || 473 | lastNote3 != curNote3 || 474 | lastNote4 != curNote4) 475 | { 476 | if (!updateBend && (curNote1 != -1)) vib1.reset(); 477 | if (!updateBend && (curNote2 != -1)) vib2.reset(); 478 | if (!updateBend && (curNote3 != -1)) vib3.reset(); 479 | 480 | runOscs (curNote1, curNote2, curNote3, curNote4, 481 | lastNote1 != curNote1, 482 | lastNote2 != curNote2, 483 | lastNote3 != curNote3, 484 | lastNote4 != curNote4); 485 | lastNote1 = curNote1; 486 | lastNote2 = curNote2; 487 | lastNote3 = curNote3; 488 | lastNote4 = curNote4; 489 | } 490 | } 491 | 492 | void PAPUEngine::setWave(uint8_t index) 493 | { 494 | if (index == waveIndex) 495 | return; 496 | 497 | waveIndex = index; 498 | writeReg (0xff1A, 0x00, true); // reset 499 | // set pattern 500 | for (uint8_t s = 0; s < 16; s++) { 501 | uint8_t high = (wave_samples[waveIndex][s * 2]) & 0xff; 502 | uint8_t low = (wave_samples[waveIndex][(s * 2) + 1]) & 0xff; 503 | writeReg (0xff30 + s, (low | (high << 4)), true); 504 | } 505 | writeReg (0xff1A, 0x80, true); // enable 506 | } 507 | 508 | //============================================================================== 509 | static juce::String percentTextFunction (const gin::Parameter& p, float v) 510 | { 511 | return juce::String::formatted("%.0f%%", v / p.getUserRangeEnd() * 100); 512 | } 513 | 514 | static juce::String enableTextFunction (const gin::Parameter&, float v) 515 | { 516 | return v > 0.0f ? "On" : "Off"; 517 | } 518 | 519 | static juce::String dutyTextFunction (const gin::Parameter&, float v) 520 | { 521 | const int duty = int (v); 522 | switch (duty) 523 | { 524 | case 0: return "12.5%"; 525 | case 1: return "25%"; 526 | case 2: return "50%"; 527 | case 3: return "75%"; 528 | } 529 | return ""; 530 | } 531 | 532 | static juce::String arTextFunction (const gin::Parameter&, float v) 533 | { 534 | return juce::String::formatted("%.1f s", v * 1.0/64.0 * 16); 535 | } 536 | 537 | static juce::String hzTextFunction (const gin::Parameter&, float v) 538 | { 539 | return juce::String::formatted("%.1f Hz", v); 540 | } 541 | 542 | static juce::String stTextFunction (const gin::Parameter&, float v) 543 | { 544 | juce::String str; 545 | switch (abs (int (v))) 546 | { 547 | case 0: str = "Off"; break; 548 | case 1: str = "7.8 ms"; break; 549 | case 2: str = "15.6 ms"; break; 550 | case 3: str = "23.4 ms"; break; 551 | case 4: str = "31.3 ms"; break; 552 | case 5: str = "39.1 ms"; break; 553 | case 6: str = "46.9 ms"; break; 554 | case 7: str = "54.7 ms"; break; 555 | } 556 | 557 | if (v < 0) 558 | str = "-" + str; 559 | 560 | return str; 561 | } 562 | 563 | static juce::String stepTextFunction (const gin::Parameter&, float v) 564 | { 565 | return v > 0.0f ? "15" : "7"; 566 | } 567 | 568 | static juce::String intTextFunction (const gin::Parameter&, float v) 569 | { 570 | return juce::String (int (v)); 571 | } 572 | 573 | //============================================================================== 574 | PAPUAudioProcessor::PAPUAudioProcessor() 575 | : gin::Processor (false, gin::ProcessorOptions().withAdditionalCredits({"Shay Green"})) 576 | { 577 | addExtParam (paramPulse1OL, "Pulse 1 OL", "Left", "", { 0.0f, 1.0f, 1.0f, 1.0f }, 1.0f, 0.0f, enableTextFunction); 578 | addExtParam (paramPulse1OR, "Pulse 1 OR", "Right", "", { 0.0f, 1.0f, 1.0f, 1.0f }, 1.0f, 0.0f, enableTextFunction); 579 | addExtParam (paramPulse1Duty, "Pulse 1 Duty", "PW", "", { 0.0f, 3.0f, 1.0f, 1.0f }, 0.0f, 0.0f, dutyTextFunction); 580 | addExtParam (paramPulse1A, "Pulse 1 A", "Attack", "", { 0.0f, 7.0f, 1.0f, 1.0f }, 1.0f, 0.0f, arTextFunction); 581 | addExtParam (paramPulse1R, "Pulse 1 R", "Release", "", { 0.0f, 7.0f, 1.0f, 1.0f }, 1.0f, 0.0f, arTextFunction); 582 | addExtParam (paramPulse1Tune, "Pulse 1 Tune", "Tune", "", { -48.0f, 48.0f, 1.0f, 1.0f }, 0.0f, 0.0f, intTextFunction); 583 | addExtParam (paramPulse1Fine, "Pulse 1 Tune Fine", "Fine", "", { -100.0f, 100.0f, 1.0f, 1.0f }, 0.0f, 0.0f, intTextFunction); 584 | addExtParam (paramPulse1Sweep, "Pulse 1 Sweep", "Sweep", "", { -7.0f, 7.0f, 1.0f, 1.0f }, 0.0f, 0.0f, stTextFunction); 585 | addExtParam (paramPulse1Shift, "Pulse 1 Shift", "Shift", "", { 0.0f, 7.0f, 1.0f, 1.0f }, 0.0f, 0.0f, intTextFunction); 586 | addExtParam (paramPulse1VibRate, "Pulse 1 Vib Rate", "Rate", "", { 0.0f, 15.0f, 0.1f, 1.0f }, 5.0f, 0.0f, hzTextFunction); 587 | addExtParam (paramPulse1VibAmt, "Pulse 1 Vib Amt", "Amount", "", { 0.0f, 100.0f, 0.5f, 1.0f }, 0.0f, 0.0f, percentTextFunction); 588 | addExtParam (paramPulse2OL, "Pulse 2 OL", "Left", "", { 0.0f, 1.0f, 1.0f, 1.0f }, 0.0f, 0.0f, enableTextFunction); 589 | addExtParam (paramPulse2OR, "Pulse 2 OR", "Right", "", { 0.0f, 1.0f, 1.0f, 1.0f }, 0.0f, 0.0f, enableTextFunction); 590 | addExtParam (paramPulse2Duty, "Pulse 2 Duty", "PW", "", { 0.0f, 3.0f, 1.0f, 1.0f }, 0.0f, 0.0f, dutyTextFunction); 591 | addExtParam (paramPulse2A, "Pulse 2 A", "Attack", "", { 0.0f, 7.0f, 1.0f, 1.0f }, 1.0f, 0.0f, arTextFunction); 592 | addExtParam (paramPulse2R, "Pulse 2 R", "Release", "", { 0.0f, 7.0f, 1.0f, 1.0f }, 1.0f, 0.0f, arTextFunction); 593 | addExtParam (paramPulse2Tune, "Pulse 2 Tune", "Tune", "", { -48.0f, 48.0f, 1.0f, 1.0f }, 0.0f, 0.0f, intTextFunction); 594 | addExtParam (paramPulse2Fine, "Pulse 2 Tune Fine", "Fine", "", { -100.0f, 100.0f, 1.0f, 1.0f }, 0.0f, 0.0f, intTextFunction); 595 | addExtParam (paramPulse2VibRate, "Pulse 2 Vib Rate", "Rate", "", { 0.0f, 15.0f, 0.1f, 1.0f }, 5.0f, 0.0f, hzTextFunction); 596 | addExtParam (paramPulse2VibAmt, "Pulse 2 Vib Amt", "Amount", "", { 0.0f, 100.0f, 0.5f, 1.0f }, 0.0f, 0.0f, percentTextFunction); 597 | addExtParam (paramNoiseOL, "Noise OL", "Left", "", { 0.0f, 1.0f, 1.0f, 1.0f }, 0.0f, 0.0f, enableTextFunction); 598 | addExtParam (paramNoiseOR, "Noise OR", "Right", "", { 0.0f, 1.0f, 1.0f, 1.0f }, 0.0f, 0.0f, enableTextFunction); 599 | addExtParam (paramNoiseA, "Noise A", "Attack", "", { 0.0f, 7.0f, 1.0f, 1.0f }, 1.0f, 0.0f, arTextFunction); 600 | addExtParam (paramNoiseR, "Noise R", "Release", "", { 0.0f, 7.0f, 1.0f, 1.0f }, 1.0f, 0.0f, arTextFunction); 601 | addExtParam (paramNoiseShift, "Noise Shift", "Shift", "", { 0.0f, 13.0f, 1.0f, 1.0f }, 0.0f, 0.0f, intTextFunction); 602 | addExtParam (paramNoiseStep, "Noise Step", "Steps", "", { 0.0f, 1.0f, 1.0f, 1.0f }, 0.0f, 0.0f, stepTextFunction); 603 | addExtParam (paramNoiseRatio, "Noise Ratio", "Ratio", "", { 0.0f, 7.0f, 1.0f, 1.0f }, 0.0f, 0.0f, intTextFunction); 604 | addExtParam (paramWaveOL, "Wave OL", "Left", "", { 0.0f, 1.0f, 1.0f, 1.0f }, 0.0f, 0.0f, enableTextFunction); 605 | addExtParam (paramWaveOR, "Wave OR", "Right", "", { 0.0f, 1.0f, 1.0f, 1.0f }, 0.0f, 0.0f, enableTextFunction); 606 | addExtParam (paramWaveWfm, "Waveform", "Waveform", "", { 0.0f, 14.0f, 1.0f, 1.0f }, 0.0f, 0.0f, intTextFunction); 607 | addExtParam (paramWaveTune, "Wave Tune", "Tune", "", { -48.0f, 48.0f, 1.0f, 1.0f }, 0.0f, 0.0f, intTextFunction); 608 | addExtParam (paramWaveFine, "Wave Tune Fine", "Fine", "", { -100.0f, 100.0f, 1.0f, 1.0f }, 0.0f, 0.0f, intTextFunction); 609 | addExtParam (paramWaveVibRate, "Wave Vib Rate", "Rate", "", { 0.0f, 15.0f, 0.1f, 1.0f }, 5.0f, 0.0f, hzTextFunction); 610 | addExtParam (paramWaveVibAmt, "Wave Vib Amt", "Amount", "", { 0.0f, 100.0f, 0.5f, 1.0f }, 0.0f, 0.0f, percentTextFunction); 611 | addExtParam (paramChannelSplit, "Channel split", "Channel split", "", { 0.0f, 1.0f, 1.0f, 1.0f }, 0.0f, 0.0f, enableTextFunction); 612 | addExtParam (paramTreble, "Treble EQ", "Treble", "", { -50.0f, 50.0f, 1.0f, 1.0f }, -30.0f, 0.0f, intTextFunction); 613 | addExtParam (paramBass, "Bass frequency", "Bass", "", { 15.0f, 600.0f, 1.0f, 1.0f }, 461.0f, 0.0f, intTextFunction); 614 | addExtParam (paramOutput, "Output", "Output", "", { 0.0f, 7.0f, 1.0f, 1.0f }, 7.0f, 0.0f, percentTextFunction); 615 | addExtParam (paramVoices, "Voices", "Voices", "", { 1.0f, 8.0f, 1.0f, 1.0f }, 1.0f, 0.0f, intTextFunction); 616 | 617 | for (int i = 0; i < 16; i++) 618 | papus.add (new PAPUEngine (*this)); 619 | 620 | init(); 621 | } 622 | 623 | PAPUAudioProcessor::~PAPUAudioProcessor() 624 | { 625 | } 626 | 627 | //============================================================================== 628 | void PAPUAudioProcessor::prepareToPlay (double sampleRate, int /*samplesPerBlock*/) 629 | { 630 | for (auto p : papus) 631 | p->prepareToPlay (sampleRate); 632 | } 633 | 634 | void PAPUAudioProcessor::releaseResources() 635 | { 636 | } 637 | 638 | void PAPUAudioProcessor::processBlock (juce::AudioSampleBuffer& buffer, juce::MidiBuffer& midi) 639 | { 640 | int numSamples = buffer.getNumSamples(); 641 | buffer.clear(); 642 | 643 | #if JUCE_IOS 644 | keyState.processNextMidiBuffer (midi, 0, numSamples, true); 645 | #endif 646 | 647 | auto new_waveIndex = uint8_t (parameterIntValue (PAPUAudioProcessor::paramWaveWfm)); 648 | if (new_waveIndex != papus[0]->waveIndex) 649 | for (int i = 0; i < 16; i++) 650 | papus[i]->setWave(new_waveIndex); 651 | 652 | float new_treble = parameterValue (PAPUAudioProcessor::paramTreble); 653 | if (new_treble != papus[0]->treble) 654 | { 655 | for (int i = 0; i < 16; i++) 656 | { 657 | papus[i]->treble = new_treble; 658 | papus[i]->getApu()->treble_eq (new_treble); 659 | } 660 | } 661 | 662 | int new_bass = parameterIntValue (PAPUAudioProcessor::paramBass); 663 | if (new_bass != papus[0]->bass) 664 | { 665 | for (int i = 0; i < 16; i++) 666 | { 667 | papus[i]->bass = new_bass; 668 | papus[i]->getBuffer()->bass_freq (new_bass); 669 | } 670 | } 671 | 672 | float new_vib1 = 0.25f * parameterValue (PAPUAudioProcessor::paramPulse1VibAmt) / 100.0f; 673 | if (new_vib1 != papus[0]->vib1Parameters.depth) 674 | { 675 | for (int i = 0; i < 16; i++) 676 | { 677 | papus[i]->vib1Parameters.depth = new_vib1; 678 | papus[i]->vib1.setParameters (papus[i]->vib1Parameters); 679 | } 680 | } 681 | 682 | float new_rate1 = parameterValue (PAPUAudioProcessor::paramPulse1VibRate); 683 | if (new_rate1 != papus[0]->vib1Parameters.frequency) 684 | { 685 | for (int i = 0; i < 16; i++) 686 | { 687 | papus[i]->vib1Parameters.frequency = new_rate1; 688 | papus[i]->vib1.setParameters (papus[i]->vib1Parameters); 689 | } 690 | } 691 | 692 | float new_vib2 = 0.25f * parameterValue (PAPUAudioProcessor::paramPulse2VibAmt) / 100.0f; 693 | if (new_vib2 != papus[0]->vib2Parameters.depth) 694 | { 695 | for (int i = 0; i < 16; i++) 696 | { 697 | papus[i]->vib2Parameters.depth = new_vib2; 698 | papus[i]->vib2.setParameters (papus[i]->vib2Parameters); 699 | } 700 | } 701 | 702 | float new_rate2 = parameterValue (PAPUAudioProcessor::paramPulse2VibRate); 703 | if (new_rate2 != papus[0]->vib2Parameters.frequency) 704 | { 705 | for (int i = 0; i < 16; i++) 706 | { 707 | papus[i]->vib2Parameters.frequency = new_rate2; 708 | papus[i]->vib2.setParameters (papus[i]->vib2Parameters); 709 | } 710 | } 711 | 712 | float new_vib3 = 0.25f * parameterValue (PAPUAudioProcessor::paramWaveVibAmt) / 100.0f; 713 | if (new_vib3 != papus[0]->vib3Parameters.depth) 714 | { 715 | for (int i = 0; i < 16; i++) 716 | { 717 | papus[i]->vib3Parameters.depth = new_vib3; 718 | papus[i]->vib3.setParameters (papus[i]->vib3Parameters); 719 | } 720 | } 721 | 722 | float new_rate3 = parameterValue (PAPUAudioProcessor::paramWaveVibRate); 723 | if (new_rate3 != papus[0]->vib3Parameters.frequency) 724 | { 725 | for (int i = 0; i < 16; i++) 726 | { 727 | papus[i]->vib3Parameters.frequency = new_rate3; 728 | papus[i]->vib3.setParameters (papus[i]->vib3Parameters); 729 | } 730 | } 731 | 732 | int voices = parameterIntValue (paramVoices); 733 | 734 | if (voices == 1) 735 | { 736 | papus[0]->processBlock (buffer, midi); 737 | } 738 | else 739 | { 740 | int done = 0; 741 | 742 | for (int i = 0; i < voices; i++) 743 | papus[i]->prepareBlock (buffer); 744 | 745 | bool channelsplit = parameterIntValue (PAPUAudioProcessor::paramChannelSplit) ? true : false; 746 | 747 | for (auto itr : midi) 748 | { 749 | auto msg = itr.getMessage(); 750 | int pos = itr.samplePosition; 751 | 752 | runUntil (done, buffer, pos); 753 | 754 | if (msg.isNoteOn()) 755 | { 756 | if (auto voice = findFreeVoice(!channelsplit ? 1 : msg.getChannel())) 757 | voice->handleMessage (msg); 758 | } 759 | else if (msg.isNoteOff()) 760 | { 761 | if (auto voice = findVoiceForNote (msg.getNoteNumber(), !channelsplit ? 1 : msg.getChannel())) 762 | voice->handleMessage (msg); 763 | } 764 | else if (msg.isAllNotesOff()) 765 | { 766 | for (int i = 0; i < voices; i++) 767 | papus[i]->handleMessage (msg); 768 | } 769 | else if (msg.isPitchWheel()) 770 | { 771 | for (int i = 0; i < voices; i++) 772 | papus[i]->handleMessage (msg); 773 | } 774 | } 775 | 776 | runUntil (done, buffer, numSamples); 777 | } 778 | 779 | if (fifo.getFreeSpace() >= numSamples) 780 | { 781 | auto dataL = buffer.getReadPointer (0); 782 | auto dataR = buffer.getReadPointer (1); 783 | 784 | auto mono = (float*) alloca (size_t (numSamples) * sizeof (float)); 785 | 786 | for (int i = 0; i < numSamples; i++) 787 | mono[i] = (dataL[i] + dataR[i]) / 2.0f; 788 | 789 | fifo.writeMono (mono, numSamples); 790 | } 791 | } 792 | 793 | void PAPUAudioProcessor::runUntil (int& done, juce::AudioSampleBuffer& buffer, int pos) 794 | { 795 | int todo = juce::jmin (pos, buffer.getNumSamples()) - done; 796 | 797 | int voices = parameterIntValue (paramVoices); 798 | for (int i = 0; i < voices; i++) 799 | { 800 | int doneCopy = done; 801 | papus[i]->runUntil (doneCopy, buffer, pos); 802 | } 803 | 804 | done += todo; 805 | } 806 | 807 | PAPUEngine* PAPUAudioProcessor::findFreeVoice(int channel) 808 | { 809 | int voices = parameterIntValue (paramVoices); 810 | for (int i = 0; i < voices; i++) 811 | { 812 | int vidx = (nextVoice + i) % voices; 813 | if (papus[vidx]->getNote(channel) == -1) 814 | { 815 | nextVoice = (nextVoice + 1) % voices; 816 | return papus[vidx]; 817 | } 818 | } 819 | return nullptr; 820 | } 821 | 822 | PAPUEngine* PAPUAudioProcessor::findVoiceForNote (int note, int channel) 823 | { 824 | int voices = parameterIntValue (paramVoices); 825 | for (int i = 0; i < voices; i++) 826 | if (papus[i]->getNote(channel) == note) 827 | return papus[i]; 828 | 829 | return nullptr; 830 | } 831 | 832 | //============================================================================== 833 | bool PAPUAudioProcessor::hasEditor() const 834 | { 835 | return true; 836 | } 837 | 838 | juce::AudioProcessorEditor* PAPUAudioProcessor::createEditor() 839 | { 840 | return new PAPUAudioProcessorEditor (*this); 841 | } 842 | 843 | //============================================================================== 844 | // This creates new instances of the plugin.. 845 | juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() 846 | { 847 | return new PAPUAudioProcessor(); 848 | } 849 | -------------------------------------------------------------------------------- /plugin/Source/PluginProcessor.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 processor. 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include "gb_apu/Gb_Apu.h" 15 | #include "gb_apu/Multi_Buffer.h" 16 | 17 | // Source: https://github.com/dannye/pokered-crysaudio/blob/master/crysaudio/wave_samples.asm 18 | static uint8_t wave_samples[15][32] = { 19 | { 0, 2, 4, 6, 8, 10, 12, 14, 15, 15, 15, 14, 14, 13, 13, 12, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 3, 2, 2, 1, 1 }, 20 | { 0, 2, 4, 6, 8, 10, 12, 14, 14, 15, 15, 15, 15, 14, 14, 14, 13, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1 }, 21 | { 1, 3, 6, 9, 11, 13, 14, 14, 14, 14, 15, 15, 15, 15, 14, 13, 13, 14, 15, 15, 15, 15, 14, 14, 14, 14, 13, 11, 9, 6, 3, 1 }, 22 | { 0, 2, 4, 6, 8, 10, 12, 13, 14, 15, 15, 14, 13, 14, 15, 15, 14, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, 23 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 13, 14, 14, 15, 7, 7, 15, 14, 14, 13, 12, 10, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, 24 | { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 3, 3, 2, 2, 1, 1, 15, 15, 14, 14, 12, 12, 10, 10, 8, 8, 10, 10, 12, 12, 14, 14 }, 25 | { 0, 2, 4, 6, 8, 10, 12, 14, 12, 11, 10, 9, 8, 7, 6, 5, 15, 15, 15, 14, 14, 13, 13, 12, 4, 4, 3, 3, 2, 2, 1, 1 }, 26 | {12, 0, 10, 9, 8, 7, 15, 5, 15, 15, 15, 14, 14, 13, 13, 12, 4, 4, 3, 3, 2, 2, 15, 1, 0, 2, 4, 6, 8, 10, 12, 14 }, 27 | { 4, 4, 3, 3, 2, 2, 1, 15, 0, 0, 4, 6, 8, 10, 12, 14, 15, 8, 15, 14, 14, 13, 13, 12, 12, 11, 10, 9, 8, 7, 6, 5 }, 28 | { 1, 1, 0, 0, 0, 0, 0, 8, 0, 0, 1, 3, 5, 7, 9, 10, 11, 4, 11, 10, 10, 9, 9, 8, 8, 7, 6, 5, 4, 3, 2, 1 }, 29 | { 7, 9, 11, 13, 15, 15, 15, 15, 15, 15, 15, 15, 15, 13, 11, 9, 7, 5, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5 }, 30 | { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15 }, 31 | { 4, 6, 8, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 10, 8, 6, 4, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2 }, 32 | { 7, 10, 13, 15, 15, 15, 13, 10, 7, 4, 1, 0, 0, 0, 1, 4, 7, 10, 13, 15, 15, 15, 13, 10, 7, 4, 1, 0, 0, 0, 1, 4 }, 33 | {14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}; 34 | 35 | //============================================================================== 36 | class PAPUAudioProcessor; 37 | class PAPUEngine 38 | { 39 | public: 40 | PAPUEngine (PAPUAudioProcessor& p); 41 | 42 | void prepareToPlay (double sampleRate); 43 | void processBlock (juce::AudioSampleBuffer& buffer, juce::MidiBuffer& midi); 44 | 45 | void prepareBlock (juce::AudioSampleBuffer& buffer); 46 | void handleMessage (const juce::MidiMessage& msg); 47 | void runUntil (int& done, juce::AudioSampleBuffer& buffer, int pos); 48 | 49 | int getNote(int note) 50 | { 51 | if (note == 1) {return lastNote1;} 52 | else if (note == 2) {return lastNote2;} 53 | else if (note == 3) {return lastNote3;} 54 | else if (note == 4) {return lastNote4;} 55 | else {return -1;} 56 | } 57 | int getNote1() { return lastNote1; } 58 | int getNote2() { return lastNote2; } 59 | int getNote3() { return lastNote3; } 60 | int getNote4() { return lastNote4; } 61 | 62 | uint8_t waveIndex = 0; 63 | void setWave(uint8_t index); 64 | float treble = -1.0; 65 | int bass = -1; 66 | inline Gb_Apu* getApu() { return &apu; } 67 | inline Stereo_Buffer* getBuffer() { return &buf; } 68 | 69 | gin::LFO::Parameters vib1Parameters; 70 | gin::LFO::Parameters vib2Parameters; 71 | gin::LFO::Parameters vib3Parameters; 72 | gin::LFO vib1; 73 | gin::LFO vib2; 74 | gin::LFO vib3; 75 | int vibNote1, vibNote2, vibNote3; 76 | 77 | bool channelsplit = false; 78 | 79 | private: 80 | int parameterIntValue (const juce::String& uid); 81 | void runOscs (int curNote1, int curNote2, int curNote3, int curNote4, bool trigger1, bool trigger2, bool trigger3, bool trigger4); 82 | 83 | PAPUAudioProcessor& processor; 84 | 85 | int lastNote1 = -1; 86 | int lastNote2 = -1; 87 | int lastNote3 = -1; 88 | int lastNote4 = -1; 89 | double pitchBend = 0; 90 | juce::Array noteQueue1; 91 | juce::Array noteQueue2; 92 | juce::Array noteQueue3; 93 | juce::Array noteQueue4; 94 | float freq1 = 0.0f, freq2 = 0.0f, freq3 = 0.0f; 95 | 96 | void runVibrato(int todo); 97 | 98 | Gb_Apu apu; 99 | Stereo_Buffer buf; 100 | 101 | blip_time_t time = 0; 102 | 103 | blip_time_t clock() { return time += 4; } 104 | 105 | void writeReg (int reg, int value, bool force); 106 | 107 | std::map regCache; 108 | }; 109 | 110 | //============================================================================== 111 | /** 112 | */ 113 | class PAPUAudioProcessorEditor; 114 | class PAPUAudioProcessor : public gin::Processor 115 | { 116 | public: 117 | //============================================================================== 118 | PAPUAudioProcessor(); 119 | ~PAPUAudioProcessor() override; 120 | 121 | //============================================================================== 122 | void prepareToPlay (double sampleRate, int samplesPerBlock) override; 123 | void releaseResources() override; 124 | 125 | void processBlock (juce::AudioSampleBuffer&, juce::MidiBuffer&) override; 126 | 127 | //============================================================================== 128 | juce::AudioProcessorEditor* createEditor() override; 129 | bool hasEditor() const override; 130 | 131 | //============================================================================== 132 | static juce::String paramPulse1OL; 133 | static juce::String paramPulse1OR; 134 | static juce::String paramPulse1Duty; 135 | static juce::String paramPulse1A; 136 | static juce::String paramPulse1R; 137 | static juce::String paramPulse1Tune; 138 | static juce::String paramPulse1Fine; 139 | static juce::String paramPulse1Sweep; 140 | static juce::String paramPulse1Shift; 141 | static juce::String paramPulse1VibRate; 142 | static juce::String paramPulse1VibAmt; 143 | static juce::String paramPulse2OL; 144 | static juce::String paramPulse2OR; 145 | static juce::String paramPulse2Duty; 146 | static juce::String paramPulse2A; 147 | static juce::String paramPulse2R; 148 | static juce::String paramPulse2Tune; 149 | static juce::String paramPulse2Fine; 150 | static juce::String paramPulse2VibRate; 151 | static juce::String paramPulse2VibAmt; 152 | static juce::String paramNoiseOL; 153 | static juce::String paramNoiseOR; 154 | static juce::String paramNoiseA; 155 | static juce::String paramNoiseR; 156 | static juce::String paramNoiseShift; 157 | static juce::String paramNoiseStep; 158 | static juce::String paramNoiseRatio; 159 | static juce::String paramWaveOL; 160 | static juce::String paramWaveOR; 161 | static juce::String paramWaveWfm; 162 | static juce::String paramWaveTune; 163 | static juce::String paramWaveFine; 164 | static juce::String paramWaveVibRate; 165 | static juce::String paramWaveVibAmt; 166 | static juce::String paramChannelSplit; 167 | static juce::String paramTreble; 168 | static juce::String paramBass; 169 | static juce::String paramOutput; 170 | static juce::String paramVoices; 171 | 172 | gin::AudioFifo fifo {1, 44100}; 173 | 174 | #if JUCE_IOS 175 | juce::MidiKeyboardState keyState; 176 | #endif 177 | 178 | private: 179 | void runUntil (int& done, juce::AudioSampleBuffer& buffer, int pos); 180 | PAPUEngine* findFreeVoice(int channel); 181 | PAPUEngine* findVoiceForNote (int note, int channel); 182 | 183 | juce::OwnedArray papus; 184 | int nextVoice = 0; 185 | 186 | //============================================================================== 187 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PAPUAudioProcessor) 188 | }; 189 | -------------------------------------------------------------------------------- /plugin/Source/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 BLARGG_BEGIN_NAMESPACE( name ) 10 | #define BLARGG_END_NAMESPACE 11 | 12 | #endif 13 | 14 | -------------------------------------------------------------------------------- /plugin/Source/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 | #if BLARGG_USE_NAMESPACE 8 | #include 9 | #else 10 | #include 11 | #endif 12 | 13 | BLARGG_BEGIN_NAMESPACE( boost ) 14 | 15 | #if UCHAR_MAX != 0xFF || SCHAR_MAX != 0x7F 16 | # error "No suitable 8-bit type available" 17 | #endif 18 | 19 | typedef unsigned char uint8_t; 20 | typedef signed char int8_t; 21 | 22 | #if USHRT_MAX != 0xFFFF 23 | # error "No suitable 16-bit type available" 24 | #endif 25 | 26 | typedef short int16_t; 27 | typedef unsigned short uint16_t; 28 | 29 | #if ULONG_MAX == 0xFFFFFFFF 30 | typedef long int32_t; 31 | typedef unsigned long uint32_t; 32 | #elif UINT_MAX == 0xFFFFFFFF 33 | typedef int int32_t; 34 | typedef unsigned int uint32_t; 35 | #else 36 | # error "No suitable 32-bit type available" 37 | #endif 38 | 39 | BLARGG_END_NAMESPACE 40 | 41 | #endif 42 | 43 | -------------------------------------------------------------------------------- /plugin/Source/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 | #if defined (_MSC_VER) && _MSC_VER <= 1200 8 | // MSVC6 can't handle the ##line concatenation 9 | #define BOOST_STATIC_ASSERT( expr ) struct { int n [1 / ((expr) ? 1 : 0)]; } 10 | 11 | #else 12 | #define BOOST_STATIC_ASSERT3( expr, line ) \ 13 | typedef int boost_static_assert_##line [1 / ((expr) ? 1 : 0)] 14 | 15 | #define BOOST_STATIC_ASSERT2( expr, line ) BOOST_STATIC_ASSERT3( expr, line ) 16 | 17 | #define BOOST_STATIC_ASSERT( expr ) BOOST_STATIC_ASSERT2( expr, __LINE__ ) 18 | 19 | #endif 20 | 21 | #endif 22 | 23 | -------------------------------------------------------------------------------- /plugin/Source/gb_apu/Blip_Buffer.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Blip_Buffer 0.3.4. http://www.slack.net/~ant/libs/ 3 | 4 | #include "Blip_Buffer.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | /* Copyright (C) 2003-2005 Shay Green. This module is free software; you 11 | can redistribute it and/or modify it under the terms of the GNU Lesser 12 | General Public License as published by the Free Software Foundation; either 13 | version 2.1 of the License, or (at your option) any later version. This 14 | module is distributed in the hope that it will be useful, but WITHOUT ANY 15 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for 17 | more details. You should have received a copy of the GNU Lesser General 18 | Public License along with this module; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 20 | 21 | #include BLARGG_SOURCE_BEGIN 22 | 23 | Blip_Buffer::Blip_Buffer() 24 | { 25 | samples_per_sec = 44100; 26 | buffer_ = NULL; 27 | 28 | // try to cause assertion failure if buffer is used before these are set 29 | clocks_per_sec = 0; 30 | factor_ = ~0ul; 31 | offset_ = 0; 32 | buffer_size_ = 0; 33 | length_ = 0; 34 | 35 | bass_freq_ = 16; 36 | } 37 | 38 | void Blip_Buffer::clear( bool entire_buffer ) 39 | { 40 | long count = (entire_buffer ? buffer_size_ : samples_avail()); 41 | offset_ = 0; 42 | reader_accum = 0; 43 | if ( buffer_ ) 44 | memset( buffer_, sample_offset_ & 0xFF, (count + widest_impulse_) * sizeof (buf_t_) ); 45 | } 46 | 47 | blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) 48 | { 49 | unsigned new_size = (UINT_MAX >> BLIP_BUFFER_ACCURACY) + 1 - widest_impulse_ - 64; 50 | if ( msec != blip_default_length ) 51 | { 52 | size_t s = (new_rate * (msec + 1) + 999) / 1000; 53 | if ( s < new_size ) 54 | new_size = unsigned (s); 55 | else 56 | require( false ); // requested buffer length exceeds limit 57 | } 58 | 59 | if ( buffer_size_ != new_size ) 60 | { 61 | delete [] buffer_; 62 | buffer_ = NULL; // allow for exception in allocation below 63 | buffer_size_ = 0; 64 | offset_ = 0; 65 | 66 | int const count_clocks_extra = 2; 67 | buffer_ = BLARGG_NEW buf_t_ [new_size + widest_impulse_ + count_clocks_extra]; 68 | BLARGG_CHECK_ALLOC( buffer_ ); 69 | } 70 | 71 | buffer_size_ = new_size; 72 | length_ = new_size * 1000 / new_rate - 1; 73 | if ( msec ) 74 | assert( length_ == msec ); // ensure length is same as that passed in 75 | 76 | samples_per_sec = new_rate; 77 | if ( clocks_per_sec ) 78 | clock_rate( clocks_per_sec ); // recalculate factor 79 | 80 | bass_freq( bass_freq_ ); // recalculate shift 81 | 82 | clear(); 83 | 84 | return blargg_success; 85 | } 86 | 87 | blip_resampled_time_t Blip_Buffer::clock_rate_factor( long clock_rate ) const 88 | { 89 | blip_resampled_time_t factor = (unsigned long) floor( 90 | (double) samples_per_sec / clock_rate * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); 91 | require( factor > 0 ); // clock_rate/sample_rate ratio is too large 92 | return factor; 93 | } 94 | 95 | Blip_Buffer::~Blip_Buffer() 96 | { 97 | delete [] buffer_; 98 | } 99 | 100 | void Blip_Buffer::bass_freq( int freq ) 101 | { 102 | bass_freq_ = freq; 103 | if ( freq == 0 ) 104 | { 105 | bass_shift = 31; // 32 or greater invokes undefined behavior elsewhere 106 | return; 107 | } 108 | bass_shift = 1 + (int) floor( 1.442695041 * log( 0.124 * samples_per_sec / freq ) ); 109 | if ( bass_shift < 0 ) 110 | bass_shift = 0; 111 | if ( bass_shift > 24 ) 112 | bass_shift = 24; 113 | } 114 | 115 | long Blip_Buffer::count_samples( blip_time_t t ) const 116 | { 117 | return (resampled_time( t ) >> BLIP_BUFFER_ACCURACY) - (offset_ >> BLIP_BUFFER_ACCURACY); 118 | } 119 | 120 | blip_time_t Blip_Buffer::count_clocks( long count ) const 121 | { 122 | if ( count > buffer_size_ ) 123 | count = buffer_size_; 124 | 125 | return ((count << BLIP_BUFFER_ACCURACY) - offset_ + (factor_ - 1)) / factor_; 126 | } 127 | 128 | void Blip_Impulse_::init( blip_pair_t_* imps, int w, int r, int fb ) 129 | { 130 | fine_bits = fb; 131 | width = w; 132 | impulses = (imp_t*) imps; 133 | generate = true; 134 | volume_unit_ = -1.0; 135 | res = r; 136 | buf = nullptr; 137 | 138 | impulse = &impulses [width * res * 2 * (fine_bits ? 2 : 1)]; 139 | offset = 0; 140 | } 141 | 142 | const int impulse_bits = 15; 143 | const long impulse_amp = 1L << impulse_bits; 144 | const long impulse_offset = impulse_amp / 2; 145 | 146 | void Blip_Impulse_::scale_impulse( int unit, imp_t* imp_in ) const 147 | { 148 | long offset = ((long) unit << impulse_bits) - impulse_offset * unit + 149 | (1 << (impulse_bits - 1)); 150 | imp_t* imp = imp_in; 151 | imp_t* fimp = impulse; 152 | for ( int n = res / 2 + 1; n--; ) 153 | { 154 | int error = unit; 155 | for ( int nn = width; nn--; ) 156 | { 157 | long a = ((long) *fimp++ * unit + offset) >> impulse_bits; 158 | error -= a - unit; 159 | *imp++ = (imp_t) a; 160 | } 161 | 162 | // add error to middle 163 | imp [-width / 2 - 1] += (imp_t) error; 164 | } 165 | 166 | if ( res > 2 ) 167 | { 168 | // second half is mirror-image 169 | const imp_t* rev = imp - width - 1; 170 | for ( int nn = (res / 2 - 1) * width - 1; nn--; ) 171 | *imp++ = *--rev; 172 | *imp++ = (imp_t) unit; 173 | } 174 | 175 | // copy to odd offset 176 | *imp++ = (imp_t) unit; 177 | memcpy( imp, imp_in, (res * width - 1) * sizeof *imp ); 178 | 179 | /* 180 | for ( int i = 0; i < res; i++ ) 181 | { 182 | for ( int j = 0; j < width; j++ ) 183 | printf( "%6d,", imp_in [i * width + j] - 0x8000 ); 184 | printf( "\n" ); 185 | }*/ 186 | } 187 | 188 | const int max_res = 1 << blip_res_bits_; 189 | 190 | void Blip_Impulse_::fine_volume_unit() 191 | { 192 | // to do: find way of merging in-place without temporary buffer 193 | 194 | imp_t temp [max_res * 2 * Blip_Buffer::widest_impulse_]; 195 | scale_impulse( (offset & 0xffff) << fine_bits, temp ); 196 | imp_t* imp2 = impulses + res * 2 * width; 197 | scale_impulse( offset & 0xffff, imp2 ); 198 | 199 | // merge impulses 200 | imp_t* imp = impulses; 201 | imp_t* src2 = temp; 202 | for ( int n = res / 2 * 2 * width; n--; ) 203 | { 204 | *imp++ = *imp2++; 205 | *imp++ = *imp2++; 206 | *imp++ = *src2++; 207 | *imp++ = *src2++; 208 | } 209 | } 210 | 211 | void Blip_Impulse_::volume_unit( double new_unit ) 212 | { 213 | if ( new_unit == volume_unit_ ) 214 | return; 215 | 216 | if ( generate ) 217 | treble_eq( blip_eq_t( -8.87, 8800, 44100 ) ); 218 | 219 | volume_unit_ = new_unit; 220 | 221 | offset = BOOST::uint32_t (0x10001 * (unsigned long) floor( volume_unit_ * 0x10000 + 0.5 )); 222 | 223 | if ( fine_bits ) 224 | fine_volume_unit(); 225 | else 226 | scale_impulse( offset & 0xffff, impulses ); 227 | } 228 | 229 | static const double pi = 3.1415926535897932384626433832795029L; 230 | 231 | void Blip_Impulse_::treble_eq( const blip_eq_t& new_eq ) 232 | { 233 | if ( !generate && new_eq.treble == eq.treble && new_eq.cutoff == eq.cutoff && 234 | new_eq.sample_rate == eq.sample_rate ) 235 | return; // already calculated with same parameters 236 | 237 | generate = false; 238 | eq = new_eq; 239 | 240 | double treble = pow( 10.0, 1.0 / 20 * eq.treble ); // dB (-6dB = 0.50) 241 | if ( treble < 0.000005 ) 242 | treble = 0.000005; 243 | 244 | const double treble_freq = 22050.0; // treble level at 22 kHz harmonic 245 | const double sample_rate = eq.sample_rate; 246 | const double pt = treble_freq * 2 / sample_rate; 247 | double cutoff = eq.cutoff * 2 / sample_rate; 248 | if ( cutoff >= pt * 0.95 || cutoff >= 0.95 ) 249 | { 250 | cutoff = 0.5; 251 | treble = 1.0; 252 | } 253 | 254 | // DSF Synthesis (See T. Stilson & J. Smith (1996), 255 | // Alias-free digital synthesis of classic analog waveforms) 256 | 257 | // reduce adjacent impulse interference by using small part of wide impulse 258 | const double n_harm = 4096; 259 | const double rolloff = pow( treble, 1.0 / (n_harm * pt - n_harm * cutoff) ); 260 | const double rescale = 1.0 / pow( rolloff, n_harm * cutoff ); 261 | 262 | const double pow_a_n = rescale * pow( rolloff, n_harm ); 263 | const double pow_a_nc = rescale * pow( rolloff, n_harm * cutoff ); 264 | 265 | double total = 0.0; 266 | const double to_angle = pi / 2 / n_harm / max_res; 267 | 268 | float buf [max_res * (Blip_Buffer::widest_impulse_ - 2) / 2]; 269 | const int size = max_res * (width - 2) / 2; 270 | for ( int i = size; i--; ) 271 | { 272 | double angle = (i * 2 + 1) * to_angle; 273 | 274 | // equivalent 275 | //double y = dsf( angle, n_harm * cutoff, 1.0 ); 276 | //y -= rescale * dsf( angle, n_harm * cutoff, rolloff ); 277 | //y += rescale * dsf( angle, n_harm, rolloff ); 278 | 279 | const double cos_angle = cos( angle ); 280 | const double cos_nc_angle = cos( n_harm * cutoff * angle ); 281 | const double cos_nc1_angle = cos( (n_harm * cutoff - 1.0) * angle ); 282 | 283 | double b = 2.0 - 2.0 * cos_angle; 284 | double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; 285 | 286 | double d = 1.0 + rolloff * (rolloff - 2.0 * cos_angle); 287 | double c = pow_a_n * rolloff * cos( (n_harm - 1.0) * angle ) - 288 | pow_a_n * cos( n_harm * angle ) - 289 | pow_a_nc * rolloff * cos_nc1_angle + 290 | pow_a_nc * cos_nc_angle; 291 | 292 | // optimization of a / b + c / d 293 | double y = (a * d + c * b) / (b * d); 294 | 295 | // fixed window which affects wider impulses more 296 | if ( width > 12 ) 297 | { 298 | double window = cos( n_harm / 1.25 / Blip_Buffer::widest_impulse_ * angle ); 299 | y *= window * window; 300 | } 301 | 302 | total += (float) y; 303 | buf [i] = (float) y; 304 | } 305 | 306 | // integrate runs of length 'max_res' 307 | double factor = impulse_amp * 0.5 / total; // 0.5 accounts for other mirrored half 308 | imp_t* imp = impulse; 309 | const int step = max_res / res; 310 | int offset = res > 1 ? max_res : max_res / 2; 311 | for ( int n = res / 2 + 1; n--; offset -= step ) 312 | { 313 | for ( int w = -width / 2; w < width / 2; w++ ) 314 | { 315 | double sum = 0; 316 | for ( int i = max_res; i--; ) 317 | { 318 | int index = w * max_res + offset + i; 319 | if ( index < 0 ) 320 | index = -index - 1; 321 | if ( index < size ) 322 | sum += buf [index]; 323 | } 324 | *imp++ = (imp_t) floor( sum * factor + (impulse_offset + 0.5) ); 325 | } 326 | } 327 | 328 | // rescale 329 | double unit = volume_unit_; 330 | if ( unit >= 0 ) 331 | { 332 | volume_unit_ = -1; 333 | volume_unit( unit ); 334 | } 335 | } 336 | 337 | void Blip_Buffer::remove_samples( long count ) 338 | { 339 | require( buffer_ ); // sample rate must have been set 340 | 341 | if ( !count ) // optimization 342 | return; 343 | 344 | remove_silence( count ); 345 | 346 | // Allows synthesis slightly past time passed to end_frame(), as long as it's 347 | // not more than an output sample. 348 | // to do: kind of hacky, could add run_until() which keeps track of extra synthesis 349 | int const copy_extra = 1; 350 | 351 | // copy remaining samples to beginning and clear old samples 352 | long remain = samples_avail() + widest_impulse_ + copy_extra; 353 | if ( count >= remain ) 354 | memmove( buffer_, buffer_ + count, remain * sizeof (buf_t_) ); 355 | else 356 | memcpy( buffer_, buffer_ + count, remain * sizeof (buf_t_) ); 357 | memset( buffer_ + remain, sample_offset_ & 0xFF, count * sizeof (buf_t_) ); 358 | } 359 | 360 | #include BLARGG_ENABLE_OPTIMIZER 361 | 362 | long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, bool stereo ) 363 | { 364 | require( buffer_ ); // sample rate must have been set 365 | 366 | long count = samples_avail(); 367 | if ( count > max_samples ) 368 | count = max_samples; 369 | 370 | if ( !count ) 371 | return 0; // optimization 372 | 373 | int sample_offset_ = this->sample_offset_; 374 | int bass_shift = this->bass_shift; 375 | buf_t_* buf = buffer_; 376 | long accum = reader_accum; 377 | 378 | if ( !stereo ) 379 | { 380 | for ( long n = count; n--; ) 381 | { 382 | long s = accum >> accum_fract; 383 | accum -= accum >> bass_shift; 384 | accum += (long (*buf++) - sample_offset_) << accum_fract; 385 | *out++ = (blip_sample_t) s; 386 | 387 | // clamp sample 388 | if ( (BOOST::int16_t) s != s ) 389 | out [-1] = blip_sample_t (0x7FFF - (s >> 24)); 390 | } 391 | } 392 | else 393 | { 394 | for ( long n = count; n--; ) 395 | { 396 | long s = accum >> accum_fract; 397 | accum -= accum >> bass_shift; 398 | accum += (long (*buf++) - sample_offset_) << accum_fract; 399 | *out = (blip_sample_t) s; 400 | out += 2; 401 | 402 | // clamp sample 403 | if ( (BOOST::int16_t) s != s ) 404 | out [-2] = blip_sample_t (0x7FFF - (s >> 24)); 405 | } 406 | } 407 | 408 | reader_accum = accum; 409 | 410 | remove_samples( count ); 411 | 412 | return count; 413 | } 414 | 415 | void Blip_Buffer::mix_samples( const blip_sample_t* in, long count ) 416 | { 417 | buf_t_* buf = &buffer_ [(offset_ >> BLIP_BUFFER_ACCURACY) + (widest_impulse_ / 2 - 1)]; 418 | 419 | int prev = 0; 420 | while ( count-- ) 421 | { 422 | int s = *in++; 423 | *buf += s - prev; 424 | prev = s; 425 | ++buf; 426 | } 427 | *buf -= *--in; 428 | } 429 | 430 | -------------------------------------------------------------------------------- /plugin/Source/gb_apu/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.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. 6 | 7 | #ifndef BLIP_BUFFER_H 8 | #define BLIP_BUFFER_H 9 | 10 | #include "blargg_common.h" 11 | 12 | class Blip_Reader; 13 | 14 | // Source time unit. 15 | typedef long blip_time_t; 16 | 17 | // Type of sample produced. Signed 16-bit format. 18 | typedef BOOST::int16_t blip_sample_t; 19 | 20 | // Make buffer as large as possible (currently about 65000 samples) 21 | const int blip_default_length = 0; 22 | 23 | typedef unsigned long blip_resampled_time_t; // not documented 24 | 25 | class Blip_Buffer { 26 | public: 27 | // Construct an empty buffer. 28 | Blip_Buffer(); 29 | ~Blip_Buffer(); 30 | 31 | // Set output sample rate and buffer length in milliseconds (1/1000 sec), 32 | // then clear buffer. If length is not specified, make as large as possible. 33 | // If there is insufficient memory for the buffer, sets the buffer length 34 | // to 0 and returns error string (or propagates exception if compiler supports it). 35 | blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = blip_default_length ); 36 | 37 | // Length of buffer, in milliseconds 38 | int length() const; 39 | 40 | // Current output sample rate 41 | long sample_rate() const; 42 | 43 | // Number of source time units per second 44 | void clock_rate( long ); 45 | long clock_rate() const; 46 | 47 | // Set frequency at which high-pass filter attenuation passes -3dB 48 | void bass_freq( int frequency ); 49 | 50 | // Remove all available samples and clear buffer to silence. If 'entire_buffer' is 51 | // false, just clear out any samples waiting rather than the entire buffer. 52 | void clear( bool entire_buffer = true ); 53 | 54 | // End current time frame of specified duration and make its samples available 55 | // (along with any still-unread samples) for reading with read_samples(). Begin 56 | // a new time frame at the end of the current frame. All transitions must have 57 | // been added before 'time'. 58 | void end_frame( blip_time_t time ); 59 | 60 | // Number of samples available for reading with read_samples() 61 | long samples_avail() const; 62 | 63 | // Read at most 'max_samples' out of buffer into 'dest', removing them from from 64 | // the buffer. Return number of samples actually read and removed. If stereo is 65 | // true, increment 'dest' one extra time after writing each sample, to allow 66 | // easy interleving of two channels into a stereo output buffer. 67 | long read_samples( blip_sample_t* dest, long max_samples, bool stereo = false ); 68 | 69 | // Remove 'count' samples from those waiting to be read 70 | void remove_samples( long count ); 71 | 72 | // Number of samples delay from synthesis to samples read out 73 | int output_latency() const; 74 | 75 | // Beta features 76 | 77 | // Number of raw samples that can be mixed within frame of specified duration 78 | long count_samples( blip_time_t duration ) const; 79 | 80 | // Mix 'count' samples from 'buf' into buffer. 81 | void mix_samples( const blip_sample_t* buf, long count ); 82 | 83 | // Count number of clocks needed until 'count' samples will be available. 84 | // If buffer can't even hold 'count' samples, returns number of clocks until 85 | // buffer is full. 86 | blip_time_t count_clocks( long count ) const; 87 | 88 | 89 | // not documented yet 90 | 91 | void remove_silence( long count ); 92 | 93 | blip_resampled_time_t resampled_time( blip_time_t t ) const 94 | { 95 | return t * blip_resampled_time_t (factor_) + offset_; 96 | } 97 | 98 | blip_resampled_time_t clock_rate_factor( long clock_rate ) const; 99 | 100 | blip_resampled_time_t resampled_duration( int t ) const 101 | { 102 | return t * blip_resampled_time_t (factor_); 103 | } 104 | 105 | private: 106 | // noncopyable 107 | Blip_Buffer( const Blip_Buffer& ); 108 | Blip_Buffer& operator = ( const Blip_Buffer& ); 109 | 110 | // Don't use the following members. They are public only for technical reasons. 111 | public: 112 | enum { sample_offset_ = 0x7F7F }; // repeated byte allows memset to clear buffer 113 | enum { widest_impulse_ = 24 }; 114 | typedef BOOST::uint16_t buf_t_; 115 | 116 | unsigned long factor_; 117 | blip_resampled_time_t offset_; 118 | buf_t_* buffer_; 119 | unsigned buffer_size_; 120 | private: 121 | long reader_accum; 122 | int bass_shift; 123 | long samples_per_sec; 124 | long clocks_per_sec; 125 | int bass_freq_; 126 | int length_; 127 | 128 | enum { accum_fract = 15 }; // less than 16 to give extra sample range 129 | 130 | friend class Blip_Reader; 131 | }; 132 | 133 | // Low-pass equalization parameters (see notes.txt) 134 | class blip_eq_t { 135 | public: 136 | blip_eq_t( double treble = 0 ); 137 | blip_eq_t( double treble, long cutoff, long sample_rate ); 138 | private: 139 | double treble; 140 | long cutoff; 141 | long sample_rate; 142 | friend class Blip_Impulse_; 143 | }; 144 | 145 | // not documented yet (see Multi_Buffer.cpp for an example of use) 146 | class Blip_Reader { 147 | const Blip_Buffer::buf_t_* buf; 148 | long accum; 149 | #ifdef __MWERKS__ 150 | void operator = ( struct foobar ); // helps optimizer 151 | #endif 152 | public: 153 | // avoid anything which might cause optimizer to put object in memory 154 | 155 | int begin( Blip_Buffer& blip_buf ) { 156 | buf = blip_buf.buffer_; 157 | accum = blip_buf.reader_accum; 158 | return blip_buf.bass_shift; 159 | } 160 | 161 | int read() const { 162 | return int (accum >> Blip_Buffer::accum_fract); 163 | } 164 | 165 | void next( int bass_shift = 9 ) { 166 | accum -= accum >> bass_shift; 167 | accum += ((long) *buf++ - Blip_Buffer::sample_offset_) << Blip_Buffer::accum_fract; 168 | } 169 | 170 | void end( Blip_Buffer& blip_buf ) { 171 | blip_buf.reader_accum = accum; 172 | } 173 | }; 174 | 175 | 176 | 177 | // End of public interface 178 | 179 | #ifndef BLIP_BUFFER_ACCURACY 180 | #define BLIP_BUFFER_ACCURACY 16 181 | #endif 182 | 183 | const int blip_res_bits_ = 5; 184 | 185 | typedef BOOST::uint32_t blip_pair_t_; 186 | 187 | class Blip_Impulse_ { 188 | typedef BOOST::uint16_t imp_t; 189 | 190 | blip_eq_t eq; 191 | double volume_unit_; 192 | imp_t* impulses; 193 | imp_t* impulse; 194 | int width; 195 | int fine_bits; 196 | int res; 197 | bool generate; 198 | 199 | void fine_volume_unit(); 200 | void scale_impulse( int unit, imp_t* ) const; 201 | public: 202 | Blip_Buffer* buf; 203 | BOOST::uint32_t offset; 204 | 205 | void init( blip_pair_t_* impulses, int width, int res, int fine_bits = 0 ); 206 | void volume_unit( double ); 207 | void treble_eq( const blip_eq_t& ); 208 | }; 209 | 210 | inline blip_eq_t::blip_eq_t( double t ) : 211 | treble( t ), cutoff( 0 ), sample_rate( 44100 ) { 212 | } 213 | 214 | inline blip_eq_t::blip_eq_t( double t, long c, long sr ) : 215 | treble( t ), cutoff( c ), sample_rate( sr ) { 216 | } 217 | 218 | inline int Blip_Buffer::length() const { 219 | return length_; 220 | } 221 | 222 | inline long Blip_Buffer::samples_avail() const { 223 | return long (offset_ >> BLIP_BUFFER_ACCURACY); 224 | } 225 | 226 | inline long Blip_Buffer::sample_rate() const { 227 | return samples_per_sec; 228 | } 229 | 230 | inline void Blip_Buffer::end_frame( blip_time_t t ) { 231 | offset_ += t * factor_; 232 | assert(( "Blip_Buffer::end_frame(): Frame went past end of buffer", 233 | samples_avail() <= (long) buffer_size_ )); 234 | } 235 | 236 | inline void Blip_Buffer::remove_silence( long count ) { 237 | assert(( "Blip_Buffer::remove_silence(): Tried to remove more samples than available", 238 | count <= samples_avail() )); 239 | offset_ -= blip_resampled_time_t (count) << BLIP_BUFFER_ACCURACY; 240 | } 241 | 242 | inline int Blip_Buffer::output_latency() const { 243 | return widest_impulse_ / 2; 244 | } 245 | 246 | inline long Blip_Buffer::clock_rate() const { 247 | return clocks_per_sec; 248 | } 249 | 250 | inline void Blip_Buffer::clock_rate( long cps ) 251 | { 252 | clocks_per_sec = cps; 253 | factor_ = clock_rate_factor( cps ); 254 | } 255 | 256 | #include "Blip_Synth.h" 257 | 258 | #endif 259 | 260 | -------------------------------------------------------------------------------- /plugin/Source/gb_apu/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.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL 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 | // Use blip_good_quality as a starting point. 16 | const int blip_low_quality = 1; 17 | const int blip_med_quality = 2; 18 | const int blip_good_quality = 3; 19 | const int blip_high_quality = 4; 20 | 21 | // Blip_Synth is a transition waveform synthesizer which adds band-limited 22 | // offsets (transitions) into a Blip_Buffer. For a simpler interface, use 23 | // Blip_Wave (below). 24 | // 25 | // Range specifies the greatest expected offset that will occur. For a 26 | // waveform that goes between +amp and -amp, range should be amp * 2 (half 27 | // that if it only goes between +amp and 0). When range is large, a higher 28 | // accuracy scheme is used; to force this even when range is small, pass 29 | // the negative of range (i.e. -range). 30 | template 31 | class Blip_Synth { 32 | BOOST_STATIC_ASSERT( 1 <= quality && quality <= 5 ); 33 | BOOST_STATIC_ASSERT( -32768 <= range && range <= 32767 ); 34 | enum { 35 | abs_range = (range < 0) ? -range : range, 36 | fine_mode = (range > 512 || range < 0), 37 | width = (quality < 5 ? quality * 4 : Blip_Buffer::widest_impulse_), 38 | res = 1 << blip_res_bits_, 39 | impulse_size = width / 2 * (fine_mode + 1), 40 | base_impulses_size = width / 2 * (res / 2 + 1), 41 | fine_bits = (fine_mode ? (abs_range <= 64 ? 2 : abs_range <= 128 ? 3 : 42 | abs_range <= 256 ? 4 : abs_range <= 512 ? 5 : abs_range <= 1024 ? 6 : 43 | abs_range <= 2048 ? 7 : 8) : 0) 44 | }; 45 | blip_pair_t_ impulses [impulse_size * res * 2 + base_impulses_size]; 46 | Blip_Impulse_ impulse; 47 | void init() { impulse.init( impulses, width, res, fine_bits ); } 48 | public: 49 | Blip_Synth() { init(); } 50 | Blip_Synth( double volume ) { init(); this->volume( volume ); } 51 | 52 | // Configure low-pass filter (see notes.txt). Not optimized for real-time control 53 | void treble_eq( const blip_eq_t& eq ) { impulse.treble_eq( eq ); } 54 | 55 | // Set volume of a transition at amplitude 'range' by setting volume_unit 56 | // to v / range 57 | void volume( double v ) { impulse.volume_unit( v * (1.0 / abs_range) ); } 58 | 59 | // Set base volume unit of transitions, where 1.0 is a full swing between the 60 | // positive and negative extremes. Not optimized for real-time control. 61 | void volume_unit( double unit ) { impulse.volume_unit( unit ); } 62 | 63 | // Default Blip_Buffer used for output when none is specified for a given call 64 | Blip_Buffer* output() const { return impulse.buf; } 65 | void output( Blip_Buffer* b ) { impulse.buf = b; } 66 | 67 | // Add an amplitude offset (transition) with a magnitude of delta * volume_unit 68 | // into the specified buffer (default buffer if none specified) at the 69 | // specified source time. Delta can be positive or negative. To increase 70 | // performance by inlining code at the call site, use offset_inline(). 71 | void offset( blip_time_t, int delta, Blip_Buffer* ) const; 72 | 73 | void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; 74 | void offset_resampled( blip_resampled_time_t t, int o ) const { 75 | offset_resampled( t, o, impulse.buf ); 76 | } 77 | void offset( blip_time_t t, int delta ) const { 78 | offset( t, delta, impulse.buf ); 79 | } 80 | void offset_inline( blip_time_t time, int delta, Blip_Buffer* buf ) const { 81 | offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); 82 | } 83 | void offset_inline( blip_time_t time, int delta ) const { 84 | offset_inline( time, delta, impulse.buf ); 85 | } 86 | }; 87 | 88 | // Blip_Wave is a synthesizer for adding a *single* waveform to a Blip_Buffer. 89 | // A wave is built from a series of delays and new amplitudes. This provides a 90 | // simpler interface than Blip_Synth, nothing more. 91 | template 92 | class Blip_Wave { 93 | Blip_Synth synth; 94 | blip_time_t time_; 95 | int last_amp; 96 | void init() { time_ = 0; last_amp = 0; } 97 | public: 98 | // Start wave at time 0 and amplitude 0 99 | Blip_Wave() { init(); } 100 | Blip_Wave( double volume ) { init(); this->volume( volume ); } 101 | 102 | // See Blip_Synth for description 103 | void volume( double v ) { synth.volume( v ); } 104 | void volume_unit( double v ) { synth.volume_unit( v ); } 105 | void treble_eq( const blip_eq_t& eq){ synth.treble_eq( eq ); } 106 | Blip_Buffer* output() const { return synth.output(); } 107 | void output( Blip_Buffer* b ) { synth.output( b ); if ( !b ) time_ = last_amp = 0; } 108 | 109 | // Current time in frame 110 | blip_time_t time() const { return time_; } 111 | void time( blip_time_t t ) { time_ = t; } 112 | 113 | // Current amplitude of wave 114 | int amplitude() const { return last_amp; } 115 | void amplitude( int ); 116 | 117 | // Move forward by 't' time units 118 | void delay( blip_time_t t ) { time_ += t; } 119 | 120 | // End time frame of specified duration. Localize time to new frame. 121 | // If wave hadn't been run to end of frame, start it at beginning of new frame. 122 | void end_frame( blip_time_t duration ) 123 | { 124 | time_ -= duration; 125 | if ( time_ < 0 ) 126 | time_ = 0; 127 | } 128 | }; 129 | 130 | // End of public interface 131 | 132 | template 133 | void Blip_Wave::amplitude( int amp ) { 134 | int delta = amp - last_amp; 135 | last_amp = amp; 136 | synth.offset_inline( time_, delta ); 137 | } 138 | 139 | template 140 | inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, 141 | int delta, Blip_Buffer* blip_buf ) const 142 | { 143 | typedef blip_pair_t_ pair_t; 144 | 145 | unsigned sample_index = unsigned ((time >> BLIP_BUFFER_ACCURACY) & ~1); 146 | assert(( "Blip_Synth/Blip_wave: Went past end of buffer", 147 | sample_index < blip_buf->buffer_size_ )); 148 | enum { const_offset = Blip_Buffer::widest_impulse_ / 2 - width / 2 }; 149 | pair_t* buf = (pair_t*) &blip_buf->buffer_ [const_offset + sample_index]; 150 | 151 | enum { shift = BLIP_BUFFER_ACCURACY - blip_res_bits_ }; 152 | enum { mask = res * 2 - 1 }; 153 | const pair_t* imp = &impulses [((time >> shift) & mask) * impulse_size]; 154 | 155 | pair_t offset = impulse.offset * delta; 156 | 157 | if ( !fine_bits ) 158 | { 159 | // normal mode 160 | for ( int n = width / 4; n; --n ) 161 | { 162 | pair_t t0 = buf [0] - offset; 163 | pair_t t1 = buf [1] - offset; 164 | 165 | t0 += imp [0] * delta; 166 | t1 += imp [1] * delta; 167 | imp += 2; 168 | 169 | buf [0] = t0; 170 | buf [1] = t1; 171 | buf += 2; 172 | } 173 | } 174 | else 175 | { 176 | // fine mode 177 | enum { sub_range = 1 << fine_bits }; 178 | delta += sub_range / 2; 179 | int delta2 = (delta & (sub_range - 1)) - sub_range / 2; 180 | delta >>= fine_bits; 181 | 182 | for ( int n = width / 4; n; --n ) 183 | { 184 | pair_t t0 = buf [0] - offset; 185 | pair_t t1 = buf [1] - offset; 186 | 187 | t0 += imp [0] * delta2; 188 | t0 += imp [1] * delta; 189 | 190 | t1 += imp [2] * delta2; 191 | t1 += imp [3] * delta; 192 | 193 | imp += 4; 194 | 195 | buf [0] = t0; 196 | buf [1] = t1; 197 | buf += 2; 198 | } 199 | } 200 | } 201 | 202 | template 203 | void Blip_Synth::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const { 204 | offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); 205 | } 206 | 207 | #endif 208 | 209 | -------------------------------------------------------------------------------- /plugin/Source/gb_apu/Gb_Apu.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/libs/ 3 | 4 | #include "Gb_Apu.h" 5 | 6 | #include 7 | 8 | /* Copyright (C) 2003-2005 Shay Green. This module is free software; you 9 | can redistribute it and/or modify it under the terms of the GNU Lesser 10 | General Public License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. This 12 | module is distributed in the hope that it will be useful, but WITHOUT ANY 13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for 15 | more details. You should have received a copy of the GNU Lesser General 16 | Public License along with this module; if not, write to the Free Software 17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 18 | 19 | #include BLARGG_SOURCE_BEGIN 20 | 21 | Gb_Apu::Gb_Apu() 22 | { 23 | square1.synth = &square_synth; 24 | square2.synth = &square_synth; 25 | square1.has_sweep = true; 26 | wave.synth = &other_synth; 27 | noise.synth = &other_synth; 28 | 29 | oscs [0] = &square1; 30 | oscs [1] = &square2; 31 | oscs [2] = &wave; 32 | oscs [3] = &noise; 33 | 34 | volume( 1.0 ); 35 | reset(); 36 | } 37 | 38 | Gb_Apu::~Gb_Apu() 39 | { 40 | } 41 | 42 | void Gb_Apu::treble_eq( const blip_eq_t& eq ) 43 | { 44 | square_synth.treble_eq( eq ); 45 | other_synth.treble_eq( eq ); 46 | } 47 | 48 | void Gb_Apu::volume( double vol ) 49 | { 50 | vol *= 0.60 / osc_count; 51 | square_synth.volume( vol ); 52 | other_synth.volume( vol ); 53 | } 54 | 55 | void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) 56 | { 57 | for ( int i = 0; i < osc_count; i++ ) 58 | osc_output( i, center, left, right ); 59 | } 60 | 61 | void Gb_Apu::reset() 62 | { 63 | next_frame_time = 0; 64 | last_time = 0; 65 | frame_count = 0; 66 | stereo_found = false; 67 | 68 | square1.reset(); 69 | square2.reset(); 70 | wave.reset(); 71 | noise.reset(); 72 | 73 | memset( regs, 0, sizeof regs ); 74 | } 75 | 76 | void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) 77 | { 78 | require( (unsigned) index < osc_count ); 79 | 80 | Gb_Osc& osc = *oscs [index]; 81 | if ( center && !left && !right ) 82 | { 83 | // mono 84 | left = center; 85 | right = center; 86 | } 87 | else 88 | { 89 | // must be silenced or stereo 90 | require( (!left && !right) || (left && right) ); 91 | } 92 | osc.outputs [1] = right; 93 | osc.outputs [2] = left; 94 | osc.outputs [3] = center; 95 | osc.output = osc.outputs [osc.output_select]; 96 | } 97 | 98 | void Gb_Apu::stopWave() 99 | { 100 | wave.disableOnZeroCrossing = 32; 101 | } 102 | 103 | void Gb_Apu::resetStopWave() 104 | { 105 | wave.disableOnZeroCrossing = -1; 106 | } 107 | 108 | void Gb_Apu::run_until( gb_time_t end_time ) 109 | { 110 | require( end_time >= last_time ); // end_time must not be before previous time 111 | if ( end_time == last_time ) 112 | return; 113 | 114 | while ( true ) 115 | { 116 | gb_time_t time = next_frame_time; 117 | if ( time > end_time ) 118 | time = end_time; 119 | 120 | // run oscillators 121 | for ( int i = 0; i < osc_count; ++i ) { 122 | Gb_Osc& osc = *oscs [i]; 123 | if ( osc.output ) { 124 | if ( osc.output != osc.outputs [3] ) 125 | stereo_found = true; 126 | osc.run( last_time, time ); 127 | } 128 | } 129 | last_time = time; 130 | 131 | if ( time == end_time ) 132 | break; 133 | 134 | next_frame_time += 4194304 / 256; // 256 Hz 135 | 136 | // 256 Hz actions 137 | square1.clock_length(); 138 | square2.clock_length(); 139 | wave.clock_length(); 140 | noise.clock_length(); 141 | 142 | frame_count = (frame_count + 1) & 3; 143 | if ( frame_count == 0 ) { 144 | // 64 Hz actions 145 | square1.clock_envelope(); 146 | square2.clock_envelope(); 147 | noise.clock_envelope(); 148 | } 149 | 150 | if ( frame_count & 1 ) 151 | square1.clock_sweep(); // 128 Hz action 152 | } 153 | } 154 | 155 | bool Gb_Apu::end_frame( gb_time_t end_time ) 156 | { 157 | if ( end_time > last_time ) 158 | run_until( end_time ); 159 | 160 | assert( next_frame_time >= end_time ); 161 | next_frame_time -= end_time; 162 | 163 | assert( last_time >= end_time ); 164 | last_time -= end_time; 165 | 166 | bool result = stereo_found; 167 | stereo_found = false; 168 | return result; 169 | } 170 | 171 | void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data ) 172 | { 173 | require( (unsigned) data < 0x100 ); 174 | 175 | int reg = addr - start_addr; 176 | if ( (unsigned) reg >= register_count ) 177 | return; 178 | 179 | run_until( time ); 180 | 181 | regs [reg] = data; 182 | 183 | if ( addr < 0xff24 ) 184 | { 185 | // oscillator 186 | int index = reg / 5; 187 | oscs [index]->write_register( reg - index * 5, data ); 188 | } 189 | // added 190 | else if ( addr == 0xff24 ) 191 | { 192 | int global_volume = data & 7; 193 | int old_volume = square1.global_volume; 194 | if ( old_volume != global_volume ) 195 | { 196 | int any_enabled = false; 197 | for ( int i = 0; i < osc_count; i++ ) 198 | { 199 | Gb_Osc& osc = *oscs [i]; 200 | if ( osc.enabled ) 201 | { 202 | if ( osc.last_amp ) 203 | { 204 | int new_amp = osc.last_amp * global_volume / osc.global_volume; 205 | if ( osc.output ) 206 | square_synth.offset( time, new_amp - osc.last_amp, osc.output ); 207 | osc.last_amp = new_amp; 208 | } 209 | any_enabled |= osc.volume; 210 | } 211 | osc.global_volume = global_volume; 212 | } 213 | 214 | if ( !any_enabled && square1.outputs [3] ) 215 | square_synth.offset( time, (global_volume - old_volume) * 15 * 2, square1.outputs [3] ); 216 | } 217 | } 218 | 219 | else if ( addr == 0xff25 || addr == 0xff26 ) 220 | { 221 | int mask = (regs [0xff26 - start_addr] & 0x80) ? ~0 : 0; 222 | int flags = regs [0xff25 - start_addr] & mask; 223 | 224 | // left/right assignments 225 | for ( int i = 0; i < osc_count; i++ ) 226 | { 227 | Gb_Osc& osc = *oscs [i]; 228 | osc.enabled &= mask; 229 | int bits = flags >> i; 230 | Blip_Buffer* old_output = osc.output; 231 | osc.output_select = (bits >> 3 & 2) | (bits & 1); 232 | osc.output = osc.outputs [osc.output_select]; 233 | if ( osc.output != old_output && osc.last_amp ) 234 | { 235 | if ( old_output ) 236 | square_synth.offset( time, -osc.last_amp, old_output ); 237 | osc.last_amp = 0; 238 | } 239 | } 240 | } 241 | else if ( addr >= 0xff30 ) 242 | { 243 | int index = (addr & 0x0f) * 2; 244 | wave.wave [index] = data >> 4; 245 | wave.wave [index + 1] = data & 0x0f; 246 | } 247 | } 248 | 249 | int Gb_Apu::read_register( gb_time_t time, gb_addr_t addr ) 250 | { 251 | // function now takes actual address, i.e. 0xFFXX 252 | require( start_addr <= addr && addr <= end_addr ); 253 | 254 | run_until( time ); 255 | 256 | int data = regs [addr - start_addr]; 257 | 258 | if ( addr == 0xff26 ) 259 | { 260 | data &= 0xf0; 261 | for ( int i = 0; i < osc_count; i++ ) 262 | { 263 | const Gb_Osc& osc = *oscs [i]; 264 | if ( osc.enabled && (osc.length || !osc.length_enabled) ) 265 | data |= 1 << i; 266 | } 267 | } 268 | 269 | return data; 270 | } 271 | 272 | -------------------------------------------------------------------------------- /plugin/Source/gb_apu/Gb_Apu.h: -------------------------------------------------------------------------------- 1 | 2 | // Nintendo Game Boy PAPU sound chip emulator 3 | 4 | // Gb_Snd_Emu 0.1.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. 5 | 6 | #ifndef GB_APU_H 7 | #define GB_APU_H 8 | 9 | typedef long gb_time_t; // clock cycle count 10 | typedef unsigned gb_addr_t; // 16-bit address 11 | 12 | #include "Gb_Oscs.h" 13 | 14 | class Gb_Apu { 15 | public: 16 | Gb_Apu(); 17 | ~Gb_Apu(); 18 | 19 | // Set overall volume of all oscillators, where 1.0 is full volume 20 | void volume( double ); 21 | 22 | // Set treble equalization 23 | void treble_eq( const blip_eq_t& ); 24 | 25 | // Reset oscillators and internal state 26 | void reset(); 27 | 28 | // Assign all oscillator outputs to specified buffer(s). If buffer 29 | // is NULL, silence all oscillators. 30 | void output( Blip_Buffer* mono ); 31 | void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); 32 | 33 | // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, 34 | // which refer to Square 1, Square 2, Wave, and Noise. 35 | // If buffer is NULL, silence oscillator. 36 | enum { osc_count = 4 }; 37 | void osc_output( int index, Blip_Buffer* mono ); 38 | void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); 39 | 40 | // Reads and writes at addr must satisfy start_addr <= addr <= end_addr 41 | enum { start_addr = 0xff10 }; 42 | enum { end_addr = 0xff3f }; 43 | enum { register_count = end_addr - start_addr + 1 }; 44 | 45 | // Write 'data' to address at specified time 46 | void write_register( gb_time_t, gb_addr_t, int data ); 47 | 48 | // Read from address at specified time 49 | int read_register( gb_time_t, gb_addr_t ); 50 | 51 | // Run all oscillators up to specified time, end current time frame, then 52 | // start a new frame at time 0. Return true if any oscillators added 53 | // sound to one of the left/right buffers, false if they only added 54 | // to the center buffer. 55 | bool end_frame( gb_time_t ); 56 | 57 | void stopWave(); 58 | void resetStopWave(); 59 | 60 | private: 61 | // noncopyable 62 | Gb_Apu( const Gb_Apu& ); 63 | Gb_Apu& operator = ( const Gb_Apu& ); 64 | 65 | Gb_Osc* oscs [osc_count]; 66 | gb_time_t next_frame_time; 67 | gb_time_t last_time; 68 | int frame_count; 69 | bool stereo_found; 70 | 71 | Gb_Square square1; 72 | Gb_Square square2; 73 | Gb_Wave wave; 74 | Gb_Noise noise; 75 | BOOST::uint8_t regs [register_count]; 76 | Gb_Square::Synth square_synth; // shared between squares 77 | Gb_Wave::Synth other_synth; // shared between wave and noise 78 | 79 | void run_until( gb_time_t ); 80 | }; 81 | 82 | inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, nullptr, nullptr ); } 83 | 84 | inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, nullptr, nullptr ); } 85 | 86 | #endif 87 | 88 | -------------------------------------------------------------------------------- /plugin/Source/gb_apu/Gb_Oscs.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/libs/ 3 | 4 | #include "Gb_Apu.h" 5 | 6 | #include 7 | 8 | /* Copyright (C) 2003-2005 Shay Green. This module is free software; you 9 | can redistribute it and/or modify it under the terms of the GNU Lesser 10 | General Public License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. This 12 | module is distributed in the hope that it will be useful, but WITHOUT ANY 13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for 15 | more details. You should have received a copy of the GNU Lesser General 16 | Public License along with this module; if not, write to the Free Software 17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 18 | 19 | #include BLARGG_SOURCE_BEGIN 20 | 21 | const int trigger = 0x80; 22 | 23 | // Gb_Osc 24 | 25 | Gb_Osc::Gb_Osc() 26 | { 27 | output = nullptr; 28 | outputs [0] = nullptr; 29 | outputs [1] = nullptr; 30 | outputs [2] = nullptr; 31 | outputs [3] = nullptr; 32 | } 33 | 34 | void Gb_Osc::reset() 35 | { 36 | delay = 0; 37 | last_amp = 0; 38 | period = 2048; 39 | volume = 0; 40 | global_volume = 7; // added 41 | frequency = 0; 42 | length = 0; 43 | enabled = false; 44 | length_enabled = false; 45 | output_select = 3; 46 | output = outputs [output_select]; 47 | } 48 | 49 | void Gb_Osc::clock_length() 50 | { 51 | if ( length_enabled && length ) 52 | --length; 53 | } 54 | 55 | void Gb_Osc::write_register( int reg, int value ) 56 | { 57 | if ( reg == 4 ) 58 | length_enabled = value & 0x40; 59 | } 60 | 61 | // Gb_Env 62 | 63 | void Gb_Env::reset() 64 | { 65 | env_period = 0; 66 | env_dir = 0; 67 | env_delay = 0; 68 | new_volume = 0; 69 | Gb_Osc::reset(); 70 | } 71 | 72 | Gb_Env::Gb_Env() 73 | { 74 | } 75 | 76 | void Gb_Env::clock_envelope() 77 | { 78 | if ( env_delay && !--env_delay ) 79 | { 80 | env_delay = env_period; 81 | if ( env_dir ) 82 | { 83 | if ( volume < 15 ) 84 | ++volume; 85 | } 86 | else if ( volume > 0 ) 87 | { 88 | --volume; 89 | } 90 | } 91 | } 92 | 93 | void Gb_Env::write_register( int reg, int value ) 94 | { 95 | if ( reg == 2 ) { 96 | env_period = value & 7; 97 | env_dir = value & 8; 98 | volume = new_volume = value >> 4; 99 | } 100 | else if ( reg == 4 && (value & trigger) ) { 101 | env_delay = env_period; 102 | volume = new_volume; 103 | enabled = true; 104 | } 105 | Gb_Osc::write_register( reg, value ); 106 | } 107 | 108 | // Gb_Square 109 | 110 | void Gb_Square::reset() 111 | { 112 | phase = 1; 113 | duty = 1; 114 | 115 | sweep_period = 0; 116 | sweep_delay = 0; 117 | sweep_shift = 0; 118 | sweep_dir = 0; 119 | sweep_freq = 0; 120 | 121 | new_length = 0; 122 | 123 | Gb_Env::reset(); 124 | } 125 | 126 | Gb_Square::Gb_Square() 127 | { 128 | has_sweep = false; 129 | } 130 | 131 | void Gb_Square::clock_sweep() 132 | { 133 | if ( sweep_period && sweep_delay && !--sweep_delay ) 134 | { 135 | sweep_delay = sweep_period; 136 | frequency = sweep_freq; 137 | 138 | period = (2048 - frequency) * 4; 139 | 140 | int offset = sweep_freq >> sweep_shift; 141 | if ( sweep_dir ) 142 | offset = -offset; 143 | sweep_freq += offset; 144 | 145 | if ( sweep_freq < 0 ) 146 | { 147 | sweep_freq = 0; 148 | } 149 | else if ( sweep_freq >= 2048 ) 150 | { 151 | sweep_delay = 0; 152 | sweep_freq = 2048; // stop sound output 153 | } 154 | } 155 | } 156 | 157 | void Gb_Square::write_register( int reg, int value ) 158 | { 159 | static unsigned char const duty_table [4] = { 1, 2, 4, 6 }; 160 | 161 | switch ( reg ) 162 | { 163 | case 0: 164 | sweep_period = (value >> 4) & 7; // changed 165 | sweep_shift = value & 7; 166 | sweep_dir = value & 0x08; 167 | break; 168 | 169 | case 1: 170 | new_length = length = 64 - (value & 0x3f); 171 | duty = duty_table [value >> 6]; 172 | break; 173 | 174 | case 3: 175 | frequency = (frequency & ~0xFF) + value; 176 | length = new_length; 177 | break; 178 | 179 | case 4: 180 | frequency = (value & 7) * 0x100 + (frequency & 0xFF); 181 | length = new_length; 182 | if ( value & trigger ) 183 | { 184 | sweep_freq = frequency; 185 | if ( has_sweep && sweep_period && sweep_shift ) 186 | { 187 | sweep_delay = 1; 188 | clock_sweep(); 189 | } 190 | } 191 | break; 192 | } 193 | 194 | period = (2048 - frequency) * 4; 195 | 196 | Gb_Env::write_register( reg, value ); 197 | } 198 | 199 | void Gb_Square::run( gb_time_t time, gb_time_t end_time ) 200 | { 201 | // to do: when frequency goes above 20000 Hz output should actually be 1/2 volume 202 | // rather than 0 203 | 204 | if ( !enabled || (!length && length_enabled) || !volume || sweep_freq == 2048 || 205 | !frequency || period < 27 ) 206 | { 207 | if ( last_amp ) 208 | { 209 | synth->offset( time, -last_amp, output ); 210 | last_amp = 0; 211 | } 212 | delay = 0; 213 | } 214 | else 215 | { 216 | int amp = (phase < duty) ? volume : -volume; 217 | amp *= global_volume; 218 | if ( amp != last_amp ) 219 | { 220 | synth->offset( time, amp - last_amp, output ); 221 | last_amp = amp; 222 | } 223 | 224 | time += delay; 225 | if ( time < end_time ) 226 | { 227 | Blip_Buffer* const output = this->output; 228 | const int duty = this->duty; 229 | int phase = this->phase; 230 | amp *= 2; 231 | do 232 | { 233 | phase = (phase + 1) & 7; 234 | if ( phase == 0 || phase == duty ) 235 | { 236 | amp = -amp; 237 | synth->offset_inline( time, amp, output ); 238 | } 239 | time += period; 240 | } 241 | while ( time < end_time ); 242 | 243 | this->phase = phase; 244 | last_amp = amp >> 1; 245 | } 246 | delay = int (time - end_time); 247 | } 248 | } 249 | 250 | 251 | // Gb_Wave 252 | 253 | void Gb_Wave::reset() 254 | { 255 | volume_shift = 0; 256 | wave_pos = 0; 257 | new_length = 0; 258 | memset( wave, 0, sizeof wave ); 259 | Gb_Osc::reset(); 260 | } 261 | 262 | Gb_Wave::Gb_Wave() { 263 | } 264 | 265 | void Gb_Wave::write_register( int reg, int value ) 266 | { 267 | switch ( reg ) 268 | { 269 | case 0: 270 | new_enabled = value & 0x80; 271 | enabled &= new_enabled; 272 | break; 273 | 274 | case 1: 275 | new_length = length = 256 - value; 276 | break; 277 | 278 | case 2: 279 | volume = ((value >> 5) & 3); 280 | volume_shift = (volume - 1) & 7; // silence = 7 281 | break; 282 | 283 | case 3: 284 | frequency = (frequency & ~0xFF) + value; 285 | break; 286 | 287 | case 4: 288 | frequency = (value & 7) * 0x100 + (frequency & 0xFF); 289 | if ( new_enabled && (value & trigger) ) 290 | { 291 | wave_pos = 0; 292 | length = new_length; 293 | enabled = true; 294 | } 295 | break; 296 | } 297 | 298 | period = (2048 - frequency) * 2; 299 | 300 | Gb_Osc::write_register( reg, value ); 301 | } 302 | 303 | void Gb_Wave::run( gb_time_t time, gb_time_t end_time ) 304 | { 305 | // to do: when frequency goes above 20000 Hz output should actually be 1/2 volume 306 | // rather than 0 307 | if ( !enabled || (!length && length_enabled) || !volume || !frequency || period < 7 ) 308 | { 309 | if ( last_amp ) { 310 | synth->offset( time, -last_amp, output ); 311 | last_amp = 0; 312 | } 313 | delay = 0; 314 | } 315 | else 316 | { 317 | int const vol_factor = global_volume * 2; 318 | 319 | // wave data or shift may have changed 320 | int diff = (wave [wave_pos] >> volume_shift) * vol_factor - last_amp; 321 | if ( diff ) 322 | { 323 | last_amp += diff; 324 | synth->offset( time, diff, output ); 325 | } 326 | 327 | time += delay; 328 | if ( time < end_time ) 329 | { 330 | int const volume_shift = this->volume_shift; 331 | int wave_pos = this->wave_pos; 332 | 333 | do 334 | { 335 | wave_pos = unsigned (wave_pos + 1) % wave_size; 336 | int amp = (wave [wave_pos] >> volume_shift) * vol_factor; 337 | int delta = amp - last_amp; 338 | if (disableOnZeroCrossing >= 0) { // disableOnZeroCrossing has been requested, initially set at 32 == one full period 339 | // from above to below, from below to above or equals 7, digital 7 is 0 340 | // if disableOnZeroCrossing equals 32, one full period has passed, just stop here! 341 | if ((last_amp > 7 && amp <= 7) || (last_amp < 7 && amp >= 7) || (amp == 7) || (disableOnZeroCrossing == 0)) 342 | { 343 | enabled = false; 344 | disableOnZeroCrossing = -1; 345 | synth->offset_inline( time, 7, output ); 346 | time = end_time; 347 | continue; 348 | } 349 | else // decrement the zc timer 350 | { 351 | disableOnZeroCrossing -= 1; 352 | } 353 | } 354 | else if ( delta ) 355 | { 356 | last_amp = amp; 357 | synth->offset_inline( time, delta, output ); 358 | } 359 | time += period; 360 | } 361 | while ( time < end_time ); 362 | 363 | this->wave_pos = wave_pos; 364 | } 365 | delay = int (time - end_time); 366 | } 367 | } 368 | 369 | 370 | // Gb_Noise 371 | 372 | void Gb_Noise::reset() 373 | { 374 | bits = 1; 375 | tap = 14; 376 | Gb_Env::reset(); 377 | } 378 | 379 | Gb_Noise::Gb_Noise() { 380 | } 381 | 382 | void Gb_Noise::write_register( int reg, int value ) 383 | { 384 | if ( reg == 1 ) { 385 | new_length = length = 64 - (value & 0x3f); 386 | } 387 | else if ( reg == 2 ) { 388 | // based on VBA code, noise is the only exception to the envelope code 389 | // while the volume level here is applied when the channel is enabled, 390 | // current volume is only affected by writes to this register if volume 391 | // is zero and direction is up... (definitely needs verification) 392 | int temp = volume; 393 | Gb_Env::write_register( reg, value ); 394 | if ( ( value & 0xF8 ) != 0 ) volume = temp; 395 | return; 396 | } 397 | else if ( reg == 3 ) { 398 | tap = 14 - (value & 8); 399 | // noise formula and frequency tested against Metroid 2 and Zelda LA 400 | int divisor = (value & 7) * 16; 401 | if ( !divisor ) 402 | divisor = 8; 403 | period = divisor << (value >> 4); 404 | } 405 | else if ( reg == 4 && value & trigger ) { 406 | bits = ~0u; 407 | length = new_length; 408 | } 409 | 410 | Gb_Env::write_register( reg, value ); 411 | } 412 | 413 | #include BLARGG_ENABLE_OPTIMIZER 414 | 415 | void Gb_Noise::run( gb_time_t time, gb_time_t end_time ) 416 | { 417 | if ( !enabled || (!length && length_enabled) || !volume ) { 418 | if ( last_amp ) { 419 | synth->offset( time, -last_amp, output ); 420 | last_amp = 0; 421 | } 422 | delay = 0; 423 | } 424 | else 425 | { 426 | int amp = bits & 1 ? -volume : volume; 427 | amp *= global_volume; 428 | if ( amp != last_amp ) { 429 | synth->offset( time, amp - last_amp, output ); 430 | last_amp = amp; 431 | } 432 | 433 | time += delay; 434 | if ( time < end_time ) 435 | { 436 | Blip_Buffer* const output = this->output; 437 | // keep parallel resampled time to eliminate multiplication in the loop 438 | const blip_resampled_time_t resampled_period = 439 | output->resampled_duration( period ); 440 | blip_resampled_time_t resampled_time = output->resampled_time( time ); 441 | const unsigned mask = ~(1u << tap); 442 | unsigned bits = this->bits; 443 | amp *= 2; 444 | 445 | do { 446 | unsigned feedback = bits; 447 | bits >>= 1; 448 | feedback = 1 & (feedback ^ bits); 449 | time += period; 450 | bits = (feedback << tap) | (bits & mask); 451 | // feedback just happens to be true only when the level needs to change 452 | // (the previous and current bits are different) 453 | if ( feedback ) { 454 | amp = -amp; 455 | synth->offset_resampled( resampled_time, amp, output ); 456 | } 457 | resampled_time += resampled_period; 458 | } 459 | while ( time < end_time ); 460 | 461 | this->bits = bits; 462 | last_amp = amp >> 1; 463 | } 464 | delay = int (time - end_time); 465 | } 466 | } 467 | 468 | -------------------------------------------------------------------------------- /plugin/Source/gb_apu/Gb_Oscs.h: -------------------------------------------------------------------------------- 1 | 2 | // Private oscillators used by Gb_Apu 3 | 4 | // Gb_Snd_Emu 0.1.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. 5 | 6 | #ifndef GB_OSCS_H 7 | #define GB_OSCS_H 8 | 9 | #include "Blip_Buffer.h" 10 | 11 | enum { gb_apu_max_vol = 7 }; 12 | 13 | struct Gb_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 period; 22 | int volume; 23 | int global_volume; 24 | int frequency; 25 | int length; 26 | int new_length; 27 | bool enabled; 28 | bool length_enabled; 29 | 30 | Gb_Osc(); 31 | virtual ~Gb_Osc() {} 32 | 33 | void clock_length(); 34 | void reset(); 35 | virtual void run( gb_time_t begin, gb_time_t end ) = 0; 36 | virtual void write_register( int reg, int value ); 37 | }; 38 | 39 | struct Gb_Env : Gb_Osc { 40 | int env_period; 41 | int env_dir; 42 | int env_delay; 43 | int new_volume; 44 | 45 | Gb_Env(); 46 | void reset(); 47 | void clock_envelope(); 48 | void write_register( int, int ); 49 | }; 50 | 51 | struct Gb_Square : Gb_Env { 52 | int phase; 53 | int duty; 54 | 55 | int sweep_period; 56 | int sweep_delay; 57 | int sweep_shift; 58 | int sweep_dir; 59 | int sweep_freq; 60 | bool has_sweep; 61 | 62 | typedef Blip_Synth Synth; 63 | const Synth* synth; 64 | 65 | Gb_Square(); 66 | void reset(); 67 | void run( gb_time_t, gb_time_t ); 68 | void write_register( int, int ); 69 | void clock_sweep(); 70 | }; 71 | 72 | struct Gb_Wave : Gb_Osc { 73 | int volume_shift; 74 | unsigned wave_pos; 75 | enum { wave_size = 32 }; 76 | bool new_enabled; 77 | BOOST::uint8_t wave [wave_size]; 78 | int8_t disableOnZeroCrossing = -1; 79 | 80 | typedef Blip_Synth Synth; 81 | const Synth* synth; 82 | 83 | Gb_Wave(); 84 | void reset(); 85 | void run( gb_time_t, gb_time_t ); 86 | void write_register( int, int ); 87 | }; 88 | 89 | struct Gb_Noise : Gb_Env { 90 | unsigned bits; 91 | int tap; 92 | 93 | typedef Blip_Synth Synth; 94 | const Synth* synth; 95 | 96 | Gb_Noise(); 97 | void reset(); 98 | void run( gb_time_t, gb_time_t ); 99 | void write_register( int, int ); 100 | }; 101 | 102 | #endif 103 | 104 | -------------------------------------------------------------------------------- /plugin/Source/gb_apu/LGPL.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 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 | [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 | 474 | Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | 504 | 505 | -------------------------------------------------------------------------------- /plugin/Source/gb_apu/Multi_Buffer.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Blip_Buffer 0.3.4. http://www.slack.net/~ant/libs/ 3 | 4 | #include "Multi_Buffer.h" 5 | 6 | /* Copyright (C) 2003-2005 Shay Green. This module is free software; you 7 | can redistribute it and/or modify it under the terms of the GNU Lesser 8 | General Public License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. This 10 | module is distributed in the hope that it will be useful, but WITHOUT ANY 11 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for 13 | more details. You should have received a copy of the GNU Lesser General 14 | Public License along with this module; if not, write to the Free Software 15 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 16 | 17 | #include BLARGG_SOURCE_BEGIN 18 | 19 | Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) 20 | { 21 | length_ = 0; 22 | sample_rate_ = 0; 23 | channels_changed_count_ = 1; 24 | } 25 | 26 | blargg_err_t Multi_Buffer::set_channel_count( int ) 27 | { 28 | return blargg_success; 29 | } 30 | 31 | Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) 32 | { 33 | } 34 | 35 | Mono_Buffer::~Mono_Buffer() 36 | { 37 | } 38 | 39 | blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) 40 | { 41 | BLARGG_RETURN_ERR( buf.set_sample_rate( rate, msec ) ); 42 | return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); 43 | } 44 | 45 | // Silent_Buffer 46 | 47 | Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse 48 | { 49 | chan.left = nullptr; 50 | chan.center = nullptr; 51 | chan.right = nullptr; 52 | } 53 | 54 | // Mono_Buffer 55 | 56 | Mono_Buffer::channel_t Mono_Buffer::channel( int ) 57 | { 58 | channel_t ch; 59 | ch.center = &buf; 60 | ch.left = &buf; 61 | ch.right = &buf; 62 | return ch; 63 | } 64 | 65 | void Mono_Buffer::end_frame( blip_time_t t, bool ) 66 | { 67 | buf.end_frame( t ); 68 | } 69 | 70 | // Stereo_Buffer 71 | 72 | Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) 73 | { 74 | chan.center = &bufs [0]; 75 | chan.left = &bufs [1]; 76 | chan.right = &bufs [2]; 77 | } 78 | 79 | Stereo_Buffer::~Stereo_Buffer() 80 | { 81 | } 82 | 83 | blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec ) 84 | { 85 | for ( int i = 0; i < buf_count; i++ ) 86 | BLARGG_RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); 87 | return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); 88 | } 89 | 90 | void Stereo_Buffer::clock_rate( long rate ) 91 | { 92 | for ( int i = 0; i < buf_count; i++ ) 93 | bufs [i].clock_rate( rate ); 94 | } 95 | 96 | void Stereo_Buffer::bass_freq( int bass ) 97 | { 98 | for ( unsigned i = 0; i < buf_count; i++ ) 99 | bufs [i].bass_freq( bass ); 100 | } 101 | 102 | void Stereo_Buffer::clear() 103 | { 104 | stereo_added = false; 105 | was_stereo = false; 106 | for ( int i = 0; i < buf_count; i++ ) 107 | bufs [i].clear(); 108 | } 109 | 110 | void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo ) 111 | { 112 | for ( unsigned i = 0; i < buf_count; i++ ) 113 | bufs [i].end_frame( clock_count ); 114 | 115 | stereo_added |= stereo; 116 | } 117 | 118 | long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) 119 | { 120 | count = (unsigned) count; 121 | 122 | long avail = bufs [0].samples_avail(); 123 | if ( count > avail ) 124 | count = avail; 125 | if ( count ) 126 | { 127 | if ( stereo_added || was_stereo ) 128 | { 129 | mix_stereo( out, count ); 130 | 131 | bufs [0].remove_samples( count ); 132 | bufs [1].remove_samples( count ); 133 | bufs [2].remove_samples( count ); 134 | } 135 | else 136 | { 137 | mix_mono( out, count ); 138 | 139 | bufs [0].remove_samples( count ); 140 | 141 | bufs [1].remove_silence( count ); 142 | bufs [2].remove_silence( count ); 143 | } 144 | 145 | // to do: this might miss opportunities for optimization 146 | if ( !bufs [0].samples_avail() ) { 147 | was_stereo = stereo_added; 148 | stereo_added = false; 149 | } 150 | } 151 | 152 | return count; 153 | } 154 | 155 | #include BLARGG_ENABLE_OPTIMIZER 156 | 157 | void Stereo_Buffer::mix_stereo( blip_sample_t* out, long count ) 158 | { 159 | Blip_Reader left; 160 | Blip_Reader right; 161 | Blip_Reader center; 162 | 163 | left.begin( bufs [1] ); 164 | right.begin( bufs [2] ); 165 | int bass = center.begin( bufs [0] ); 166 | 167 | while ( count-- ) 168 | { 169 | int c = center.read(); 170 | long l = c + left.read(); 171 | long r = c + right.read(); 172 | center.next( bass ); 173 | out [0] = blip_sample_t (l); 174 | out [1] = blip_sample_t (r); 175 | out += 2; 176 | 177 | if ( (BOOST::int16_t) l != l ) 178 | out [-2] = 0x7FFF - (l >> 24); 179 | 180 | left.next( bass ); 181 | right.next( bass ); 182 | 183 | if ( (BOOST::int16_t) r != r ) 184 | out [-1] = 0x7FFF - (r >> 24); 185 | } 186 | 187 | center.end( bufs [0] ); 188 | right.end( bufs [2] ); 189 | left.end( bufs [1] ); 190 | } 191 | 192 | void Stereo_Buffer::mix_mono( blip_sample_t* out, long count ) 193 | { 194 | Blip_Reader in; 195 | int bass = in.begin( bufs [0] ); 196 | 197 | while ( count-- ) 198 | { 199 | long s = in.read(); 200 | in.next( bass ); 201 | out [0] = s; 202 | out [1] = s; 203 | out += 2; 204 | 205 | if ( (BOOST::int16_t) s != s ) { 206 | s = 0x7FFF - (s >> 24); 207 | out [-2] = s; 208 | out [-1] = s; 209 | } 210 | } 211 | 212 | in.end( bufs [0] ); 213 | } 214 | 215 | -------------------------------------------------------------------------------- /plugin/Source/gb_apu/Multi_Buffer.h: -------------------------------------------------------------------------------- 1 | 2 | // Multi-channel sound buffer interface, and basic mono and stereo buffers 3 | 4 | // Blip_Buffer 0.3.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. 5 | 6 | #ifndef MULTI_BUFFER_H 7 | #define MULTI_BUFFER_H 8 | 9 | #include "Blip_Buffer.h" 10 | 11 | // Interface to one or more Blip_Buffers mapped to one or more channels 12 | // consisting of left, center, and right buffers. 13 | class Multi_Buffer { 14 | public: 15 | Multi_Buffer( int samples_per_frame ); 16 | virtual ~Multi_Buffer() { } 17 | 18 | // Set the number of channels available 19 | virtual blargg_err_t set_channel_count( int ); 20 | 21 | // Get indexed channel, from 0 to channel count - 1 22 | struct channel_t { 23 | Blip_Buffer* center; 24 | Blip_Buffer* left; 25 | Blip_Buffer* right; 26 | }; 27 | virtual channel_t channel( int index ) = 0; 28 | 29 | // See Blip_Buffer.h 30 | virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0; 31 | virtual void clock_rate( long ) = 0; 32 | virtual void bass_freq( int ) = 0; 33 | virtual void clear() = 0; 34 | long sample_rate() const; 35 | 36 | // Length of buffer, in milliseconds 37 | int length() const; 38 | 39 | // See Blip_Buffer.h. For optimal operation, pass false for 'added_stereo' 40 | // if nothing was added to the left and right buffers of any channel for 41 | // this time frame. 42 | virtual void end_frame( blip_time_t, bool added_stereo = true ) = 0; 43 | 44 | // Number of samples per output frame (1 = mono, 2 = stereo) 45 | int samples_per_frame() const; 46 | 47 | // Count of changes to channel configuration. Incremented whenever 48 | // a change is made to any of the Blip_Buffers for any channel. 49 | unsigned channels_changed_count() { return channels_changed_count_; } 50 | 51 | // See Blip_Buffer.h 52 | virtual long read_samples( blip_sample_t*, long ) = 0; 53 | virtual long samples_avail() const = 0; 54 | 55 | protected: 56 | void channels_changed() { channels_changed_count_++; } 57 | private: 58 | // noncopyable 59 | Multi_Buffer( const Multi_Buffer& ); 60 | Multi_Buffer& operator = ( const Multi_Buffer& ); 61 | 62 | unsigned channels_changed_count_; 63 | long sample_rate_; 64 | int length_; 65 | int const samples_per_frame_; 66 | }; 67 | 68 | // Uses a single buffer and outputs mono samples. 69 | class Mono_Buffer : public Multi_Buffer { 70 | Blip_Buffer buf; 71 | public: 72 | Mono_Buffer(); 73 | ~Mono_Buffer(); 74 | 75 | // Buffer used for all channels 76 | Blip_Buffer* center() { return &buf; } 77 | 78 | // See Multi_Buffer 79 | blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); 80 | void clock_rate( long ); 81 | void bass_freq( int ); 82 | void clear(); 83 | channel_t channel( int ); 84 | void end_frame( blip_time_t, bool unused = true ); 85 | long samples_avail() const; 86 | long read_samples( blip_sample_t*, long ); 87 | }; 88 | 89 | // Uses three buffers (one for center) and outputs stereo sample pairs. 90 | class Stereo_Buffer : public Multi_Buffer { 91 | public: 92 | Stereo_Buffer(); 93 | ~Stereo_Buffer(); 94 | 95 | // Buffers used for all channels 96 | Blip_Buffer* center() { return &bufs [0]; } 97 | Blip_Buffer* left() { return &bufs [1]; } 98 | Blip_Buffer* right() { return &bufs [2]; } 99 | 100 | // See Multi_Buffer 101 | blargg_err_t set_sample_rate( long, int msec = blip_default_length ); 102 | void clock_rate( long ); 103 | void bass_freq( int ); 104 | void clear(); 105 | channel_t channel( int index ); 106 | void end_frame( blip_time_t, bool added_stereo = true ); 107 | 108 | long samples_avail() const; 109 | long read_samples( blip_sample_t*, long ); 110 | 111 | private: 112 | enum { buf_count = 3 }; 113 | Blip_Buffer bufs [buf_count]; 114 | channel_t chan; 115 | bool stereo_added; 116 | bool was_stereo; 117 | 118 | void mix_stereo( blip_sample_t*, long ); 119 | void mix_mono( blip_sample_t*, long ); 120 | }; 121 | 122 | // Silent_Buffer generates no samples, useful where no sound is wanted 123 | class Silent_Buffer : public Multi_Buffer { 124 | channel_t chan; 125 | public: 126 | Silent_Buffer(); 127 | 128 | blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); 129 | void clock_rate( long ) { } 130 | void bass_freq( int ) { } 131 | void clear() { } 132 | channel_t channel( int ) { return chan; } 133 | void end_frame( blip_time_t, bool unused = true ) { (void)unused; } 134 | long samples_avail() const { return 0; } 135 | long read_samples( blip_sample_t*, long ) { return 0; } 136 | }; 137 | 138 | 139 | // End of public interface 140 | 141 | inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec ) 142 | { 143 | return Multi_Buffer::set_sample_rate( rate, msec ); 144 | } 145 | 146 | inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) 147 | { 148 | sample_rate_ = rate; 149 | length_ = msec; 150 | return blargg_success; 151 | } 152 | 153 | inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } 154 | 155 | inline long Stereo_Buffer::samples_avail() const { return bufs [0].samples_avail(); } 156 | 157 | inline Stereo_Buffer::channel_t Stereo_Buffer::channel( int index ) { (void)index; return chan; } 158 | 159 | inline long Multi_Buffer::sample_rate() const { return sample_rate_; } 160 | 161 | inline int Multi_Buffer::length() const { return length_; } 162 | 163 | inline void Mono_Buffer::clock_rate( long rate ) { buf.clock_rate( rate ); } 164 | 165 | inline void Mono_Buffer::clear() { buf.clear(); } 166 | 167 | inline void Mono_Buffer::bass_freq( int freq ) { buf.bass_freq( freq ); } 168 | 169 | inline long Mono_Buffer::read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } 170 | 171 | inline long Mono_Buffer::samples_avail() const { return buf.samples_avail(); } 172 | 173 | #endif 174 | 175 | -------------------------------------------------------------------------------- /plugin/Source/gb_apu/blargg_common.h: -------------------------------------------------------------------------------- 1 | 2 | // Sets up common environment for Shay Green's libraries. 3 | // 4 | // Don't modify this file directly; #define HAVE_CONFIG_H and put your 5 | // configuration into "config.h". 6 | 7 | // Copyright (C) 2004-2005 Shay Green. 8 | 9 | #ifndef BLARGG_COMMON_H 10 | #define BLARGG_COMMON_H 11 | 12 | #if __clang__ 13 | #pragma clang diagnostic ignored "-Wunused-value" 14 | #endif 15 | 16 | // Allow prefix configuration file *which can re-include blargg_common.h* 17 | // (probably indirectly). 18 | #ifdef HAVE_CONFIG_H 19 | #undef BLARGG_COMMON_H 20 | #include "config.h" 21 | #define BLARGG_COMMON_H 22 | #endif 23 | 24 | // Source files use #include BLARGG_ENABLE_OPTIMIZER before performance-critical code 25 | #ifndef BLARGG_ENABLE_OPTIMIZER 26 | #define BLARGG_ENABLE_OPTIMIZER "blargg_common.h" 27 | #endif 28 | 29 | // Source files have #include BLARGG_SOURCE_BEGIN at the beginning 30 | #ifndef BLARGG_SOURCE_BEGIN 31 | #define BLARGG_SOURCE_BEGIN "blargg_source.h" 32 | #endif 33 | 34 | // Determine compiler's language support 35 | 36 | #if defined (__MWERKS__) 37 | // Metrowerks CodeWarrior 38 | #define BLARGG_COMPILER_HAS_NAMESPACE 1 39 | #if !__option(bool) 40 | #define BLARGG_COMPILER_HAS_BOOL 0 41 | #endif 42 | 43 | #elif defined (_MSC_VER) 44 | // Microsoft Visual C++ 45 | #if _MSC_VER < 1100 46 | #define BLARGG_COMPILER_HAS_BOOL 0 47 | #endif 48 | 49 | #elif defined (__GNUC__) 50 | // GNU C++ 51 | #define BLARGG_COMPILER_HAS_NAMESPACE 1 52 | #define BLARGG_COMPILER_HAS_BOOL 1 53 | 54 | #elif defined (__MINGW32__) 55 | // Mingw? 56 | #define BLARGG_COMPILER_HAS_BOOL 1 57 | 58 | #elif __cplusplus < 199711 59 | // Pre-ISO C++ compiler 60 | #define BLARGG_COMPILER_HAS_BOOL 0 61 | #define STATIC_CAST( type ) (type) 62 | 63 | #endif 64 | 65 | // STATIC_CAST(T) (expr) -> static_cast< T > (expr) 66 | #ifndef STATIC_CAST 67 | #define STATIC_CAST( type ) static_cast< type > 68 | #endif 69 | 70 | // Set up boost 71 | #include "../boost/config.hpp" 72 | #ifndef BOOST_MINIMAL 73 | #define BOOST 74 | #ifndef BLARGG_COMPILER_HAS_NAMESPACE 75 | #define BLARGG_COMPILER_HAS_NAMESPACE 1 76 | #endif 77 | #ifndef BLARGG_COMPILER_HAS_BOOL 78 | #define BLARGG_COMPILER_HAS_BOOL 1 79 | #endif 80 | #endif 81 | 82 | // Bool support 83 | #ifndef BLARGG_COMPILER_HAS_BOOL 84 | #define BLARGG_COMPILER_HAS_BOOL 1 85 | #elif !BLARGG_COMPILER_HAS_BOOL 86 | typedef int bool; 87 | const bool true = 1; 88 | const bool false = 0; 89 | #endif 90 | 91 | // Set up namespace support 92 | 93 | #ifndef BLARGG_COMPILER_HAS_NAMESPACE 94 | #define BLARGG_COMPILER_HAS_NAMESPACE 0 95 | #endif 96 | 97 | #ifndef BLARGG_USE_NAMESPACE 98 | #define BLARGG_USE_NAMESPACE BLARGG_COMPILER_HAS_NAMESPACE 99 | #endif 100 | 101 | #ifndef BOOST 102 | #if BLARGG_USE_NAMESPACE 103 | #define BOOST 104 | #else 105 | #define BOOST 106 | #endif 107 | #endif 108 | 109 | #undef BLARGG_BEGIN_NAMESPACE 110 | #undef BLARGG_END_NAMESPACE 111 | #if BLARGG_USE_NAMESPACE 112 | #define BLARGG_BEGIN_NAMESPACE( name ) namespace name { 113 | #define BLARGG_END_NAMESPACE } 114 | #else 115 | #define BLARGG_BEGIN_NAMESPACE( name ) 116 | #define BLARGG_END_NAMESPACE 117 | #endif 118 | 119 | #if BLARGG_USE_NAMESPACE 120 | #define STD std 121 | #else 122 | #define STD 123 | #endif 124 | 125 | // BOOST::uint8_t, BOOST::int16_t, etc. 126 | //#include "boost/cstdint.hpp" 127 | #include 128 | #include 129 | 130 | // BOOST_STATIC_ASSERT( expr ) 131 | #include "../boost/static_assert.hpp" 132 | 133 | // Common standard headers 134 | #if BLARGG_COMPILER_HAS_NAMESPACE 135 | #include 136 | #include 137 | #else 138 | #include 139 | #include 140 | #endif 141 | 142 | // blargg_err_t (NULL on success, otherwise error string) 143 | typedef const char* blargg_err_t; 144 | const blargg_err_t blargg_success = nullptr; 145 | 146 | // BLARGG_NEW is used in place of 'new' to create objects. By default, 147 | // plain new is used. 148 | #ifndef BLARGG_NEW 149 | #define BLARGG_NEW new 150 | #endif 151 | 152 | // BLARGG_BIG_ENDIAN and BLARGG_LITTLE_ENDIAN 153 | // Only needed if modules are used which must know byte order. 154 | #if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) 155 | #if defined (__powerc) || defined (macintosh) 156 | #define BLARGG_BIG_ENDIAN 1 157 | 158 | #elif defined (_MSC_VER) && defined (_M_IX86) 159 | #define BLARGG_LITTLE_ENDIAN 1 160 | 161 | #endif 162 | #endif 163 | 164 | // BLARGG_NONPORTABLE (allow use of nonportable optimizations/features) 165 | #ifndef BLARGG_NONPORTABLE 166 | #define BLARGG_NONPORTABLE 0 167 | #endif 168 | #ifdef BLARGG_MOST_PORTABLE 169 | #error "BLARGG_MOST_PORTABLE has been removed; use BLARGG_NONPORTABLE." 170 | #endif 171 | 172 | // BLARGG_CPU_* 173 | #if !defined (BLARGG_CPU_POWERPC) && !defined (BLARGG_CPU_X86) 174 | #if defined (__powerc) 175 | #define BLARGG_CPU_POWERPC 1 176 | 177 | #elif defined (_MSC_VER) && defined (_M_IX86) 178 | #define BLARGG_CPU_X86 1 179 | 180 | #endif 181 | #endif 182 | 183 | #endif 184 | 185 | -------------------------------------------------------------------------------- /plugin/Source/gb_apu/blargg_source.h: -------------------------------------------------------------------------------- 1 | 2 | // By default, #included at beginning of library source files 3 | 4 | // Copyright (C) 2005 Shay Green. 5 | 6 | #ifndef BLARGG_SOURCE_H 7 | #define BLARGG_SOURCE_H 8 | 9 | // If debugging is enabled, abort program if expr is false. Meant for checking 10 | // internal state and consistency. A failed assertion indicates a bug in the module. 11 | // void assert( bool expr ); 12 | #include 13 | 14 | // If debugging is enabled and expr is false, abort program. Meant for checking 15 | // caller-supplied parameters and operations that are outside the control of the 16 | // module. A failed requirement indicates a bug outside the module. 17 | // void require( bool expr ); 18 | #undef require 19 | #define require( expr ) assert(( "unmet requirement", expr )) 20 | 21 | // Like printf() except output goes to debug log file. Might be defined to do 22 | // nothing (not even evaluate its arguments). 23 | // void dprintf( const char* format, ... ); 24 | #undef dprintf 25 | #define dprintf (1) ? ((void) 0) : (void) 26 | 27 | // If enabled, evaluate expr and if false, make debug log entry with source file 28 | // and line. Meant for finding situations that should be examined further, but that 29 | // don't indicate a problem. In all cases, execution continues normally. 30 | #undef check 31 | #define check( expr ) ((void) 0) 32 | 33 | // If expr returns non-NULL error string, return it from current function, otherwise continue. 34 | #define BLARGG_RETURN_ERR( expr ) do { \ 35 | blargg_err_t blargg_return_err_ = (expr); \ 36 | if ( blargg_return_err_ ) return blargg_return_err_; \ 37 | } while ( 0 ) 38 | 39 | // If ptr is NULL, return out of memory error string. 40 | #define BLARGG_CHECK_ALLOC( ptr ) do { if ( !(ptr) ) return "Out of memory"; } while ( 0 ) 41 | 42 | // Avoid any macros which evaluate their arguments multiple times 43 | #undef min 44 | #undef max 45 | 46 | // using const references generates crappy code, and I am currenly only using these 47 | // for built-in types, so they take arguments by value 48 | 49 | template 50 | inline T min( T x, T y ) 51 | { 52 | if ( x < y ) 53 | return x; 54 | return y; 55 | } 56 | 57 | template 58 | inline T max( T x, T y ) 59 | { 60 | if ( x < y ) 61 | return y; 62 | return x; 63 | } 64 | 65 | #endif 66 | 67 | --------------------------------------------------------------------------------