commit 5124f5e4ec9efa0adabb6ce57be2652e35c70b77
parent cea723280109532c7b64c27a7937f55fc0ab9c1a
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Mon, 13 Feb 2023 22:37:25 +0100
update open source version
Diffstat:
47 files changed, 1460 insertions(+), 508 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,8 +1,9 @@
-temp/
-vtune/
+/temp/
+/vtune/
*.aps
/.vs
-/out/build/x64-Debug
+/out/
/temp2/cmake_win64
/source/jucePlugin_24.01.2022.zip
/build_win64_VS2019.bat
+/out/
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -5,61 +5,17 @@ set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "OS X Architectures")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version")
-project(gearmulator VERSION 1.2.25)
+project(gearmulator VERSION 1.2.29)
include(base.cmake)
include(CTest)
-include(${CMAKE_CURRENT_LIST_DIR}/scripts/rclone.cmake)
-
-set(ASMJIT_STATIC TRUE)
-set(ASMJIT_NO_INSTALL TRUE)
-set(BUILD_SHARED_LIBS OFF)
option(${PROJECT_NAME}_BUILD_JUCEPLUGIN "Build Juce plugin" on)
option(${PROJECT_NAME}_BUILD_JUCEPLUGIN_CLAP "Build CLAP version of Juce plugin" on)
-add_subdirectory(source/dsp56300/source)
-add_subdirectory(source/synthLib)
-add_subdirectory(source/virusLib)
-add_subdirectory(source/virusConsoleLib)
-add_subdirectory(source/libresample)
-
-# ----------------- Try to install VST2 SDK
-
-set(VST2SDK_TESTFILE ${CMAKE_CURRENT_SOURCE_DIR}/source/vstsdk2.4.2/public.sdk/source/vst2.x/audioeffect.h)
-set(VST2SDK_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/source/vstsdk2.4.2/)
-
-if(NOT EXISTS ${VST2SDK_TESTFILE})
- if(EXISTS ${RCLONE_CONF})
- copyDataFrom("vstsdk2.4.2/" "${CMAKE_CURRENT_LIST_DIR}/source/vstsdk2.4.2/")
- else()
- message(WARNING "rclone.conf not found, unable to copy VST2 SDK")
- endif()
-endif()
-
-if(EXISTS ${VST2SDK_TESTFILE})
- set(JUCE_GLOBAL_VST2_SDK_PATH ${VST2SDK_FOLDER})
-endif()
-
-# ----------------- Juce based audio plugin
-
-set_property(GLOBAL PROPERTY USE_FOLDERS YES)
-if(${PROJECT_NAME}_BUILD_JUCEPLUGIN)
- set(JUCE_ENABLE_MODULE_SOURCE_GROUPS ON CACHE BOOL "" FORCE)
- add_subdirectory(source/JUCE)
- if(${PROJECT_NAME}_BUILD_JUCEPLUGIN_CLAP)
- add_subdirectory(source/clap-juce-extensions)
- endif()
- add_subdirectory(source/jucePluginLib)
- add_subdirectory(source/juceUiLib)
- add_subdirectory(source/jucePlugin)
- add_subdirectory(source/jucePluginEditorLib)
-endif()
-
-# ----------------- Console Programs
+# ----------------- source
-add_subdirectory(source/virusTestConsole)
-add_subdirectory(source/virusIntegrationTest)
+add_subdirectory(source)
# ----------------- CPack
diff --git a/scripts/Jenkinsfile b/scripts/Jenkinsfile
@@ -0,0 +1,189 @@
+pipeline {
+ agent { label "${params.AgentLabel}" }
+
+ environment {
+ GIT_URL = credentials('dsp56300_gitUrl')
+ BUILD_JUCE = needsJuce()
+ BUILD_FX_PLUGIN = buildFxPlugins()
+ CMAKE_BUILD_DIR = 'temp/cmake_jenkins'
+ }
+ parameters
+ {
+ string(name: 'Branch', defaultValue: "master", description: '')
+ string(name: 'AgentLabel', defaultValue: "git && cmake", description: '')
+ booleanParam(name: 'FXPlugins', defaultValue: false, description: '')
+ booleanParam(name: 'Deploy', defaultValue: true, description: '')
+ booleanParam(name: 'IntegrationTests', defaultValue: true, description: '')
+ booleanParam(name: 'Upload', defaultValue: false, description: '')
+ string(name: 'UploadFolder', defaultValue: "", description: '')
+ }
+ stages {
+ stage("Checkout") {
+ steps {
+ script {
+ currentBuild.displayName += " - ${params.AgentLabel} - ${params.Branch}"
+ currentBuild.description = "Integration Tests: ${params.IntegrationTests}\nDeploy: ${params.Deploy}\nUpload: ${params.Upload}"
+ }
+ doCheckout()
+ }
+ }
+ stage('Compile') {
+ steps {
+ cmakeBuild();
+ }
+ }
+ stage('Integration Tests') {
+ when {
+ expression {
+ return params.IntegrationTests
+ }
+ }
+ steps {
+ doIntegrationTests();
+ }
+ }
+ stage('Pack') {
+ when {
+ expression {
+ return params.Deploy || params.Upload
+ }
+ }
+ steps {
+ doPack();
+ }
+ }
+ stage('Deploy') {
+ when {
+ expression {
+ return params.Deploy
+ }
+ }
+ steps {
+ doDeploy();
+ }
+ }
+ stage('Upload') {
+ when {
+ expression {
+ return params.Upload
+ }
+ }
+ steps {
+ doUpload();
+ }
+ }
+ }
+}
+
+def genericSh(cmd)
+{
+ if (isUnix())
+ {
+ sh cmd
+ }
+ else
+ {
+ bat cmd
+ }
+}
+
+def formatArg(_arg)
+{
+ if(isUnix())
+ return '$' + _arg
+ else
+ return '%' + _arg + '%'
+}
+
+def deleteFile(name)
+{
+ if(isUnix())
+ sh "rm -f ${name}"
+ else
+ bat "del ${name}"
+}
+
+def supportTI()
+{
+ return params.Branch == 'snow' ? 'ON' : 'OFF'
+}
+def needsJuce()
+{
+ return params.Deploy || params.Upload ? 'ON' : 'OFF'
+}
+def buildFxPlugins()
+{
+ return params.FXPlugins ? 'ON' : 'OFF'
+}
+def cmakeBuildDir()
+{
+ return "${CMAKE_BUILD_DIR}_${params.Branch}"
+}
+
+def doCheckout()
+{
+ checkout(
+ [$class: 'GitSCM'
+ , branches: [[name: "*/${params.Branch}"]]
+ , extensions: [
+ [$class: 'SubmoduleOption'
+ , disableSubmodules: false
+ , parentCredentials: false
+ , recursiveSubmodules: true
+ , reference: ''
+ , trackingSubmodules: false]]
+ , userRemoteConfigs: [[ url: GIT_URL ]]
+ ]
+ )
+}
+
+def cmakeBuild()
+{
+ def buildDir = cmakeBuildDir();
+ def ti = supportTI();
+
+ dir(cmakeBuildDir()) {
+ deleteFile('CMakeCache.txt') // we need to do this to be able to change option in the command line below. Otherwise, they won't change for an existing project
+ }
+
+ genericSh "cmake . -B ${buildDir} -Dgearmulator_BUILD_JUCEPLUGIN=${BUILD_JUCE} -Dgearmulator_BUILD_FX_PLUGIN=${BUILD_FX_PLUGIN} -DCMAKE_BUILD_TYPE=Release -DVIRUS_SUPPORT_TI=${ti}"
+
+ dir(cmakeBuildDir()) {
+ genericSh 'cmake --build . --config Release'
+ }
+}
+
+def doIntegrationTests()
+{
+ withCredentials([file(credentialsId: 'rclone_dsp56300_conf', variable: 'RCLONE_CONF')]) {
+ dir(cmakeBuildDir()) {
+ genericSh "ctest -C Release -VV --output-on-failure"
+ }
+ }
+}
+
+def doPack()
+{
+ dir(cmakeBuildDir()) {
+ genericSh 'cmake -P ../../scripts/pack.cmake'
+ }
+}
+
+def copyArtefacts(local, remote)
+{
+ withCredentials([file(credentialsId: 'rclone_dsp56300_conf', variable: 'RCLONE_CONF')]) {
+ dir(cmakeBuildDir()) {
+ genericSh "cmake -DUPLOAD_LOCAL=${local} -DUPLOAD_REMOTE=${remote} -DFOLDER=${params.Branch}/${params.UploadFolder} -P ../../scripts/deploy.cmake"
+ }
+ }
+}
+
+def doDeploy()
+{
+ copyArtefacts(1,0)
+}
+
+def doUpload()
+{
+ copyArtefacts(0,1)
+}
diff --git a/scripts/JenkinsfileMulti b/scripts/JenkinsfileMulti
@@ -0,0 +1,87 @@
+pipeline {
+ agent any
+ parameters
+ {
+ booleanParam(name: 'LinuxARM', defaultValue: true, description: '')
+ booleanParam(name: 'LinuxX86', defaultValue: true, description: '')
+ booleanParam(name: 'Win', defaultValue: true, description: '')
+ booleanParam(name: 'Mac', defaultValue: true, description: '')
+ choice(name: 'Branch', choices: ['master', 'snow'], description: '')
+ booleanParam(name: 'FXPlugins', defaultValue: false, description: '')
+ booleanParam(name: 'IntegrationTests', defaultValue: false, description: '')
+ booleanParam(name: 'Deploy', defaultValue: true, description: '')
+ booleanParam(name: 'Upload', defaultValue: false, description: '')
+ choice(name: 'UploadFolder', choices: ['', '/alpha', '/beta'], description: '')
+ }
+ stages {
+ stage('Prepare') {
+ steps {
+ script {
+ currentBuild.displayName += " - ${params.Branch} -"
+
+ if(params.LinuxARM) currentBuild.displayName += " La"
+ if(params.LinuxX86) currentBuild.displayName += " Lx"
+ if(params.Mac) currentBuild.displayName += " M"
+ if(params.Win) currentBuild.displayName += " W"
+ if(params.FXPlugins) currentBuild.displayName += " FX"
+ if(params.IntegrationTests) currentBuild.displayName += " i"
+ if(params.Deploy) currentBuild.displayName += " d"
+ if(params.Upload) currentBuild.displayName += " u"
+
+ currentBuild.description = "Integration Tests: ${params.IntegrationTests}\nDeploy: ${params.Deploy}"
+ }
+ }
+ }
+ stage('Parallel Build') {
+ parallel {
+ stage('Windows') {
+ when { expression { return params.Win } }
+ steps {
+ startBuildJob('win')
+ }
+ }
+ stage('Mac') {
+ when { expression { return params.Mac } }
+ steps {
+ startBuildJob('mac')
+ }
+ }
+ stage('Linux aarch64') {
+ when { expression { return params.LinuxARM } }
+ steps {
+ startBuildJob('linux && arm')
+ }
+ }
+ stage('Linux x86') {
+ when { expression { return params.LinuxX86 } }
+ steps {
+ startBuildJob('linux && x86')
+ }
+ }
+ }
+ }
+ }
+ post {
+ always {
+ mqttNotification(
+ brokerUrl: "${env.MQTT_URL}",
+ credentialsId: 'mqtt',
+ message: currentBuild.displayName + ' - ' + currentBuild.currentResult)
+ }
+ }
+}
+
+def startBuildJob(label)
+{
+// build job: 'dsp56300', parameters: [[$class: 'LabelParameterValue', name: 'Agent', label: "win" ]]
+ build job: 'dsp56300', parameters:
+ [
+ [$class: 'StringParameterValue', name: 'AgentLabel', value: "${label}"],
+ [$class: 'StringParameterValue', name: 'Branch', value: params.Branch],
+ [$class: 'BooleanParameterValue', name: 'Deploy', value: params.Deploy],
+ [$class: 'BooleanParameterValue', name: 'Upload', value: params.Upload],
+ [$class: 'BooleanParameterValue', name: 'IntegrationTests', value: params.IntegrationTests],
+ [$class: 'BooleanParameterValue', name: 'FXPlugins', value: params.FXPlugins],
+ [$class: 'StringParameterValue', name: 'UploadFolder', value: params.UploadFolder],
+ ]
+}
diff --git a/scripts/io.cmake b/scripts/io.cmake
@@ -0,0 +1,7 @@
+macro(removeGlob PATTERN)
+ file(GLOB filesToRemove LIST_DIRECTORIES false "${PATTERN}")
+ foreach(f ${filesToRemove})
+ message(STATUS "Removing file ${f}")
+ file(REMOVE "${f}")
+ endforeach()
+endmacro()
diff --git a/scripts/pack.cmake b/scripts/pack.cmake
@@ -1,7 +1,10 @@
+include(${CMAKE_CURRENT_LIST_DIR}/io.cmake)
+
message(STATUS "Removing old packages")
-file(REMOVE *.zip)
-file(REMOVE *.deb)
-file(REMOVE *.rpm)
+
+removeGlob("*.zip")
+removeGlob("*.deb")
+removeGlob("*.rpm")
macro(pack GENERATOR)
message(STATUS "Packaging ${GENERATOR}")
diff --git a/scripts/rclone.cmake b/scripts/rclone.cmake
@@ -10,16 +10,37 @@ if(NOT RCLONE_CONF)
endif()
endif()
+set(postfix "_partial")
+
macro(copyArtefacts TARGET FOLDER)
set(RCLONE_RESULT 0)
- execute_process(COMMAND rclone --config ${RCLONE_CONF} copy
- --transfers=1 -v --include *.zip --include *.deb --include *.rpm --min-size 100 --max-depth 1 "${ROOT_DIR}" "${TARGET}/${FOLDER}"
- COMMAND_ECHO STDOUT
- RESULT_VARIABLE RCLONE_RESULT
- WORKING_DIRECTORY ${ROOT_DIR})
- if(RCLONE_RESULT)
- message(FATAL_ERROR "Failed to execute rclone: " ${CMD_RESULT})
- endif()
+ file(GLOB files LIST_DIRECTORIES false "${ROOT_DIR}/*.zip" "${ROOT_DIR}/*.deb" "${ROOT_DIR}/*.rpm")
+ foreach(fileLocal ${files})
+
+ file(SIZE ${fileLocal} fileSize)
+ if(${fileSize} GREATER 100)
+ file(RELATIVE_PATH remoteFile "${ROOT_DIR}" ${fileLocal})
+
+ file(RELATIVE_PATH remoteFile "${ROOT_DIR}" ${fileLocal})
+ set(remoteFileTemp "${remoteFile}${postfix}")
+
+ set(RCLONE_RESULT 0)
+ execute_process(COMMAND rclone -v --config ${RCLONE_CONF} copyto
+ "${fileLocal}" "${TARGET}/${FOLDER}/${remoteFileTemp}"
+ COMMAND_ECHO STDOUT RESULT_VARIABLE RCLONE_RESULT WORKING_DIRECTORY ${ROOT_DIR})
+ if(RCLONE_RESULT)
+ message(FATAL_ERROR "Failed to execute rclone: " ${CMD_RESULT})
+ endif()
+
+ set(RCLONE_RESULT 0)
+ execute_process(COMMAND rclone -v --config ${RCLONE_CONF} moveto
+ "${TARGET}/${FOLDER}/${remoteFileTemp}" "${TARGET}/${FOLDER}/${remoteFile}"
+ COMMAND_ECHO STDOUT RESULT_VARIABLE RCLONE_RESULT WORKING_DIRECTORY ${ROOT_DIR})
+ if(RCLONE_RESULT)
+ message(FATAL_ERROR "Failed to execute rclone: " ${CMD_RESULT})
+ endif()
+ endif()
+ endforeach()
endmacro()
macro(copyDataFrom FROM TO)
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
@@ -0,0 +1,51 @@
+cmake_minimum_required(VERSION 3.15)
+
+option(${CMAKE_PROJECT_NAME}_BUILD_JUCEPLUGIN "Build Juce plugin" on)
+option(${CMAKE_PROJECT_NAME}_BUILD_JUCEPLUGIN_CLAP "Build CLAP version of Juce plugin" on)
+
+option(${CMAKE_PROJECT_NAME}_SYNTH_OSIRUS "Build Osirus" on)
+
+# ----------------- DSP56300 emulator
+
+set(ASMJIT_STATIC TRUE)
+set(ASMJIT_NO_INSTALL TRUE)
+set(BUILD_SHARED_LIBS OFF)
+
+add_subdirectory(dsp56300/source)
+
+# ----------------- Common libraries used by all synths
+
+add_subdirectory(synthLib)
+add_subdirectory(libresample)
+
+# ----------------- Try to install VST2 SDK
+
+include(findvst2.cmake)
+
+# ----------------- Juce based audio plugin dependencies
+
+set_property(GLOBAL PROPERTY USE_FOLDERS YES)
+if(${CMAKE_PROJECT_NAME}_BUILD_JUCEPLUGIN)
+ set(JUCE_ENABLE_MODULE_SOURCE_GROUPS ON CACHE BOOL "" FORCE)
+ add_subdirectory(JUCE)
+ if(${CMAKE_PROJECT_NAME}_BUILD_JUCEPLUGIN_CLAP)
+ add_subdirectory(clap-juce-extensions)
+ endif()
+ add_subdirectory(jucePluginLib)
+ add_subdirectory(juceUiLib)
+ add_subdirectory(jucePluginEditorLib)
+
+ include(juce.cmake)
+endif()
+
+# ----------------- Synth Osirus
+
+if(${CMAKE_PROJECT_NAME}_SYNTH_OSIRUS)
+ add_subdirectory(virusLib)
+ add_subdirectory(virusConsoleLib)
+ add_subdirectory(virusTestConsole)
+ add_subdirectory(virusIntegrationTest)
+ if(${CMAKE_PROJECT_NAME}_BUILD_JUCEPLUGIN)
+ add_subdirectory(jucePlugin)
+ endif()
+endif()
diff --git a/source/findvst2.cmake b/source/findvst2.cmake
@@ -0,0 +1,16 @@
+include(${CMAKE_CURRENT_LIST_DIR}/../scripts/rclone.cmake)
+
+set(VST2SDK_TESTFILE ${CMAKE_CURRENT_LIST_DIR}/vstsdk2.4.2/public.sdk/source/vst2.x/audioeffect.h)
+set(VST2SDK_FOLDER ${CMAKE_CURRENT_LIST_DIR}/vstsdk2.4.2/)
+
+if(NOT EXISTS ${VST2SDK_TESTFILE})
+ if(EXISTS ${RCLONE_CONF})
+ copyDataFrom("vstsdk2.4.2/" "${VST2SDK_FOLDER}")
+ else()
+ message(WARNING "rclone.conf not found, unable to copy VST2 SDK")
+ endif()
+endif()
+
+if(EXISTS ${VST2SDK_TESTFILE})
+ set(JUCE_GLOBAL_VST2_SDK_PATH ${VST2SDK_FOLDER})
+endif()
diff --git a/source/juce.cmake b/source/juce.cmake
@@ -0,0 +1,104 @@
+option(${CMAKE_PROJECT_NAME}_BUILD_FX_PLUGIN "Build FX plugin variants" off)
+
+set(USE_CLAP ${CMAKE_PROJECT_NAME}_BUILD_JUCEPLUGIN_CLAP)
+
+if(JUCE_GLOBAL_VST2_SDK_PATH)
+ set(VST "VST")
+else()
+ set(VST "")
+endif()
+
+macro(createJucePlugin targetName productName isSynth plugin4CC binaryDataProject synthLibProject)
+ juce_add_plugin(${targetName}
+ # VERSION ... # Set this if the plugin version is different to the project version
+ # ICON_BIG ... # ICON_* arguments specify a path to an image file to use as an icon for the Standalone
+ # ICON_SMALL ...
+ COMPANY_NAME "The Usual Suspects" # Specify the name of the plugin's author
+ IS_SYNTH ${isSynth} # Is this a synth or an effect?
+ NEEDS_MIDI_INPUT TRUE # Does the plugin need midi input?
+ NEEDS_MIDI_OUTPUT TRUE # Does the plugin need midi output?
+ IS_MIDI_EFFECT FALSE # Is this plugin a MIDI effect?
+ EDITOR_WANTS_KEYBOARD_FOCUS TRUE # Does the editor need keyboard focus?
+ COPY_PLUGIN_AFTER_BUILD FALSE # Should the plugin be installed to a default location after building?
+ PLUGIN_MANUFACTURER_CODE TusP # A four-character manufacturer id with at least one upper-case character
+ PLUGIN_CODE ${plugin4CC} # A unique four-character plugin id with exactly one upper-case character
+ # GarageBand 10.3 requires the first letter to be upper-case, and the remaining letters to be lower-case
+ FORMATS AU VST3 ${VST} Standalone # The formats to build. Other valid formats are: AAX Unity VST AU AUv3
+ PRODUCT_NAME ${productName} # The name of the final executable, which can differ from the target name
+ )
+
+ target_sources(${targetName} PRIVATE ${SOURCES})
+
+ source_group("source" FILES ${SOURCES})
+
+ target_compile_definitions(${targetName}
+ PUBLIC
+ # JUCE_WEB_BROWSER and JUCE_USE_CURL would be on by default, but you might not need them.
+ JUCE_WEB_BROWSER=0 # If you remove this, add `NEEDS_WEB_BROWSER TRUE` to the `juce_add_plugin` call
+ JUCE_USE_CURL=0 # If you remove this, add `NEEDS_CURL TRUE` to the `juce_add_plugin` call
+ JUCE_VST3_CAN_REPLACE_VST2=0
+ JUCE_WIN_PER_MONITOR_DPI_AWARE=0
+ )
+
+ target_link_libraries(${targetName}
+ PRIVATE
+ ${binaryDataProject}
+ jucePluginEditorLib
+ juce::juce_audio_utils
+ juce::juce_cryptography
+ PUBLIC
+ ${synthLibProject}
+ #juce::juce_recommended_config_flags
+ #juce::juce_recommended_lto_flags
+ #juce::juce_recommended_warning_flags
+ )
+
+ set(clapFeatures "")
+ if(${isSynth})
+ list(APPEND clapFeatures instrument synthesizer)
+ else()
+ list(APPEND clapFeatures audio-effect synthesizer multi-effects)
+ endif()
+
+ if(USE_CLAP)
+ clap_juce_extensions_plugin(TARGET ${targetName}
+ CLAP_ID "com.theusualsuspects.${plugin4CC}"
+ CLAP_FEATURES ${clapFeatures}
+ CLAP_SUPPORT_URL "https://dsp56300.wordpress.com"
+ CLAP_MANUAL_URL "https://dsp56300.wordpress.com"
+ )
+ endif()
+
+ if(UNIX AND NOT APPLE)
+ target_link_libraries(${targetName} PUBLIC -static-libgcc -static-libstdc++)
+ endif()
+
+ if(MSVC OR APPLE)
+ if(JUCE_GLOBAL_VST2_SDK_PATH)
+ install(TARGETS ${targetName}_VST DESTINATION . COMPONENT ${productName}-VST2)
+ endif()
+ install(TARGETS ${targetName}_VST3 DESTINATION . COMPONENT ${productName}-VST3)
+ if(APPLE)
+ install(TARGETS ${targetName}_AU DESTINATION . COMPONENT ${productName}-AU)
+ endif()
+ if(USE_CLAP)
+ install(TARGETS ${targetName}_CLAP DESTINATION . COMPONENT ${productName}-CLAP)
+ endif()
+ elseif(UNIX)
+ if(JUCE_GLOBAL_VST2_SDK_PATH)
+ install(TARGETS ${targetName}_VST LIBRARY DESTINATION /usr/local/lib/vst/ COMPONENT ${productName}-VST2)
+ endif()
+ install(TARGETS ${targetName}_VST3 LIBRARY DESTINATION /usr/local/lib/vst3/ COMPONENT ${productName}-VST3)
+ if(USE_CLAP)
+ install(TARGETS ${targetName}_CLAP LIBRARY DESTINATION /usr/local/lib/clap/ COMPONENT ${productName}-CLAP)
+ endif()
+ endif()
+endmacro()
+
+macro(createJucePluginWithFX targetName productName plugin4CCSynth plugin4CCFX binaryDataProject synthLibProject)
+ createJucePlugin(${targetName} "${productName}" TRUE "${plugin4CCSynth}" ${binaryDataProject} ${synthLibProject})
+
+ if(${CMAKE_PROJECT_NAME}_BUILD_FX_PLUGIN)
+ createJucePlugin(${targetName}_FX "${productName}FX" FALSE "${plugin4CCFX}" ${binaryDataProject} ${synthLibProject})
+ endif()
+endmacro()
diff --git a/source/jucePlugin/CMakeLists.txt b/source/jucePlugin/CMakeLists.txt
@@ -1,19 +1,8 @@
-
cmake_minimum_required(VERSION 3.15)
project(jucePlugin VERSION ${CMAKE_PROJECT_VERSION})
-option(${CMAKE_PROJECT_NAME}_BUILD_FX_PLUGIN "Build FX plugin variants" off)
-
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/version.h)
-set(USE_CLAP ${CMAKE_PROJECT_NAME}_BUILD_JUCEPLUGIN_CLAP)
-
-if(JUCE_GLOBAL_VST2_SDK_PATH)
- set(VST "VST")
-else()
- set(VST "")
-endif()
-
set(SOURCES
parameterDescriptions_C.json
ParameterNames.h
@@ -24,9 +13,7 @@ set(SOURCES
VirusController.cpp
VirusController.h
version.h
-)
-set(SOURCES_UI3
ui3/ControllerLinks.cpp
ui3/ControllerLinks.h
ui3/FxPage.cpp
@@ -43,106 +30,12 @@ set(SOURCES_UI3
# https://forum.juce.com/t/help-needed-using-binarydata-with-cmake-juce-6/40486
# "This might be because the BinaryData files are generated during the build, so the IDE may not be able to find them until the build has been run once (and even then, some IDEs might need a bit of a nudge to re-index the binary directory…)"
-SET(ASSETS
- "parameterDescriptions_C.json"
-)
+SET(ASSETS "parameterDescriptions_C.json")
include(skins/Galaxpel/assets.cmake)
include(skins/Hoverland/assets.cmake)
include(skins/Trancy/assets.cmake)
-macro(createJucePlugin targetName productName isSynth plugin4CC binaryDataProject componentName)
- juce_add_plugin(${targetName}
- # VERSION ... # Set this if the plugin version is different to the project version
- # ICON_BIG ... # ICON_* arguments specify a path to an image file to use as an icon for the Standalone
- # ICON_SMALL ...
- COMPANY_NAME "The Usual Suspects" # Specify the name of the plugin's author
- IS_SYNTH ${isSynth} # Is this a synth or an effect?
- NEEDS_MIDI_INPUT TRUE # Does the plugin need midi input?
- NEEDS_MIDI_OUTPUT TRUE # Does the plugin need midi output?
- IS_MIDI_EFFECT FALSE # Is this plugin a MIDI effect?
- EDITOR_WANTS_KEYBOARD_FOCUS TRUE # Does the editor need keyboard focus?
- COPY_PLUGIN_AFTER_BUILD FALSE # Should the plugin be installed to a default location after building?
- PLUGIN_MANUFACTURER_CODE TusP # A four-character manufacturer id with at least one upper-case character
- PLUGIN_CODE ${plugin4CC} # A unique four-character plugin id with exactly one upper-case character
- # GarageBand 10.3 requires the first letter to be upper-case, and the remaining letters to be lower-case
- FORMATS AU VST3 ${VST} Standalone # The formats to build. Other valid formats are: AAX Unity VST AU AUv3
- PRODUCT_NAME ${productName} # The name of the final executable, which can differ from the target name
- )
-
- target_sources(${targetName} PRIVATE ${SOURCES} ${SOURCES_UI3})
-
- source_group("source" FILES ${SOURCES})
- source_group("source\\ui3" FILES ${SOURCES_UI3})
-
- target_compile_definitions(${targetName}
- PUBLIC
- # JUCE_WEB_BROWSER and JUCE_USE_CURL would be on by default, but you might not need them.
- JUCE_WEB_BROWSER=0 # If you remove this, add `NEEDS_WEB_BROWSER TRUE` to the `juce_add_plugin` call
- JUCE_USE_CURL=0 # If you remove this, add `NEEDS_CURL TRUE` to the `juce_add_plugin` call
- JUCE_VST3_CAN_REPLACE_VST2=0
- JUCE_WIN_PER_MONITOR_DPI_AWARE=0
- )
-
- target_link_libraries(${targetName}
- PRIVATE
- ${binaryDataProject}
- jucePluginEditorLib
- juce::juce_audio_utils
- juce::juce_cryptography
- PUBLIC
- virusLib
- #juce::juce_recommended_config_flags
- #juce::juce_recommended_lto_flags
- #juce::juce_recommended_warning_flags
- )
-
- set(clapFeatures "")
- if(${isSynth})
- list(APPEND clapFeatures instrument synthesizer)
- else()
- list(APPEND clapFeatures audio-effect synthesizer multi-effects)
- endif()
-
- if(USE_CLAP)
- clap_juce_extensions_plugin(TARGET ${targetName}
- CLAP_ID "com.theusualsuspects.${plugin4CC}"
- CLAP_FEATURES ${clapFeatures}
- CLAP_SUPPORT_URL "https://dsp56300.wordpress.com"
- CLAP_MANUAL_URL "https://dsp56300.wordpress.com"
- )
- endif()
-
- if(UNIX AND NOT APPLE)
- target_link_libraries(${targetName} PUBLIC -static-libgcc -static-libstdc++)
- endif()
-
- if(MSVC OR APPLE)
- if(JUCE_GLOBAL_VST2_SDK_PATH)
- install(TARGETS ${targetName}_VST DESTINATION . COMPONENT VST2${componentName})
- endif()
- install(TARGETS ${targetName}_VST3 DESTINATION . COMPONENT VST3${componentName})
- if(APPLE)
- install(TARGETS ${targetName}_AU DESTINATION . COMPONENT AU${componentName})
- endif()
- if(USE_CLAP)
- install(TARGETS ${targetName}_CLAP DESTINATION . COMPONENT CLAP${componentName})
- endif()
- elseif(UNIX)
- if(JUCE_GLOBAL_VST2_SDK_PATH)
- install(TARGETS ${targetName}_VST LIBRARY DESTINATION /usr/local/lib/vst/ COMPONENT VST2${componentName})
- endif()
- install(TARGETS ${targetName}_VST3 LIBRARY DESTINATION /usr/local/lib/vst3/ COMPONENT VST3${componentName})
- if(USE_CLAP)
- install(TARGETS ${targetName}_CLAP LIBRARY DESTINATION /usr/local/lib/clap/ COMPONENT CLAP${componentName})
- endif()
- endif()
-endmacro()
-
-juce_add_binary_data(jucePlugin_BinaryData${postfix} SOURCES ${ASSETS} ${ASSETS_VirusC_Galaxpel} ${ASSETS_VirusC_Hoverland} ${ASSETS_VirusC_Trancy})
+juce_add_binary_data(jucePlugin_BinaryData SOURCES ${ASSETS} ${ASSETS_VirusC_Galaxpel} ${ASSETS_VirusC_Hoverland} ${ASSETS_VirusC_Trancy})
-createJucePlugin(jucePlugin "DSP56300Emu" TRUE "TusV" jucePlugin_BinaryData${postfix} "")
-
-if(${CMAKE_PROJECT_NAME}_BUILD_FX_PLUGIN)
- createJucePlugin(jucePlugin_FX "DSP56300EmuFX" FALSE "TusF" jucePlugin_BinaryData${postfix} "_FX")
-endif()
+createJucePluginWithFX(jucePlugin "Osirus" "TusV" "TusF" jucePlugin_BinaryData virusLib)
diff --git a/source/jucePlugin/PluginProcessor.cpp b/source/jucePlugin/PluginProcessor.cpp
@@ -26,10 +26,9 @@ AudioPluginAudioProcessor::AudioPluginAudioProcessor() :
.withOutput("Out 2", juce::AudioChannelSet::stereo(), true)
.withOutput("Out 3", juce::AudioChannelSet::stereo(), true)
#endif
- , getConfigOptions()),
- m_romName(virusLib::ROMFile::findROM()),
- m_rom(m_romName),
- m_device(m_rom), m_plugin(&m_device)
+ , getConfigOptions())
+ , m_rom(std::string())
+ , m_device(m_rom), m_plugin(&m_device)
{
m_clockTempoParam = getController().getParameterIndexByName(Virus::g_paramClockTempo);
diff --git a/source/jucePlugin/PluginProcessor.h b/source/jucePlugin/PluginProcessor.h
@@ -50,7 +50,7 @@ public:
//
std::string getRomName() const
{
- return juce::File(juce::String(m_romName)).getFileNameWithoutExtension().toStdString();
+ return juce::File(juce::String(m_rom.getFilename())).getFileNameWithoutExtension().toStdString();
}
virusLib::ROMFile::Model getModel() const
{
@@ -74,7 +74,6 @@ private:
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioPluginAudioProcessor)
- std::string m_romName;
virusLib::ROMFile m_rom;
virusLib::Device m_device;
synthLib::Plugin m_plugin;
diff --git a/source/jucePlugin/VirusController.cpp b/source/jucePlugin/VirusController.cpp
@@ -49,8 +49,9 @@ namespace Virus
registerParams(p);
// add lambda to enforce updating patches when virus switch from/to multi/single.
- const auto& params = findSynthParam(0, 0x72, 0x7a);
- for (const auto& parameter : params)
+ const auto paramIdx = getParameterIndexByName(g_paramPlayMode);
+ auto* parameter = getParameter(paramIdx);
+ if(parameter)
{
parameter->onValueChanged.emplace_back(std::make_pair(0, [this] {
const uint8_t prg = isMultiMode() ? 0x0 : virusLib::SINGLE;
diff --git a/source/jucePlugin/ui3/PatchBrowser.cpp b/source/jucePlugin/ui3/PatchBrowser.cpp
@@ -9,6 +9,7 @@
#include "../VirusController.h"
#include "../../synthLib/midiToSysex.h"
+#include "../../synthLib/os.h"
using namespace juce;
@@ -198,6 +199,19 @@ namespace genericVirusUI
}
}
+ bool PatchBrowser::loadUnkownData(std::vector<std::vector<uint8_t>>& _result, const std::string& _filename)
+ {
+ std::vector<uint8_t> data;
+
+ if(!synthLib::readFile(data, _filename))
+ return jucePluginEditorLib::PatchBrowser::loadUnkownData(_result, _filename);
+
+ if(virusLib::Device::parsePowercorePreset(_result, data))
+ return true;
+
+ return jucePluginEditorLib::PatchBrowser::loadUnkownData(_result, _filename);
+ }
+
std::string PatchBrowser::getCellText(const jucePluginEditorLib::Patch& _patch, int columnId)
{
auto& rowElement = static_cast<const Patch&>(_patch);
diff --git a/source/jucePlugin/ui3/PatchBrowser.h b/source/jucePlugin/ui3/PatchBrowser.h
@@ -40,6 +40,8 @@ namespace genericVirusUI
bool activatePatch(jucePluginEditorLib::Patch& _patch) override;
int comparePatches(int _columnId, const jucePluginEditorLib::Patch& a, const jucePluginEditorLib::Patch& b) const override;
+ bool loadUnkownData(std::vector<std::vector<uint8_t>>& _result, const std::string& _filename) override;
+
std::string getCellText(const jucePluginEditorLib::Patch& _patch, int _columnId) override;
void loadRomBank(uint32_t _bankIndex);
diff --git a/source/jucePlugin/ui3/VirusEditor.cpp b/source/jucePlugin/ui3/VirusEditor.cpp
@@ -55,8 +55,8 @@ namespace genericVirusUI
if(m_playModeSingle && m_playModeMulti)
{
- m_playModeSingle->onClick = [this]{ setPlayMode(virusLib::PlayMode::PlayModeSingle); };
- m_playModeMulti->onClick = [this]{ setPlayMode(virusLib::PlayMode::PlayModeMulti); };
+ m_playModeSingle->onClick = [this]{ if(m_playModeSingle->getToggleState()) setPlayMode(virusLib::PlayMode::PlayModeSingle); };
+ m_playModeMulti->onClick = [this]{ if(m_playModeMulti->getToggleState()) setPlayMode(virusLib::PlayMode::PlayModeMulti); };
}
else
{
diff --git a/source/jucePluginEditorLib/patchbrowser.cpp b/source/jucePluginEditorLib/patchbrowser.cpp
@@ -13,7 +13,7 @@ namespace jucePluginEditorLib
PatchBrowser::PatchBrowser(const Editor& _editor, pluginLib::Controller& _controller, juce::PropertiesFile& _config, const std::initializer_list<ColumnDefinition>& _columns)
: m_editor(_editor), m_controller(_controller)
, m_properties(_config)
- , m_fileFilter("*.syx;*.mid;*.midi", "*", "Patch Dumps")
+ , m_fileFilter("*.syx;*.mid;*.midi;*.vstpreset;*.fxb;*.fxp", "*", "Patch Dumps")
, m_bankList(FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles, File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile), &m_fileFilter, nullptr)
, m_search("Search Box")
, m_patchList("Patch Browser")
@@ -94,7 +94,7 @@ namespace jucePluginEditorLib
bool PatchBrowser::load(PatchList& _result, std::set<std::string>* _dedupeChecksums, const std::vector<uint8_t>& _data)
{
- auto* patch = createPatch();
+ auto patch = std::shared_ptr<Patch>(createPatch());
patch->sysex = _data;
patch->progNumber = static_cast<int>(_result.size());
@@ -103,7 +103,7 @@ namespace jucePluginEditorLib
if (!_dedupeChecksums)
{
- _result.push_back(std::shared_ptr<Patch>(patch));
+ _result.emplace_back(std::move(patch));
}
else
{
@@ -112,13 +112,19 @@ namespace jucePluginEditorLib
if (_dedupeChecksums->find(md5) == _dedupeChecksums->end())
{
_dedupeChecksums->insert(md5);
- _result.push_back(std::shared_ptr<Patch>(patch));
+ _result.emplace_back(std::move(patch));
}
}
return true;
}
+ bool PatchBrowser::loadUnkownData(std::vector<std::vector<unsigned char>>& _result, const std::string& _filename)
+ {
+ synthLib::MidiToSysex::extractSysexFromFile(_result, _filename);
+ return !_result.empty();
+ }
+
uint32_t PatchBrowser::loadBankFile(PatchList& _result, std::set<std::string>* _dedupeChecksums, const File& file)
{
const auto ext = file.getFileExtension().toLowerCase();
@@ -156,7 +162,10 @@ namespace jucePluginEditorLib
return load(_result, _dedupeChecksums, packets);
}
- return 0;
+ std::vector<std::vector<uint8_t>> packets;
+ if(!loadUnkownData(packets, file.getFullPathName().toStdString()))
+ return false;
+ return load(_result, _dedupeChecksums, packets);
}
bool PatchBrowser::selectPrevNextPreset(int _dir)
@@ -221,7 +230,7 @@ namespace jucePluginEditorLib
{
const auto ext = file.getFileExtension().toLowerCase();
- if (file.existsAsFile() && ext == ".syx" || ext == ".midi" || ext == ".mid")
+ if (file.existsAsFile() && ext == ".syx" || ext == ".midi" || ext == ".mid" || ext == ".fxb" || ext == ".fxp" || ext == ".vstpreset")
{
m_properties.setValue("virus_selected_file", file.getFileName());
diff --git a/source/jucePluginEditorLib/patchbrowser.h b/source/jucePluginEditorLib/patchbrowser.h
@@ -44,6 +44,7 @@ namespace jucePluginEditorLib
uint32_t load(PatchList& _result, std::set<std::string>* _dedupeChecksums, const std::vector<std::vector<uint8_t>>& _packets);
bool load(PatchList& _result, std::set<std::string>* _dedupeChecksums, const std::vector<uint8_t>& _data);
+ virtual bool loadUnkownData(std::vector<std::vector<uint8_t>>& _result, const std::string& _filename);
uint32_t loadBankFile(PatchList& _result, std::set<std::string>* _dedupeChecksums, const juce::File& file);
protected:
diff --git a/source/synthLib/CMakeLists.txt b/source/synthLib/CMakeLists.txt
@@ -9,6 +9,7 @@ set(SOURCES
configFile.cpp configFile.h
device.cpp device.h
deviceTypes.h
+ hybridcontainer.h
midiBufferParser.cpp midiBufferParser.h
midiToSysex.cpp midiToSysex.h
midiTypes.h
diff --git a/source/synthLib/device.h b/source/synthLib/device.h
@@ -28,6 +28,7 @@ namespace synthLib
virtual bool isValid() const = 0;
virtual bool getState(std::vector<uint8_t>& _state, StateType _type) = 0;
virtual bool setState(const std::vector<uint8_t>& _state, StateType _type) = 0;
+ virtual bool setStateFromUnknownCustomData(const std::vector<uint8_t>& _state) { return false; }
virtual uint32_t getChannelCountIn() = 0;
virtual uint32_t getChannelCountOut() = 0;
diff --git a/source/synthLib/hybridcontainer.h b/source/synthLib/hybridcontainer.h
@@ -0,0 +1,216 @@
+#pragma once
+
+#include <vector>
+#include <array>
+
+namespace syntLib
+{
+ template<typename T, size_t S> class HybridVector
+ {
+ public:
+ HybridVector(const HybridVector& _source) : m_array(_source.m_array), m_size(_source.m_size), m_vector(_source.m_vector ? new std::vector<T>(*_source.m_vector) : nullptr)
+ {
+ }
+ HybridVector(HybridVector&& _source) noexcept : m_array(std::move(_source.m_array)), m_size(_source.m_size), m_vector(_source.m_vector)
+ {
+ _source.m_size = 0;
+ _source.m_vector = nullptr;
+ }
+ ~HybridVector()
+ {
+ delete m_vector;
+ }
+ HybridVector& operator = (HybridVector&& _source) noexcept
+ {
+ m_array = _source.m_array;
+ m_size = _source.m_size;
+ m_vector = _source.m_vector;
+
+ _source.m_size = 0;
+ _source.m_vector = nullptr;
+
+ return *this;
+ }
+ HybridVector& operator = (const HybridVector& _source) noexcept // NOLINT(bugprone-unhandled-self-assignment) WTF, I handle it?!
+ {
+ if(&_source == this)
+ return *this;
+
+ m_array = _source.m_array;
+ m_size = _source.m_size;
+
+ if(_source.m_vector)
+ m_vector = new std::vector<T>(*_source.m_vector);
+
+ return *this;
+ }
+
+ HybridVector& operator = (const std::vector<T>& _source) noexcept
+ {
+ m_size = _source.size();
+
+ if(_source.size() <= S)
+ {
+ std::copy_n(_source.begin(), _source.size(), m_array.begin());
+ }
+ else
+ {
+ if(!m_vector)
+ {
+ m_vector = new std::vector<T>(_source);
+ }
+ else
+ {
+ m_vector->clear();
+ m_vector->reserve(_source.size());
+ m_vector->insert(m_vector->begin(), _source.begin(), _source.end());
+ }
+ }
+ return *this;
+ }
+
+ HybridVector& operator = (std::vector<T>&& _source) noexcept
+ {
+ m_size = _source.size();
+
+ if(_source.size() <= S)
+ {
+ std::copy_n(_source.begin(), _source.size(), m_array.begin());
+ }
+ else
+ {
+ if(!m_vector)
+ m_vector = new std::vector<T>(_source);
+
+ std::swap(*m_vector, _source);
+ }
+ return *this;
+ }
+
+ size_t size() const
+ {
+ return m_size;
+ }
+
+ bool empty() const
+ {
+ return m_size == 0;
+ }
+
+ void clear()
+ {
+ delete m_vector;
+ m_vector = nullptr;
+ m_size = 0;
+ }
+
+ bool isDynamic() const
+ {
+ return m_vector != nullptr;
+ }
+
+ void push_back(const T& _value)
+ {
+ makeDynamicIfNeeded();
+
+ if(m_vector)
+ m_vector->push_back(_value);
+ else
+ m_array[m_size] = _value;
+ ++m_size;
+ }
+
+ void push_back(const T* _data, size_t _size)
+ {
+ if(!_size)
+ return;
+
+ makeDynamicIfNeeded(_size);
+
+ if(m_vector)
+ m_vector->insert(m_vector->end(), _data, _data + _size);
+ else
+ std::copy_n(_data, _size, &m_array[m_size]);
+ m_size += _size;
+ }
+
+ void push_back(const std::vector<T>& _data)
+ {
+ if(_data.empty())
+ return;
+ push_back(&_data[0], _data.size());
+ }
+
+ template<typename T2, size_t S2> void push_back(const HybridVector<T2,S2>& _source)
+ {
+ push_back(_source.begin(), _source.size());
+ }
+
+ T* begin()
+ {
+ if(m_vector)
+ return &m_vector->front();
+ return &m_array[0];
+ }
+
+ T* end()
+ {
+ return begin() + size();
+ }
+
+ void pop_back()
+ {
+ if(empty())
+ return;
+
+ if(m_vector)
+ m_vector->pop_back();
+ --m_size;
+ }
+
+ const T& front() const
+ {
+ if(m_vector)
+ return m_vector->front();
+ return m_array.front();
+ }
+
+ const T& back() const
+ {
+ if(m_vector)
+ return m_vector->back();
+ return m_array.back();
+ }
+
+ void swap(HybridVector& _source) noexcept
+ {
+ if(!m_vector || !_source.m_vector)
+ std::swap(m_array, _source.m_array);
+
+ std::swap(m_size, _source.m_size);
+ std::swap(m_vector, _source.m_vector);
+ }
+
+ private:
+ bool makeDynamicIfNeeded(const size_t _elementCountToAdd = 1)
+ {
+ if(size() + _elementCountToAdd <= S)
+ return false;
+
+ makeDynamic();
+ return true;
+ }
+
+ void makeDynamic()
+ {
+ if(!m_vector)
+ m_vector = new std::vector<T>();
+ m_vector->reserve(m_size);
+ std::copy_n(m_array.begin(), m_size, m_vector->begin());
+ }
+
+ std::array<T, S> m_array;
+ size_t m_size = 0;
+ std::vector<T>* m_vector = nullptr;
+ };
+}
diff --git a/source/synthLib/midiToSysex.cpp b/source/synthLib/midiToSysex.cpp
@@ -4,6 +4,8 @@
#include "dsp56kEmu/logging.h"
+#include "os.h"
+
namespace synthLib
{
#ifdef _MSC_VER
@@ -160,6 +162,22 @@ namespace synthLib
}
}
+ bool MidiToSysex::extractSysexFromFile(std::vector<std::vector<uint8_t>>& _messages, const std::string& _filename)
+ {
+ std::vector<uint8_t> data;
+
+ if(!synthLib::readFile(data, _filename))
+ return false;
+
+ return extractSysexFromData(_messages, data);
+ }
+
+ bool MidiToSysex::extractSysexFromData(std::vector<std::vector<uint8_t>>& _messages, const std::vector<uint8_t>& _data)
+ {
+ splitMultipleSysex(_messages, _data);
+ return !_messages.empty();
+ }
+
bool MidiToSysex::checkChunk(FILE* hFile, const char* _pCompareChunk)
{
char readChunk[4];
diff --git a/source/synthLib/midiToSysex.h b/source/synthLib/midiToSysex.h
@@ -11,6 +11,8 @@ namespace synthLib
public:
static bool readFile(std::vector<uint8_t>& _sysexMessages, const char* _filename);
static void splitMultipleSysex(std::vector<std::vector<uint8_t>>& _dst, const std::vector<uint8_t>& _src);
+ static bool extractSysexFromFile(std::vector<std::vector<uint8_t>>& _messages, const std::string& _filename);
+ static bool extractSysexFromData(std::vector<std::vector<uint8_t>>& _messages, const std::vector<uint8_t>& _data);
private:
static bool checkChunk(FILE* hFile, const char* _pCompareChunk);
static uint32_t getChunkLength(FILE* hFile);
diff --git a/source/synthLib/os.cpp b/source/synthLib/os.cpp
@@ -198,6 +198,14 @@ namespace synthLib
std::string findFile(const std::string& _rootPath, const std::string& _extension, const size_t _minSize, const size_t _maxSize)
{
std::vector<std::string> files;
+ if(!findFiles(files, _rootPath, _extension, _minSize, _maxSize))
+ return {};
+ return files.front();
+ }
+
+ bool findFiles(std::vector<std::string>& _files, const std::string& _rootPath, const std::string& _extension, const size_t _minSize, const size_t _maxSize)
+ {
+ std::vector<std::string> files;
getDirectoryEntries(files, _rootPath);
@@ -208,8 +216,8 @@ namespace synthLib
if (!_minSize && !_maxSize)
{
- LOG("Found ROM at path " << file);
- return file;
+ _files.push_back(file);
+ continue;
}
FILE *hFile = fopen(file.c_str(), "rb");
@@ -225,11 +233,9 @@ namespace synthLib
if (_maxSize && size > _maxSize)
continue;
- LOG("Found ROM at path " << file);
-
- return file;
+ _files.push_back(file);
}
- return {};
+ return !_files.empty();
}
std::string findROM(const size_t _minSize, const size_t _maxSize)
diff --git a/source/synthLib/os.h b/source/synthLib/os.h
@@ -15,6 +15,7 @@ namespace synthLib
bool getDirectoryEntries(std::vector<std::string>& _files, const std::string& _folder);
std::string findFile(const std::string& _extension, size_t _minSize, size_t _maxSize);
+ bool findFiles(std::vector<std::string>& _files, const std::string& _rootPath, const std::string& _extension, size_t _minSize, size_t _maxSize);
std::string findFile(const std::string& _rootPath, const std::string& _extension, size_t _minSize, size_t _maxSize);
std::string findROM(size_t _minSize, size_t _maxSize);
std::string findROM(size_t _expectedSize = 524288);
diff --git a/source/synthLib/plugin.cpp b/source/synthLib/plugin.cpp
@@ -101,13 +101,16 @@ namespace synthLib
if(!m_device)
return false;
- if(_state.size() < 2)
+ if(_state.empty())
return false;
+ if(_state.size() < 2)
+ return m_device->setStateFromUnknownCustomData(_state);
+
const auto version = _state[0];
if(version != g_stateVersion)
- return false;
+ return m_device->setStateFromUnknownCustomData(_state);
const auto stateType = static_cast<StateType>(_state[1]);
diff --git a/source/synthLib/wavWriter.cpp b/source/synthLib/wavWriter.cpp
@@ -8,6 +8,7 @@
#include <mutex>
#include <thread>
+#include "dsp56kEmu/threadtools.h"
#include "dsp56kEmu/types.h"
namespace synthLib
@@ -140,6 +141,8 @@ namespace synthLib
void AsyncWriter::threadWriteFunc()
{
+ dsp56k::ThreadTools::setCurrentThreadName("AsyncWavWriter");
+
synthLib::WavWriter writer;
std::vector<dsp56k::TWord> m_wordBuffer;
diff --git a/source/virusIntegrationTest/integrationTest.cpp b/source/virusIntegrationTest/integrationTest.cpp
@@ -244,12 +244,13 @@ bool IntegrationTest::loadAudioFile(File& _dst, const std::string& _filename) co
int IntegrationTest::runCompare()
{
- const auto sampleCount = m_referenceFile.data.dataByteSize * 8 / m_referenceFile.data.bitsPerSample / 2;
+ const auto sampleCount = m_referenceFile.data.dataByteSize * 8 / m_referenceFile.data.bitsPerSample;
+ const auto frameCount = sampleCount >> 1;
std::vector<uint8_t> temp;
File compareFile;
- const auto res = createAudioFile(compareFile, "compare_", static_cast<uint32_t>(sampleCount));
+ const auto res = createAudioFile(compareFile, "compare_", static_cast<uint32_t>(frameCount));
if(res)
return res;
@@ -258,12 +259,12 @@ int IntegrationTest::runCompare()
for(uint32_t i=0; i<sampleCount; ++i)
{
- const uint32_t a = (static_cast<uint32_t>(ptrA[0]) << 24) || (static_cast<uint32_t>(ptrA[1]) << 16) || ptrA[2];
- const uint32_t b = (static_cast<uint32_t>(ptrB[0]) << 24) || (static_cast<uint32_t>(ptrB[1]) << 16) || ptrB[2];
+ const uint32_t a = (static_cast<uint32_t>(ptrA[0]) << 24) | (static_cast<uint32_t>(ptrA[1]) << 16) | ptrA[2];
+ const uint32_t b = (static_cast<uint32_t>(ptrB[0]) << 24) | (static_cast<uint32_t>(ptrB[1]) << 16) | ptrB[2];
if(b != a)
{
- std::cout << "Test failed, audio output is not identical to reference file, difference starting at frame " << (i>>1) << std::endl;
+ std::cout << "Test failed, audio output is not identical to reference file, difference starting at frame " << (i>>1) << ", ROM " << m_romFile << ", preset " << m_presetName << std::endl;
return -2;
}
@@ -271,7 +272,7 @@ int IntegrationTest::runCompare()
ptrB += 3;
}
- std::cout << "Test succeeded, compared " << sampleCount << " samples" << std::endl;
+ std::cout << "Test succeeded, compared " << sampleCount << " samples, ROM " << m_romFile << ", preset " << m_presetName << std::endl;
return 0;
}
diff --git a/source/virusLib/CMakeLists.txt b/source/virusLib/CMakeLists.txt
@@ -4,7 +4,6 @@ project(virusLib)
add_library(virusLib STATIC)
set(SOURCES
- demopacketvalidator.cpp demopacketvalidator.h
demoplayback.cpp demoplayback.h
device.cpp device.h
dspSingle.cpp dspSingle.h
@@ -15,6 +14,7 @@ set(SOURCES
romfile.cpp romfile.h
microcontroller.cpp microcontroller.h
microcontrollerTypes.cpp microcontrollerTypes.h
+ midiFileToRomData.cpp midiFileToRomData.h
utils.h
)
diff --git a/source/virusLib/demopacketvalidator.cpp b/source/virusLib/demopacketvalidator.cpp
@@ -1,134 +0,0 @@
-#include "demopacketvalidator.h"
-
-#include <cassert>
-
-#include "dsp56kEmu/logging.h"
-
-namespace virusLib
-{
- bool DemoPacketValidator::add(const Packet& _packet)
- {
- if(isComplete())
- return isValid();
-
- if(_packet.size() < 11)
- return isValid();
-
- const auto cmd = _packet[5];
-
- switch (cmd)
- {
- case 0x50: // Virus A second
- case 0x55: // Virus B second
- case 0x57: // Virus C second
- {
- const auto msb = _packet[6]; // packet number MSB
- const auto lsb = _packet[7]; // packet number LSB
-
- uint8_t checksum = 0;
- for(size_t i=5; i<_packet.size()-2; ++i)
- checksum += _packet[i];
- checksum &= 0x7f;
-
- if(checksum != _packet[_packet.size()-2])
- {
- LOG("Packet MSB " << static_cast<int>(msb) << " LSB " << static_cast<int>(lsb) << " is invalid, wrong checksum");
- m_valid = false;
- return false;
- }
-
- auto packetInvalid = [&]()
- {
- LOG("Packet invalid, expected packet index " << static_cast<int>(m_expectedMSB) << " " << static_cast<int>(m_expectedLSB) << " but got " << static_cast<int>(msb) << " " << static_cast<int>(lsb));
- m_valid = false;
- return false;
- };
-
- auto matchExpected = [&]()
- {
- return msb == m_expectedMSB && lsb == m_expectedLSB;
- };
-
- if(msb == 127 && lsb >= 3)
- {
- if(lsb == 3)
- {
- if(!matchExpected())
- return packetInvalid();
-
- m_packets.push_back(_packet);
-
- m_expectedLSB = ++m_offset;
- }
- else if(lsb < 126)
- {
- if(!matchExpected())
- return packetInvalid();
- m_expectedLSB = 0;
- m_expectedMSB = 0;
- }
- else if(lsb == 127)
- {
- if(m_expectedMSB != msb)
- {
- LOG("Terminating too soon, missing packets");
- m_valid = false;
- return false;
- }
-
- m_complete = true;
- if(!toBinary(m_data))
- m_valid = false;
- return isComplete();
- }
- }
- else
- {
- if(!matchExpected())
- return packetInvalid();
-
- m_packets.push_back(_packet);
-
- if(lsb == 3)
- {
- ++m_expectedMSB;
- m_expectedLSB = 0;
- }
- else
- ++m_expectedLSB;
- }
- }
- break;
- default:
- // skip unknown packets
- return true;
- }
-
- return false;
- }
-
- bool DemoPacketValidator::toBinary(std::vector<uint8_t>& _binary) const
- {
- if(!isComplete())
- return false;
-
- for (const auto& p : m_packets)
- {
- // midi bytes in a sysex frame can only carry 7 bit, not 8. They've chosen the easy way that costs more storage
- // They transfer only one nibble of a ROM byte in one midi byte to ensure that the most significant nibble is
- // always zero. By concating two nibbles together we get one ROM byte
- for(size_t s=8; s<p.size()-2; s += 2)
- {
- const uint8_t a = p[s];
- const uint8_t b = p[s+1];
- if(a > 0xf || b > 0xf)
- {
- LOG("Invalid data, high nibble must be 0");
- return false;
- }
- _binary.push_back(static_cast<uint8_t>(b << 4) | a);
- }
- }
- return true;
- }
-}
diff --git a/source/virusLib/demopacketvalidator.h b/source/virusLib/demopacketvalidator.h
@@ -1,33 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <vector>
-
-namespace virusLib
-{
- class DemoPacketValidator
- {
- public:
- using Packet = std::vector<uint8_t>;
-
- bool add(const Packet& _packet);
-
- bool isValid() const { return m_valid; }
- bool isComplete() const { return isValid() && m_complete; }
-
- const std::vector<uint8_t>& getData() const { return m_data; }
-
- private:
- bool toBinary(std::vector<uint8_t>& _binary) const;
-
- std::vector<Packet> m_packets;
- std::vector<uint8_t> m_data;
-
- uint8_t m_expectedMSB = 127;
- uint8_t m_expectedLSB = 8;
- uint8_t m_offset = 8;
-
- bool m_valid = true;
- bool m_complete = false;
- };
-}
diff --git a/source/virusLib/demoplayback.cpp b/source/virusLib/demoplayback.cpp
@@ -13,7 +13,7 @@
#include <cstring> // memcpy
-#include "demopacketvalidator.h"
+#include "midiFileToRomData.h"
namespace virusLib
{
@@ -25,7 +25,7 @@ namespace virusLib
if(synthLib::hasExtension(_filename, ".bin"))
{
std::vector<uint8_t> data;
- auto hFile = fopen(_filename.c_str(), "rb");
+ auto* hFile = fopen(_filename.c_str(), "rb");
if(!hFile)
{
LOG("Failed to open demo file " << _filename);
@@ -39,33 +39,14 @@ namespace virusLib
return loadBinData(data);
}
- std::vector<uint8_t> sysex;
-
- synthLib::MidiToSysex::readFile(sysex, _filename.c_str());
-
- if(sysex.empty())
- {
- LOG("Failed to load demo midi file " << _filename << ", no sysex data found in file");
- return false;
- }
-
- std::vector<std::vector<uint8_t>> packets;
- synthLib::MidiToSysex::splitMultipleSysex(packets, sysex);
-
- DemoPacketValidator validator;
-
- for (const auto& packet : packets)
- validator.add(packet);
-
- if(!validator.isValid())
+ MidiFileToRomData romReader;
+ if(!romReader.load(_filename) || romReader.getData().empty())
{
- LOG("Packet validation failed, packets missing or invalid");
+ LOG("Failed to load demo midi file " << _filename << ", no valid data found in file");
return false;
}
- const auto& data = validator.getData();
-
- return loadBinData(data);
+ return loadBinData(romReader.getData());
}
bool DemoPlayback::loadBinData(const std::vector<uint8_t>& _data)
diff --git a/source/virusLib/device.cpp b/source/virusLib/device.cpp
@@ -3,6 +3,8 @@
#include "dspSingle.h"
#include "romfile.h"
+#include "dsp56kEmu/jit.h"
+
namespace virusLib
{
Device::Device(const ROMFile& _rom, const bool _createDebugger/* = false*/)
@@ -36,6 +38,8 @@ namespace virusLib
loader.join();
+// m_dsp->getMemory().saveAssembly("P.asm", 0, m_dsp->getMemory().sizeP(), true, false, m_dsp->getDSP().getPeriph(0), m_dsp->getDSP().getPeriph(1));
+
while(!m_mc->dspHasBooted())
dummyProcess(8);
@@ -80,6 +84,162 @@ namespace virusLib
return m_mc->setState(_state, _type);
}
+ bool Device::setStateFromUnknownCustomData(const std::vector<uint8_t>& _state)
+ {
+ std::vector<synthLib::SMidiEvent> messages;
+ if(!parseTIcontrolPreset(messages, _state))
+ return false;
+ return m_mc->setState(messages);
+ }
+
+ bool Device::find4CC(uint32_t& _offset, const std::vector<uint8_t>& _data, const std::string& _4cc)
+ {
+ for(uint32_t i=0; i<_data.size() - _4cc.size(); ++i)
+ {
+ bool valid = true;
+ for(size_t j=0; j<4; ++j)
+ {
+ if(static_cast<char>(_data[i + j]) == _4cc[j])
+ continue;
+ valid = false;
+ break;
+ }
+ if(valid)
+ {
+ _offset = i;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool Device::parseTIcontrolPreset(std::vector<synthLib::SMidiEvent>& _events, const std::vector<uint8_t>& _state)
+ {
+ if(_state.size() < 8)
+ return false;
+
+ uint32_t readPos = 0;
+
+ if(!find4CC(readPos, _state, "MIDI"))
+ return false;
+
+ if(readPos >= _state.size())
+ return false;
+
+ auto readLen = [&_state](const size_t _offset) -> uint32_t
+ {
+ if(_offset + 4 > _state.size())
+ return 0;
+ const uint32_t o =
+ (static_cast<uint32_t>(_state[_offset+0]) << 24) |
+ (static_cast<uint32_t>(_state[_offset+1]) << 16) |
+ (static_cast<uint32_t>(_state[_offset+2]) << 8) |
+ (static_cast<uint32_t>(_state[_offset+3]));
+ return o;
+ };
+
+ auto nextLen = [&readPos, &readLen]() -> uint32_t
+ {
+ const auto len = readLen(readPos);
+ readPos += 4;
+ return len;
+ };
+
+ const auto dataLen = nextLen();
+
+ if(dataLen + readPos > _state.size())
+ return false;
+
+ const auto controllerAssignmentsLen = nextLen();
+
+ readPos += controllerAssignmentsLen;
+
+ while(readPos < _state.size())
+ {
+ const auto midiDataLen = nextLen();
+
+ if(!midiDataLen)
+ break;
+
+ if((readPos + midiDataLen) > _state.size())
+ return false;
+
+ synthLib::SMidiEvent& e = _events.emplace_back();
+
+ e.sysex.assign(_state.begin() + readPos, _state.begin() + readPos + midiDataLen);
+
+ if(e.sysex.front() != 0xf0)
+ {
+ assert(e.sysex.size() <= 3);
+ e.a = e.sysex[0];
+ if(e.sysex.size() > 1)
+ e.b = e.sysex[1];
+ if(e.sysex.size() > 2)
+ e.c = e.sysex[2];
+
+ e.sysex.clear();
+ }
+
+ readPos += midiDataLen;
+ }
+
+ return true;
+ }
+
+ bool Device::parsePowercorePreset(std::vector<std::vector<uint8_t>>& _sysexPresets, const std::vector<uint8_t>& _data)
+ {
+ uint32_t off = 0;
+
+ // VST2 fxp/fxb chunk must exist
+ if(!find4CC(off, _data, "CcnK"))
+ return false;
+
+ uint32_t pos = 0;
+
+ // fxp or fxb?
+ if(find4CC(off, _data, "FPCh"))
+ pos = off + 0x34; // fxp
+ else if(find4CC(off, _data, "FBCh"))
+ pos = off + 0x98; // fxb
+ else
+ return false;
+
+ if(pos >= _data.size())
+ return false;
+
+ ++pos; // skip first byte, version?
+
+ constexpr uint32_t presetSize = 256; // presets seem to be stored without sysex packaging
+ constexpr uint32_t padding = 5; // five unknown bytes betweeen two presets
+
+ uint8_t programIndex = 0;
+
+ while((pos + presetSize) <= static_cast<uint32_t>(_data.size()))
+ {
+ Microcontroller::TPreset p;
+ memcpy(&p.front(), &_data[pos], presetSize);
+
+ const auto version = Microcontroller::getPresetVersion(p);
+ if(version != C)
+ break;
+ const auto name = ROMFile::getSingleName(p);
+ if(name.size() != 10)
+ break;
+
+ // pack into sysex
+ std::vector<uint8_t>& sysex = _sysexPresets.emplace_back(std::vector<uint8_t>{0xf0, 0x00, 0x20, 0x33, 0x01, OMNI_DEVICE_ID, 0x10, 0x01, programIndex});
+ sysex.insert(sysex.end(), _data.begin() + pos, _data.begin() + pos + presetSize);
+ sysex.push_back(Microcontroller::calcChecksum(sysex, 5));
+ sysex.push_back(0xf7);
+
+ ++programIndex;
+ pos += presetSize;
+ pos += padding;
+ }
+
+ return !_sysexPresets.empty();
+ }
+
uint32_t Device::getInternalLatencyMidiToOutput() const
{
// Note that this is an average value, midi latency drifts in a range of roughly +/- 61 samples
diff --git a/source/virusLib/device.h b/source/virusLib/device.h
@@ -22,6 +22,11 @@ namespace virusLib
bool getState(std::vector<uint8_t>& _state, synthLib::StateType _type) override;
bool setState(const std::vector<uint8_t>& _state, synthLib::StateType _type) override;
+ bool setStateFromUnknownCustomData(const std::vector<uint8_t>& _state) override;
+
+ static bool find4CC(uint32_t& _offset, const std::vector<uint8_t>& _data, const std::string& _4cc);
+ static bool parseTIcontrolPreset(std::vector<synthLib::SMidiEvent>& _events, const std::vector<uint8_t>& _state);
+ static bool parsePowercorePreset(std::vector<std::vector<uint8_t>>& _sysexPresets, const std::vector<uint8_t>& _data);
uint32_t getInternalLatencyMidiToOutput() const override;
uint32_t getInternalLatencyInputToOutput() const override;
diff --git a/source/virusLib/dspSingle.cpp b/source/virusLib/dspSingle.cpp
@@ -1,5 +1,7 @@
#include "dspSingle.h"
+#include "dsp56kEmu/dsp.h"
+
#if DSP56300_DEBUGGER
#include "dsp56kDebugger/debugger.h"
#endif
@@ -62,8 +64,8 @@ namespace virusLib
template<typename T> void processAudio(DspSingle& _dsp, const synthLib::TAudioInputsT<T>& _inputs, const synthLib::TAudioOutputsT<T>& _outputs, const size_t _samples, uint32_t _latency)
{
- const T* inputs[] = {_inputs[1], _inputs[0], nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
- T* outputs[] = {_outputs[1], _outputs[0], _outputs[3], _outputs[2], _outputs[5], _outputs[4], nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+ const T* inputs[] = {_inputs[0], _inputs[1], nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+ T* outputs[] = {_outputs[0], _outputs[1], _outputs[2], _outputs[3], _outputs[4], _outputs[5], nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
_dsp.getPeriphX().getEsai().processAudioInterleaved(inputs, outputs, static_cast<uint32_t>(_samples), _latency);
}
diff --git a/source/virusLib/dspSingle.h b/source/virusLib/dspSingle.h
@@ -1,9 +1,15 @@
#pragma once
#include "dsp56kEmu/dspthread.h"
+#include "dsp56kEmu/memory.h"
#include "../synthLib/audioTypes.h"
+namespace dsp56k
+{
+ class Jit;
+}
+
namespace virusLib
{
class DspSingle
diff --git a/source/virusLib/microcontroller.cpp b/source/virusLib/microcontroller.cpp
@@ -131,8 +131,11 @@ void Microcontroller::writeHostBitsWithWait(const uint8_t flag0, const uint8_t f
m_hdi08.writeHostFlags(flag0, flag1);
}
-bool Microcontroller::sendPreset(const uint8_t program, const std::vector<TWord>& preset, const bool isMulti)
+bool Microcontroller::sendPreset(const uint8_t program, const TPreset& preset, const bool isMulti)
{
+ if(!isValid(preset))
+ return false;
+
std::lock_guard lock(m_mutex);
if(m_loadingState || waitingForPresetReceiveConfirmation())
@@ -169,13 +172,32 @@ bool Microcontroller::sendPreset(const uint8_t program, const std::vector<TWord>
receiveUpgradedPreset();
+ if(isMulti)
+ {
+ m_multiEditBuffer = preset;
+
+ m_globalSettings[PLAY_MODE] = PlayModeMulti;
+ }
+ else
+ {
+ if(program == SINGLE)
+ {
+ m_globalSettings[PLAY_MODE] = PlayModeSingle;
+ m_singleEditBuffer = preset;
+ }
+ else if(program < m_singleEditBuffers.size())
+ {
+ m_singleEditBuffers[program] = preset;
+ }
+ }
+
writeHostBitsWithWait(0,1);
// Send header
TWord buf[] = {0xf47555, static_cast<TWord>(isMulti ? 0x110000 : 0x100000)};
buf[1] = buf[1] | (program << 8);
m_hdi08.writeRX(buf, 2);
- m_hdi08.writeRX(preset);
+ m_hdi08.writeRX(presetToDSPWords(preset, isMulti));
LOG("Send to DSP: " << (isMulti ? "Multi" : "Single") << " to program " << static_cast<int>(program));
@@ -292,6 +314,9 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, std::vector<S
auto buildPresetResponse = [&](const uint8_t _type, const BankNumber _bank, const uint8_t _program, const TPreset& _dump)
{
+ if(!isValid(_dump))
+ return;
+
SMidiEvent ev;
ev.source = _source;
@@ -525,6 +550,8 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, std::vector<S
buildGlobalResponses();
break;
case REQUEST_TOTAL:
+ if(!m_pendingPresetWrites.empty() || waitingForPresetReceiveConfirmation())
+ return enqueue();
buildTotalResponse();
break;
case REQUEST_ARRANGEMENT:
@@ -661,6 +688,8 @@ std::vector<TWord> Microcontroller::presetToDSPWords(const TPreset& _preset, con
bool Microcontroller::getSingle(BankNumber _bank, uint32_t _preset, TPreset& _result) const
{
+ _result[0] = 0;
+
if (_bank == BankNumber::EditBuffer)
return false;
@@ -680,6 +709,8 @@ bool Microcontroller::getSingle(BankNumber _bank, uint32_t _preset, TPreset& _re
bool Microcontroller::requestMulti(BankNumber _bank, uint8_t _program, TPreset& _data)
{
+ _data[0] = 0;
+
if (_bank == BankNumber::EditBuffer)
{
receiveUpgradedPreset();
@@ -733,25 +764,13 @@ bool Microcontroller::writeSingle(BankNumber _bank, uint8_t _program, const TPre
return true;
}
- if(_program == SINGLE)
- {
- m_globalSettings[PLAY_MODE] = PlayModeSingle;
-
- m_singleEditBuffer = _data;
- }
- else if(_program >= m_singleEditBuffers.size())
- {
+ if(_program >= m_singleEditBuffers.size() && _program != SINGLE)
return false;
- }
- else
- {
- m_singleEditBuffers[_program] = _data;
- }
LOG("Loading Single " << ROMFile::getSingleName(_data) << " to part " << static_cast<int>(_program));
// Send to DSP
- return sendPreset(_program, presetToDSPWords(_data, false), false);
+ return sendPreset(_program, _data, false);
}
bool Microcontroller::writeMulti(BankNumber _bank, uint8_t _program, const TPreset& _data)
@@ -768,14 +787,10 @@ bool Microcontroller::writeMulti(BankNumber _bank, uint8_t _program, const TPres
return true;
}
- m_multiEditBuffer = _data;
-
LOG("Loading Multi " << ROMFile::getMultiName(_data));
- m_globalSettings[PLAY_MODE] = PlayModeMulti;
-
// Convert array of uint8_t to vector of 24bit TWord
- return sendPreset(_program, presetToDSPWords(_data, true), true);
+ return sendPreset(_program, _data, true);
}
bool Microcontroller::partBankSelect(const uint8_t _part, const uint8_t _value, const bool _immediatelySelectSingle)
@@ -859,16 +874,13 @@ void Microcontroller::process(size_t _size)
std::lock_guard lock(m_mutex);
- if(m_pendingPresetWrites.empty() || !m_hdi08.rxEmpty() || waitingForPresetReceiveConfirmation())
+ if(m_loadingState || m_pendingPresetWrites.empty() || !m_hdi08.rxEmpty() || waitingForPresetReceiveConfirmation())
return;
- if(!m_pendingPresetWrites.empty())
- {
- const auto preset = m_pendingPresetWrites.front();
- m_pendingPresetWrites.pop_front();
+ const auto preset = m_pendingPresetWrites.front();
+ m_pendingPresetWrites.pop_front();
- sendPreset(preset.program, preset.data, preset.isMulti);
- }
+ sendPreset(preset.program, preset.data, preset.isMulti);
}
bool Microcontroller::getState(std::vector<unsigned char>& _state, const StateType _type)
@@ -918,7 +930,12 @@ bool Microcontroller::setState(const std::vector<unsigned char>& _state, const S
}
}
- if(events.empty())
+ return setState(events);
+}
+
+bool Microcontroller::setState(const std::vector<synthLib::SMidiEvent>& _events)
+{
+ if(_events.empty())
return false;
// delay all preset loads until everything is loaded
@@ -926,10 +943,17 @@ bool Microcontroller::setState(const std::vector<unsigned char>& _state, const S
std::vector<SMidiEvent> unusedResponses;
- for (const auto& event : events)
+ for (const auto& event : _events)
{
- sendSysex(event.sysex, unusedResponses, MidiEventSourcePlugin);
- unusedResponses.clear();
+ if(!event.sysex.empty())
+ {
+ sendSysex(event.sysex, unusedResponses, MidiEventSourcePlugin);
+ unusedResponses.clear();
+ }
+ else
+ {
+ sendMIDI(event);
+ }
}
m_loadingState = false;
@@ -969,13 +993,24 @@ void Microcontroller::readMidiOut(std::vector<synthLib::SMidiEvent>& _midiOut)
std::lock_guard lock(m_mutex);
processHdi08Tx(_midiOut);
- if (m_pendingSysexInput.empty() || !m_pendingPresetWrites.empty() || waitingForPresetReceiveConfirmation())
+ if (m_pendingSysexInput.empty())
return;
+ uint32_t eraseCount = 0;
+
for (const auto& input : m_pendingSysexInput)
+ {
+ if(!m_pendingPresetWrites.empty() || waitingForPresetReceiveConfirmation())
+ break;
+
sendSysex(input.second, _midiOut, input.first);
+ ++eraseCount;
+ }
- m_pendingSysexInput.clear();
+ if(eraseCount == m_pendingSysexInput.size())
+ m_pendingSysexInput.clear();
+ else if(eraseCount > 0)
+ m_pendingSysexInput.erase(m_pendingSysexInput.begin(), m_pendingSysexInput.begin() + eraseCount);
}
void Microcontroller::sendPendingMidiEvents(const uint32_t _maxOffset)
@@ -1026,7 +1061,7 @@ void Microcontroller::applyToSingleEditBuffer(const Page _page, const uint8_t _p
applyToSingleEditBuffer(m_singleEditBuffers[_part], _page, _param, _value);
}
-void Microcontroller::applyToSingleEditBuffer(TPreset& _single, const Page _page, const uint8_t _param, const uint8_t _value) const
+void Microcontroller::applyToSingleEditBuffer(TPreset& _single, const Page _page, const uint8_t _param, const uint8_t _value)
{
constexpr uint32_t paramsPerPage = 128;
@@ -1121,4 +1156,8 @@ void Microcontroller::receiveUpgradedPreset()
}
}
-}
-\ No newline at end of file
+bool Microcontroller::isValid(const TPreset& _preset)
+{
+ return _preset.front() > 0;
+}
+}
diff --git a/source/virusLib/microcontroller.h b/source/virusLib/microcontroller.h
@@ -41,6 +41,7 @@ public:
bool getState(std::vector<unsigned char>& _state, synthLib::StateType _type);
bool setState(const std::vector<unsigned char>& _state, synthLib::StateType _type);
+ bool setState(const std::vector<synthLib::SMidiEvent>& _events);
void addDSP(DspSingle& _dsp, bool _useEsaiBasedMidiTiming);
@@ -60,7 +61,7 @@ public:
private:
bool send(Page page, uint8_t part, uint8_t param, uint8_t value);
void sendControlCommand(ControlCommand command, uint8_t value);
- bool sendPreset(uint8_t program, const std::vector<dsp56k::TWord>& preset, bool isMulti = false);
+ bool sendPreset(uint8_t program, const TPreset& _data, bool isMulti = false);
void writeHostBitsWithWait(uint8_t flag0, uint8_t flag1);
std::vector<dsp56k::TWord> presetToDSPWords(const TPreset& _preset, bool _isMulti) const;
bool getSingle(BankNumber _bank, uint32_t _preset, TPreset& _result) const;
@@ -73,7 +74,7 @@ private:
bool loadMultiSingle(uint8_t _part, const TPreset& _multi);
void applyToSingleEditBuffer(Page _page, uint8_t _part, uint8_t _param, uint8_t _value);
- void applyToSingleEditBuffer(TPreset& _single, Page _page, uint8_t _param, uint8_t _value) const;
+ static void applyToSingleEditBuffer(TPreset& _single, Page _page, uint8_t _param, uint8_t _value);
void applyToMultiEditBuffer(uint8_t _part, uint8_t _param, uint8_t _value);
Page globalSettingsPage() const;
bool isPageSupported(Page _page) const;
@@ -81,23 +82,25 @@ private:
bool waitingForPresetReceiveConfirmation() const;
void receiveUpgradedPreset();
+ static bool isValid(const TPreset& _preset);
+
Hdi08List m_hdi08;
std::vector<Hdi08TxParser> m_hdi08TxParsers;
std::vector<Hdi08MidiQueue> m_midiQueues;
const ROMFile& m_rom;
- std::array<TPreset,128> m_multis;
+ std::array<TPreset,128> m_multis{};
TPreset m_multiEditBuffer;
std::array<uint32_t, 256> m_globalSettings;
std::vector<std::vector<TPreset>> m_singles;
// Multi mode
- std::array<TPreset,16> m_singleEditBuffers;
+ std::array<TPreset,16> m_singleEditBuffers{};
// Single mode
- TPreset m_singleEditBuffer;
+ TPreset m_singleEditBuffer{};
uint8_t m_currentBank = 0;
uint8_t m_currentSingle = 0;
@@ -109,7 +112,7 @@ private:
{
uint8_t program = 0;
bool isMulti = false;
- std::vector<dsp56k::TWord> data;
+ TPreset data;
};
std::list<SPendingPresetWrite> m_pendingPresetWrites;
diff --git a/source/virusLib/midiFileToRomData.cpp b/source/virusLib/midiFileToRomData.cpp
@@ -0,0 +1,158 @@
+#include "midiFileToRomData.h"
+
+#include "dsp56kEmu/logging.h"
+
+#include "../synthLib/os.h"
+#include "../synthLib/midiToSysex.h"
+
+namespace virusLib
+{
+ bool MidiFileToRomData::load(const std::string& _filename)
+ {
+ std::vector<uint8_t> sysex;
+ std::vector<std::vector<uint8_t>> packets;
+
+ if(!synthLib::MidiToSysex::readFile(sysex, _filename.c_str()))
+ return false;
+
+ synthLib::MidiToSysex::splitMultipleSysex(packets, sysex);
+
+ return add(packets);
+ }
+
+ bool MidiFileToRomData::add(const std::vector<Packet>& _packets)
+ {
+ for (const auto& packet : _packets)
+ {
+ if(!add(packet))
+ return false;
+ if(isComplete())
+ return true;
+ }
+ return false;
+ }
+
+ bool MidiFileToRomData::add(const Packet& _packet)
+ {
+ if(isComplete())
+ return isValid();
+
+ if(_packet.size() < 11)
+ return isValid();
+
+ const auto cmd = _packet[5];
+
+ switch (cmd)
+ {
+ case 0x50: // Virus A
+ case 0x55: // Virus B
+ case 0x57: // Virus C
+ {
+ const auto msb = _packet[6]; // packet number MSB
+ const auto lsb = _packet[7]; // packet number LSB
+
+ uint8_t checksum = 0;
+ for(size_t i=5; i<_packet.size()-2; ++i)
+ checksum += _packet[i];
+ checksum &= 0x7f;
+
+ if(checksum != _packet[_packet.size()-2])
+ {
+ LOG("Packet MSB " << static_cast<int>(msb) << " LSB " << static_cast<int>(lsb) << " is invalid, wrong checksum");
+ m_valid = false;
+ return false;
+ }
+
+ if(!processPacket(_packet, msb, lsb))
+ {
+ m_valid = false;
+ return false;
+ }
+ return isValid();
+ }
+ default:
+ // skip unknown packets
+ return true;
+ }
+ }
+
+ bool MidiFileToRomData::setCompleted()
+ {
+ m_complete = true;
+ if(!toBinary(m_data))
+ m_valid = false;
+ return isComplete();
+ }
+
+ bool MidiFileToRomData::toBinary(std::vector<uint8_t>& _binary) const
+ {
+ if(!isComplete())
+ return false;
+
+ for (const auto& p : m_packets)
+ {
+ // midi bytes in a sysex frame can only carry 7 bit, not 8. They've chosen the easy way that costs more storage
+ // They transfer only one nibble of a ROM byte in one midi byte to ensure that the most significant nibble is
+ // always zero. By concating two nibbles together we get one ROM byte
+ for(size_t s=8; s<p.size()-2; s += 2)
+ {
+ const uint8_t a = p[s];
+ const uint8_t b = p[s+1];
+ if(a > 0xf || b > 0xf)
+ {
+ LOG("Invalid data, high nibble must be 0");
+ return false;
+ }
+ _binary.push_back(static_cast<uint8_t>(b << 4) | a);
+ }
+ }
+ return true;
+ }
+ bool MidiFileToRomData::processPacket(const Packet& _packet, uint8_t msb, uint8_t lsb)
+ {
+// LOG("Got Packet " << static_cast<int>(msb) << " " << static_cast<int>(lsb) << ", size " << _packet.size());
+
+ auto packetInvalid = [&]()
+ {
+ LOG("Packet invalid, expected packet index " << static_cast<int>(m_expectedMSB) << " " << static_cast<int>(m_expectedLSB) << " but got " << static_cast<int>(msb) << " " << static_cast<int>(lsb));
+ return false;
+ };
+
+ auto matchExpected = [&]()
+ {
+ return msb == m_expectedMSB && lsb == m_expectedLSB;
+ };
+
+ if(msb == 127 && m_expectedLSB > 3)
+ {
+ if(m_expectedSector == 0xff)
+ {
+ m_firstSector = m_expectedSector = lsb;
+ }
+
+ if(lsb != m_expectedSector)
+ {
+ if(lsb == 127)
+ return setCompleted();
+ return packetInvalid();
+ }
+ m_expectedLSB = m_expectedMSB = 0;
+ ++m_expectedSector;
+// addPacket(_packet);
+ return true;
+ }
+
+ if(!matchExpected())
+ return packetInvalid();
+
+ addPacket(_packet);
+ ++m_expectedLSB;
+
+ if(m_expectedLSB > 3 && msb != 127)
+ {
+ m_expectedLSB = 0;
+ ++m_expectedMSB;
+ }
+ return true;
+ }
+}
diff --git a/source/virusLib/midiFileToRomData.h b/source/virusLib/midiFileToRomData.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace virusLib
+{
+ class MidiFileToRomData
+ {
+ public:
+ using Packet = std::vector<uint8_t>;
+
+ MidiFileToRomData(const uint8_t _expectedFirstSector = 0xff) : m_expectedSector(_expectedFirstSector)
+ {
+ }
+
+ bool load(const std::string& _filename);
+
+ bool add(const std::vector<Packet>& _packets);
+ bool add(const Packet& _packet);
+
+ bool isValid() const { return m_valid; }
+ bool isComplete() const { return isValid() && m_complete; }
+
+ const std::vector<uint8_t>& getData() const { return m_data; }
+
+ size_t getPacketCount() const { return m_packets.size(); }
+
+ uint8_t getFirstSector() const { return m_firstSector; }
+
+ private:
+ void addPacket(const Packet& _packet)
+ {
+ m_packets.push_back(_packet);
+ }
+ bool setCompleted();
+
+ bool processPacket(const Packet& _packet, uint8_t _msb, uint8_t _lsb);
+
+ bool toBinary(std::vector<uint8_t>& _binary) const;
+
+ std::vector<Packet> m_packets;
+ std::vector<uint8_t> m_data;
+
+ bool m_valid = true;
+ bool m_complete = false;
+
+ uint8_t m_expectedMSB = 127;
+ uint8_t m_expectedLSB = 8;
+ uint8_t m_expectedSector;
+ uint8_t m_firstSector = 0xff;
+ };
+}
diff --git a/source/virusLib/romfile.cpp b/source/virusLib/romfile.cpp
@@ -10,6 +10,8 @@
#include "../synthLib/os.h"
+#include "midiFileToRomData.h"
+
#include <cstring> // memcpy
#ifdef _WIN32
@@ -19,46 +21,112 @@
namespace virusLib
{
-void ROMFile::dumpToBin(const std::vector<dsp56k::TWord>& _data, const std::string& _filename)
+ROMFile::ROMFile(const std::string& _path, const Model _model/* = Model::ABC*/)
{
- FILE* hFile = fopen(_filename.c_str(), "wb");
+ if(!_path.empty())
+ {
+ m_romFileName = _path;
+
+ // Open file
+ LOG("Loading ROM at " << _path);
- for(size_t i=0; i<_data.size(); ++i)
+ if(!synthLib::readFile(m_romFileData, _path))
+ {
+ LOG("Failed to load ROM at '" << _path << "'");
+ #ifdef _WIN32
+ const auto errorMessage = std::string("Failed to load ROM file. Make sure it is put next to the plugin and ends with .bin");
+ ::MessageBoxA(nullptr, errorMessage.c_str(), "ROM not found", MB_OK);
+ #endif
+ return;
+ }
+ }
+ else
{
- const auto d = _data[i];
- const auto hsb = (d >> 16) & 0xff;
- const auto msb = (d >> 8) & 0xff;
- const auto lsb = d & 0xff;
- fwrite(&hsb, 1, 1, hFile);
- fwrite(&msb, 1, 1, hFile);
- fwrite(&lsb, 1, 1, hFile);
+ const auto expectedSize = _model == Model::ABC ? getRomSizeModelABC() : 0;
+
+ if(!loadROMData(m_romFileName, m_romFileData, expectedSize, expectedSize))
+ return;
}
- fclose(hFile);
+
+ if(initialize())
+ printf("ROM File: %s\n", m_romFileName.c_str());
}
-ROMFile::ROMFile(const std::string& _path) : m_file(_path)
+ROMFile::ROMFile(std::vector<uint8_t> _data) : m_romFileData(std::move(_data))
{
- LOG("Init access virus");
+ initialize();
+}
- // Open file
- LOG("Loading ROM at " << m_file);
- std::ifstream file(this->m_file, std::ios::binary | std::ios::ate);
- if (!file.is_open()) {
- LOG("Failed to load ROM at '" << m_file << "'");
-#ifdef _WIN32
- const auto errorMessage = std::string("Failed to load ROM file. Make sure it is put next to the plugin and ends with .bin");
- ::MessageBoxA(nullptr, errorMessage.c_str(), "ROM not found", MB_OK);
-#endif
- return;
+std::string ROMFile::findROM()
+{
+ return synthLib::findROM(getRomSizeModelABC());
+}
+
+bool ROMFile::loadROMData(std::string& _loadedFile, std::vector<uint8_t>& _loadedData, const size_t _expectedSizeMin, const size_t _expectedSizeMax)
+{
+ // try binary roms first
+ const auto file = synthLib::findROM(_expectedSizeMin, _expectedSizeMax);
+
+ if(!file.empty())
+ {
+ if(synthLib::readFile(_loadedData, file) && !_loadedData.empty())
+ {
+ _loadedFile = file;
+ return true;
+ }
}
- std::istream *dsp = &file;
+ // if that didn't work, load an OS update as rom
+ auto loadMidiAsRom = [&](const std::string& _path)
+ {
+ _loadedFile.clear();
+ _loadedData.clear();
+
+ std::vector<std::string> files;
+
+ synthLib::findFiles(files, _path, ".mid", 512 * 1024, 600 * 1024);
+
+ bool gotSector0 = false;
+ bool gotSector8 = false;
+
+ for (const auto& f : files)
+ {
+ MidiFileToRomData loader;
+ if(!loader.load(f) || loader.getData().size() != getRomSizeModelABC() / 2)
+ continue;
+ if(loader.getFirstSector() == 0)
+ {
+ if(gotSector0)
+ continue;
+ gotSector0 = true;
+ _loadedFile = f;
+ _loadedData.insert(_loadedData.begin(), loader.getData().begin(), loader.getData().end());
+ }
+ else if(loader.getFirstSector() == 8)
+ {
+ if(gotSector8)
+ continue;
+ gotSector8 = true;
+ _loadedData.insert(_loadedData.end(), loader.getData().begin(), loader.getData().end());
+ }
+ }
+ return gotSector0 && _loadedData.size() >= getRomSizeModelABC() / 2;
+ };
+
+ if(loadMidiAsRom(synthLib::getModulePath()))
+ return true;
+
+ return loadMidiAsRom(synthLib::getModulePath(false));
+}
+
+bool ROMFile::initialize()
+{
+ std::istream *dsp = new imemstream(reinterpret_cast<std::vector<char>&>(m_romFileData));
const auto chunks = readChunks(*dsp);
- file.close();
if (chunks.empty())
- return;
+ return false;
bootRom.size = chunks[0].items[0];
bootRom.offset = chunks[0].items[1];
@@ -75,22 +143,15 @@ ROMFile::ROMFile(const std::string& _path) : m_file(_path)
for (size_t j = 0; j < chunks.size(); j++)
{
for (; i < chunks[j].items.size(); i++)
- commandStream.emplace_back(chunks[j].items[i]);
+ m_commandStream.emplace_back(chunks[j].items[i]);
i = 0;
}
-// dumpToBin(bootRom.data, _path + "_bootloader.bin");
-// dumpToBin(commandStream, _path + "_commandstream.bin");
-
- printf("ROM File: %s\n", _path.c_str());
printf("Program BootROM size = 0x%x\n", bootRom.size);
printf("Program BootROM offset = 0x%x\n", bootRom.offset);
- printf("Program CommandStream size = 0x%x\n", static_cast<uint32_t>(commandStream.size()));
-}
+ printf("Program CommandStream size = 0x%x\n", static_cast<uint32_t>(m_commandStream.size()));
-std::string ROMFile::findROM()
-{
- return synthLib::findROM(getRomSizeModelABC());
+ return true;
}
std::vector<ROMFile::Chunk> ROMFile::readChunks(std::istream& _file)
@@ -101,7 +162,7 @@ std::vector<ROMFile::Chunk> ROMFile::readChunks(std::istream& _file)
uint32_t offset = 0x18000;
int lastChunkId = 4;
- if (fileSize == 1024 * 512)
+ if (fileSize == getRomSizeModelABC() || fileSize == getRomSizeModelABC()/2) // the latter is a ROM without presets
{
// ABC
m_model = Model::ABC;
@@ -161,7 +222,7 @@ std::thread ROMFile::bootDSP(dsp56k::DSP& dsp, dsp56k::Peripherals56362& periph)
// Attach command stream
std::thread feedCommandStream([&]()
{
- periph.getHDI08().writeRX(commandStream);
+ periph.getHDI08().writeRX(m_commandStream);
});
// Initialize the DSP
@@ -183,18 +244,10 @@ bool ROMFile::getMulti(const int _presetNumber, TPreset& _out) const
bool ROMFile::getPreset(const uint32_t _offset, TPreset& _out) const
{
- // Open file
- std::ifstream file(this->m_file, std::ios::binary | std::ios::ate);
- if(!file.is_open())
- {
- LOG("Failed to open ROM file " << m_file)
+ if(_offset + getSinglePresetSize() > m_romFileData.size())
return false;
- }
- file.seekg(_offset);
- if(file.tellg() != _offset)
- return false;
- file.read(reinterpret_cast<char *>(_out.data()), getSinglePresetSize());
- file.close();
+
+ memcpy(_out.data(), &m_romFileData[_offset], getSinglePresetSize());
return true;
}
diff --git a/source/virusLib/romfile.h b/source/virusLib/romfile.h
@@ -42,9 +42,8 @@ public:
using TPreset = std::array<uint8_t, 512>;
- static void dumpToBin(const std::vector<dsp56k::TWord>& _data, const std::string& _filename);
-
- explicit ROMFile(const std::string& _path);
+ explicit ROMFile(const std::string& _path, Model _model = Model::ABC);
+ explicit ROMFile(std::vector<uint8_t> _data);
bool getMulti(int _presetNumber, TPreset& _out) const;
bool getSingle(int _bank, int _presetNumber, TPreset& _out) const;
@@ -92,20 +91,27 @@ public:
static std::string findROM();
+ static bool loadROMData(std::string& _loadedFile, std::vector<uint8_t>& _loadedData, size_t _expectedSizeMin, size_t _expectedSizeMax);
+
const std::vector<uint8_t>& getDemoData() const { return m_demoData; }
+ std::string getFilename() const { return isValid() ? m_romFileName : std::string(); }
+
private:
+ bool initialize();
std::vector<Chunk> readChunks(std::istream& _file);
BootRom bootRom;
- std::vector<uint32_t> commandStream;
+ std::vector<uint32_t> m_commandStream;
- const std::string m_file;
Model m_model = Model::Invalid;
std::vector<TPreset> m_singles;
std::vector<TPreset> m_multis;
std::vector<uint8_t> m_demoData;
+
+ std::string m_romFileName;
+ std::vector<uint8_t> m_romFileData;
};
}
diff --git a/source/virusTestConsole/CMakeLists.txt b/source/virusTestConsole/CMakeLists.txt
@@ -17,10 +17,10 @@ if(UNIX AND NOT APPLE)
target_link_libraries(virusTestConsole PUBLIC -static-libgcc -static-libstdc++)
endif()
-install(TARGETS virusTestConsole DESTINATION . COMPONENT testConsole)
+install(TARGETS virusTestConsole DESTINATION . COMPONENT OsirusTestConsole)
if(MSVC)
- install(DIRECTORY ${CMAKE_SOURCE_DIR}/deploy/win/ DESTINATION . COMPONENT testConsole)
+ install(DIRECTORY ${CMAKE_SOURCE_DIR}/deploy/win/ DESTINATION . COMPONENT OsirusTestConsole)
else()
- install(DIRECTORY ${CMAKE_SOURCE_DIR}/deploy/linux/ DESTINATION . COMPONENT testConsole)
+ install(DIRECTORY ${CMAKE_SOURCE_DIR}/deploy/linux/ DESTINATION . COMPONENT OsirusTestConsole)
endif()
diff --git a/source/virusTestConsole/virusTestConsole.cpp b/source/virusTestConsole/virusTestConsole.cpp
@@ -29,22 +29,13 @@ int main(int _argc, char* _argv[])
return -1;
}
}
-
- const auto romFile = findROM(0);
-
- if(romFile.empty())
- {
- std::cout << "Unable to find ROM. Place a ROM file with .bin extension next to this program." << std::endl;
- ConsoleApp::waitReturn();
- return -1;
- }
-
+
std::unique_ptr<ConsoleApp> app;
- app.reset(new ConsoleApp(romFile));
+ app.reset(new ConsoleApp({}));
if(!app->isValid())
{
- std::cout << "ROM file " << romFile << " couldn't be loaded. Make sure tha the ROM file is valid" << std::endl;
+ std::cout << "ROM file couldn't be loaded. Make sure that the ROM file is valid" << std::endl;
ConsoleApp::waitReturn();
return -1;
}
diff --git a/temp/cmake_win32/virusEmu.sln.DotSettings b/temp/cmake_win32/virusEmu.sln.DotSettings
@@ -0,0 +1,26 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:String x:Key="/Default/CodeStyle/CodeFormatting/CppFormatting/BREAK_TEMPLATE_DECLARATION/@EntryValue">ON_SINGLE_LINE</s:String>
+ <s:Boolean x:Key="/Default/CodeStyle/EditorConfig/EnableClangFormatSupport/@EntryValue">False</s:Boolean>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=AAR/@EntryIndexedValue">AAR</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=CCR/@EntryIndexedValue">CCR</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=DSP/@EntryIndexedValue">DSP</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=FX/@EntryIndexedValue">FX</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=PC/@EntryIndexedValue">PC</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=SP/@EntryIndexedValue">SP</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Classes_0020and_0020structs/@EntryIndexedValue"><NamingElement Priority="1"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="__interface" /><type Name="class" /><type Name="struct" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020fields/@EntryIndexedValue"><NamingElement Priority="11"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="True" Prefix="m_" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020methods/@EntryIndexedValue"><NamingElement Priority="10"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020public_0020fields/@EntryIndexedValue"><NamingElement Priority="12"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="PUBLIC"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Enums/@EntryIndexedValue"><NamingElement Priority="3"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="enum" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Global_0020constants/@EntryIndexedValue"><NamingElement Priority="16"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="True" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global variable" /></Descriptor><Policy Inspect="True" Prefix="g_" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Global_0020functions/@EntryIndexedValue"><NamingElement Priority="9"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Global_0020variables/@EntryIndexedValue"><NamingElement Priority="8"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global variable" /></Descriptor><Policy Inspect="True" Prefix="g_" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Local_0020variables/@EntryIndexedValue"><NamingElement Priority="7"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="local variable" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Namespaces/@EntryIndexedValue"><NamingElement Priority="17"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="namespace" /><type Name="namespace alias" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Parameters/@EntryIndexedValue"><NamingElement Priority="6"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="function parameter" /><type Name="lambda parameter" /></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Typedefs/@EntryIndexedValue"><NamingElement Priority="18"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="type alias" /><type Name="typedef" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FCLASS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMIXED_005FENUM/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FTYPE_005FALIAS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
+ <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECpp_002ECodeStyle_002ESettingsUpgrade_002EFunctionReturnStyleSettingsUpgrader/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
+\ No newline at end of file
diff --git a/temp/cmake_win64/virusEmu.sln.DotSettings b/temp/cmake_win64/virusEmu.sln.DotSettings
@@ -0,0 +1,29 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:String x:Key="/Default/CodeStyle/CodeFormatting/CppFormatting/BREAK_TEMPLATE_DECLARATION/@EntryValue">ON_SINGLE_LINE</s:String>
+ <s:Boolean x:Key="/Default/CodeStyle/EditorConfig/EnableClangFormatSupport/@EntryValue">False</s:Boolean>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=AAR/@EntryIndexedValue">AAR</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=AB/@EntryIndexedValue">AB</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=CCR/@EntryIndexedValue">CCR</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=DSP/@EntryIndexedValue">DSP</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=FX/@EntryIndexedValue">FX</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=LC/@EntryIndexedValue">LC</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=PC/@EntryIndexedValue">PC</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=SP/@EntryIndexedValue">SP</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Abbreviations/=SR/@EntryIndexedValue">SR</s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Classes_0020and_0020structs/@EntryIndexedValue"><NamingElement Priority="1"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="__interface" /><type Name="class" /><type Name="struct" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020fields/@EntryIndexedValue"><NamingElement Priority="11"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="True" Prefix="m_" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020methods/@EntryIndexedValue"><NamingElement Priority="10"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020public_0020fields/@EntryIndexedValue"><NamingElement Priority="12"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="PUBLIC"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Enums/@EntryIndexedValue"><NamingElement Priority="3"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="enum" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Global_0020constants/@EntryIndexedValue"><NamingElement Priority="16"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="True" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global variable" /></Descriptor><Policy Inspect="True" Prefix="g_" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Global_0020functions/@EntryIndexedValue"><NamingElement Priority="9"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Global_0020variables/@EntryIndexedValue"><NamingElement Priority="8"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global variable" /></Descriptor><Policy Inspect="True" Prefix="g_" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Local_0020variables/@EntryIndexedValue"><NamingElement Priority="7"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="local variable" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Namespaces/@EntryIndexedValue"><NamingElement Priority="17"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="namespace" /><type Name="namespace alias" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Parameters/@EntryIndexedValue"><NamingElement Priority="6"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="function parameter" /><type Name="lambda parameter" /></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Typedefs/@EntryIndexedValue"><NamingElement Priority="18"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="type alias" /><type Name="typedef" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FCLASS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMIXED_005FENUM/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
+ <s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FTYPE_005FALIAS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
+ <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECpp_002ECodeStyle_002ESettingsUpgrade_002EFunctionReturnStyleSettingsUpgrader/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
+\ No newline at end of file