commit 7a7d83dde7b3d25bd3f216beae920cbe11da8d88 Author: Nanako <469449812@qq.com> Date: Wed Nov 13 21:48:35 2024 +0800 init diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8b52a0c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.16) +project(bit_alchemy CXX C) + +include(cmake/retrieve_files.cmake) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_C_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) +if (MSVC) + add_compile_options(/std:c++17) + add_compile_options(/Zc:__cplusplus) +endif() + +find_package(JUCE REQUIRED) +find_package(xtensor REQUIRED) + +#find_path(CPP_PEGLIB_INCLUDE_DIRS ")peglib.h") + +set(src_files "") +retrieve_files(${CMAKE_CURRENT_SOURCE_DIR}/src src_files) +add_library(bit_alchemy STATIC ${src_files}) +target_link_libraries(bit_alchemy PUBLIC xtensor juce::juce_dsp juce::juce_audio_processors juce::juce_gui_basics) +target_include_directories(bit_alchemy PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) diff --git a/cmake/retrieve_files.cmake b/cmake/retrieve_files.cmake new file mode 100644 index 0000000..756d614 --- /dev/null +++ b/cmake/retrieve_files.cmake @@ -0,0 +1,61 @@ + +function(retrieve_files_custom path extension out_files) + message(STATUS "Retrieving files in ${path}") + set(EXTENSIONS "") + foreach(ext ${extension}) + list(APPEND EXTENSIONS "${path}/*.${ext}") + endforeach () + + # 递归查找文件夹下的 .h .hpp. ini 文件保存到 HEAD_FILES + file(GLOB_RECURSE FIND_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS ${EXTENSIONS}) + # 将 HEDADER_FILES 和 SRC_FILES 保存到 ALL_FILES 变量 + set(ALL_FILES ${FIND_FILES}) + + set(RESULT "") + + # 对 ALL_FILES 变量里面的所有文件分类(保留资源管理器的目录结构) + foreach(fileItem ${ALL_FILES}) + # Get the directory of the source file + get_filename_component(PARENT_DIR "${fileItem}" DIRECTORY) + + # 用于检查平台的条件 + if(PARENT_DIR STREQUAL "windows") + if(WIN32) + set(RESULT "${RESULT};${fileItem}") + else() + continue() + endif() + elseif(PARENT_DIR STREQUAL "linux") + if(UNIX AND NOT APPLE) + set(RESULT "${RESULT};${fileItem}") + else() + continue() + endif() + elseif(PARENT_DIR STREQUAL "mac") + if(APPLE) + set(RESULT "${RESULT};${fileItem}") + else() + continue() + endif() + else() + # 如果文件夹名称不是平台,则始终包含 + set(RESULT "${RESULT};${fileItem}") + endif() + + # Remove common directory prefix to make the group + string(REPLACE "${path}" "" GROUP "${PARENT_DIR}") + # Make sure we are using windows slashes + string(REPLACE "/" "\\" GROUP "${GROUP}") + # Group into "Source Files" and "Header Files" + set(GROUP "${GROUP}") + source_group("${GROUP}" FILES "${fileItem}") + endforeach() + + set(${out_files} ${RESULT} PARENT_SCOPE) +endfunction() + +function(retrieve_files path out_files) + set(temp_files "") + retrieve_files_custom(${path} "h;hpp;ini;cpp;c;ixx" temp_files) + set(${out_files} ${${out_files}} ${temp_files} PARENT_SCOPE) +endfunction() diff --git a/src/FormulaParser.cpp b/src/FormulaParser.cpp new file mode 100644 index 0000000..116d44a --- /dev/null +++ b/src/FormulaParser.cpp @@ -0,0 +1,517 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "FormulaParser.h" + +using namespace fparse; +using namespace peg; +using namespace std; + +// 语法 +const char* FormulaParser::grammar = R"( + INPUT <- EXPRESSION {no_ast_opt} + EXPRESSION <- ATOM (OPERATOR ATOM)* { + precedence + L ^ + L & | + L + - + L * / % + L << >> + } + ATOM <- NUMBER / FUNCCALL / VAR / '(' EXPRESSION ')' + FUNCCALL <- FUNCNAME '(' ( EXPRESSION ( ',' EXPRESSION )* )? ')' { no_ast_opt } + OPERATOR <- < '+' | '-' | '*' | '/' | '%' | '^' | '&' | '|' | '>>' | '<<' > + NUMBER <- < '-'? [0-9]+ > + FUNCNAME <- < [a-zA-Z_] [0-9a-zA-Z_]* > & '(' + VAR <- < [a-zA-Z_] [0-9a-zA-Z_]* > ! '(' + %whitespace <- [ \t\n\r]* ( ( ('//' [^\n\r]* [\n\r]*) / ('/*' (!'*/' .)* '*/') ) [ \t\n\r]* )* + )"; + +const EvaluationResult FormulaParser::sine_table({ + 127, 130, 133, 136, 139, 143, 146, 149, 152, 155, 158, 161, 164, + 167, 170, 173, 176, 179, 182, 184, 187, 190, 193, 195, 198, 200, + 203, 205, 208, 210, 213, 215, 217, 219, 221, 224, 226, 228, 229, + 231, 233, 235, 236, 238, 239, 241, 242, 244, 245, 246, 247, 248, + 249, 250, 251, 251, 252, 253, 253, 254, 254, 254, 254, 254, 255, + 254, 254, 254, 254, 254, 253, 253, 252, 251, 251, 250, 249, 248, + 247, 246, 245, 244, 242, 241, 239, 238, 236, 235, 233, 231, 229, + 228, 226, 224, 221, 219, 217, 215, 213, 210, 208, 205, 203, 200, + 198, 195, 193, 190, 187, 184, 182, 179, 176, 173, 170, 167, 164, + 161, 158, 155, 152, 149, 146, 143, 139, 136, 133, 130, 127, 124, + 121, 118, 115, 111, 108, 105, 102, 99, 96, 93, 90, 87, 84, + 81, 78, 75, 72, 70, 67, 64, 61, 59, 56, 54, 51, 49, + 46, 44, 41, 39, 37, 35, 33, 30, 28, 26, 25, 23, 21, + 19, 18, 16, 15, 13, 12, 10, 9, 8, 7, 6, 5, 4, + 3, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 6, 7, 8, + 9, 10, 12, 13, 15, 16, 18, 19, 21, 23, 25, 26, 28, + 30, 33, 35, 37, 39, 41, 44, 46, 49, 51, 54, 56, 59, + 61, 64, 67, 70, 72, 75, 78, 81, 84, 87, 90, 93, 96, + 99, 102, 105, 108, 111, 115, 118, 121, 124 }); + +const EvaluationResult FormulaParser::triangle_table({ + 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, + 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, + 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, + 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, + 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, + 253, 251, 249, 247, 245, 243, 241, 239, 237, 235, 233, 231, 229, + 227, 225, 223, 221, 219, 217, 215, 213, 211, 209, 207, 205, 203, + 201, 199, 197, 195, 193, 191, 189, 187, 185, 183, 181, 179, 177, + 175, 173, 171, 169, 167, 165, 163, 161, 159, 157, 155, 153, 151, + 149, 147, 145, 143, 141, 139, 137, 135, 133, 131, 129, 127, 125, + 123, 121, 119, 117, 115, 113, 111, 109, 107, 105, 103, 101, 99, + 97, 95, 93, 91, 89, 87, 85, 83, 81, 79, 77, 75, 73, + 71, 69, 67, 65, 63, 61, 59, 57, 55, 53, 51, 49, 47, + 45, 43, 41, 39, 37, 35, 33, 31, 29, 27, 25, 23, 21, + 19, 17, 15, 13, 11, 9, 7, 5, 3, 1, 0, 1, 3, + 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, + 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, + 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, + 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 107, + 109, 111, 113, 115, 117, 119, 121, 123, 125 }); + +// 合法变量名及常数化简求值用的暂时变量值 +unordered_map FormulaParser::temp_vars = { + {"T", EvaluationResult(0)}, + {"t", EvaluationResult(0)}, + {"w", EvaluationResult(0)}, + {"x", EvaluationResult(0)}, + {"y", EvaluationResult(0)}, + {"z", EvaluationResult(0)}, + //{"env1", EvaluationResult(0)}, + //{"env2", EvaluationResult(0)}, + //{"env3", EvaluationResult(0)}, + //{"env4", EvaluationResult(0)} +}; + +// 合法的函数名及实现 +unordered_map FormulaParser::function_dictionary = { + { + "sin", + { [](const vector>& args, const unordered_map& vars, size_t block_size) -> EvaluationResult { + return xt::index_view(FormulaParser::sine_table, args[0]->evaluate(vars, block_size) & 255); + }, 1 , 1} + }, + { + "cos", + { [](const vector>& args, const unordered_map& vars, size_t block_size) -> EvaluationResult { + return xt::index_view(FormulaParser::sine_table, (args[0]->evaluate(vars, block_size) + 64) & 255); + }, 1 , 1} + }, + { + "tri", + { [](const vector>& args, const unordered_map& vars, size_t block_size) -> EvaluationResult { + return xt::index_view(FormulaParser::triangle_table, args[0]->evaluate(vars, block_size) & 255); + }, 1 , 1} + }, + { + "rand", + { [](const vector>& args, const unordered_map& vars, size_t block_size) -> EvaluationResult { + return xt::random::randint({block_size}, 0, 255); + }, 0 , 0} + }, + { + "abs", + { [](const vector>& args, const unordered_map& vars, size_t block_size) -> EvaluationResult { + return xt::abs(args[0]->evaluate(vars, block_size)); + }, 1 , 1} + }, + { + "srand", + { [](const vector>& args, const unordered_map& vars, size_t block_size) -> EvaluationResult { + EvaluationResult r = args[0]->evaluate(vars, block_size); + r = ((r + 3463) * 2971); + r = r ^ (r << 13); + r = r ^ (r >> 17); + r = r ^ (r << 5); + r = ((r + 2551) * 3571); + r = r ^ (r << 13); + r = r ^ (r >> 17); + return r ^ (r << 5); + }, 1 , 1} + } +}; + +// 变量类 +Variable::Variable(const string& name) : name(name) {}; + +EvaluationResult Variable::evaluate(const unordered_map& vars, size_t block_size) const { // evaluation + // vars 应存放 broadcast 后的向量 + return vars.at(name); +} + + +// 常量类 +Constant::Constant(int32_t value) : value(value) {}; + +EvaluationResult Constant::evaluate(const unordered_map&, size_t block_size) const { // evaluation + return xt::broadcast(value, { block_size }); +} + + +// 二元表达式类 +CompoundExpression::CompoundExpression(Operation op, shared_ptr lhs, shared_ptr rhs) + : l(lhs), r(rhs), operation(op) {} + +inline EvaluationResult xdiv(EvaluationResult left_value, EvaluationResult right_value) { + if (left_value.shape() != right_value.shape()) + right_value = xt::broadcast(right_value, left_value.shape()); + auto&& safe_right_value = xt::where(xt::equal(right_value, 0), 1, right_value); + return xt::where(xt::equal(right_value, 0), 0, left_value / safe_right_value); +} + +inline EvaluationResult xmod(EvaluationResult left_value, EvaluationResult right_value) { + if (left_value.shape() != right_value.shape()) + right_value = xt::broadcast(right_value, left_value.shape()); + auto&& safe_right_value = xt::where(xt::equal(right_value, 0), 1, right_value); + return xt::where(xt::equal(right_value, 0), 0, left_value % safe_right_value); +} + +EvaluationResult CompoundExpression::evaluate(const unordered_map& vars, size_t block_size) const { + EvaluationResult left_value = l->evaluate(vars, block_size); // l operand + EvaluationResult right_value = r->evaluate(vars, block_size); // r operand + + switch (operation) { + case Operation::ADD: return left_value + right_value; + case Operation::SUBTRACT: return left_value - right_value; + case Operation::MULTIPLY: return left_value * right_value; + case Operation::DIVIDE: return xdiv(left_value, right_value); + case Operation::MOD: return xmod(left_value, right_value); + case Operation::AND: return left_value & right_value; + case Operation::OR: return left_value | right_value; + case Operation::XOR: return left_value ^ right_value; + case Operation::SHIFT_LEFT: return left_value << (right_value & 15); + case Operation::SHIFT_RIGHT: return left_value >> (right_value & 15); + default: throw invalid_argument("Invalid operation"); // invalid operation + } +} + + +// 函数表达式类 +FunctionExpression::FunctionExpression(string function_name, vector> function_args) + :name(function_name), function(FormulaParser::function_dictionary.at(function_name)), args(function_args) { +} + +EvaluationResult FunctionExpression::evaluate(const unordered_map& vars, size_t block_size) const { + return function.function(args, vars, block_size); +} + + +// + +shared_ptr operator+(shared_ptr lhs, shared_ptr rhs) { + // Constant simplify + if (lhs->isConstant() && rhs->isConstant()) { + auto l = dynamic_pointer_cast(lhs); + auto r = dynamic_pointer_cast(rhs); + return make_shared(l->value + r->value); + } // preprocess 0 + else if (lhs->isConstant() && dynamic_pointer_cast(lhs)->value == 0) + return rhs; + else if (rhs->isConstant() && dynamic_pointer_cast(rhs)->value == 0) + return lhs; + // build new expression object + return make_shared(Operation::ADD, lhs, rhs); +} +// - +shared_ptr operator-(shared_ptr lhs, shared_ptr rhs) { + if (lhs->isConstant() && rhs->isConstant()) { + auto l = dynamic_pointer_cast(lhs); + auto r = dynamic_pointer_cast(rhs); + return make_shared(l->value - r->value); + } + else if (rhs->isConstant() && dynamic_pointer_cast(rhs)->value == 0) + return lhs; + + return make_shared(Operation::SUBTRACT, lhs, rhs); +} +// * +shared_ptr operator*(shared_ptr lhs, shared_ptr rhs) { + if (lhs->isConstant() && rhs->isConstant()) { + auto l = dynamic_pointer_cast(lhs); + auto r = dynamic_pointer_cast(rhs); + return make_shared(l->value * r->value); + } + else if (lhs->isConstant() && dynamic_pointer_cast(lhs)->value == 0) + return make_shared(0); + else if (rhs->isConstant() && dynamic_pointer_cast(rhs)->value == 0) + return make_shared(0); + + return make_shared(Operation::MULTIPLY, lhs, rhs); +} +// / +shared_ptr operator/(shared_ptr lhs, shared_ptr rhs) { + if (lhs->isConstant() && rhs->isConstant()) { + auto l = dynamic_pointer_cast(lhs); + auto r = dynamic_pointer_cast(rhs); + if (r->value == 0) + return make_shared(0); + return make_shared(l->value / r->value); + } + else if (lhs->isConstant() && dynamic_pointer_cast(lhs)->value == 0) + return make_shared(0); + else if (rhs->isConstant() && dynamic_pointer_cast(rhs)->value == 0) + return make_shared(0); + + return make_shared(Operation::DIVIDE, lhs, rhs); +} +// % +shared_ptr operator%(shared_ptr lhs, shared_ptr rhs) { + if (lhs->isConstant() && rhs->isConstant()) { + auto l = dynamic_pointer_cast(lhs); + auto r = dynamic_pointer_cast(rhs); + if (r->value == 0) + return make_shared(0); + return make_shared(l->value % r->value); + } + else if (lhs->isConstant() && dynamic_pointer_cast(lhs)->value == 0) + return make_shared(0); + else if (rhs->isConstant() && dynamic_pointer_cast(rhs)->value == 0) + return make_shared(0); + + return make_shared(Operation::MOD, lhs, rhs); +} +// & +shared_ptr operator&(shared_ptr lhs, shared_ptr rhs) { + if (lhs->isConstant() && rhs->isConstant()) { + auto l = dynamic_pointer_cast(lhs); + auto r = dynamic_pointer_cast(rhs); + return make_shared(l->value & r->value); + } + else if (lhs->isConstant() && dynamic_pointer_cast(lhs)->value == 0) + return make_shared(0); + else if (rhs->isConstant() && dynamic_pointer_cast(rhs)->value == 0) + return make_shared(0); + + return make_shared(Operation::AND, lhs, rhs); +} +// | +shared_ptr operator|(shared_ptr lhs, shared_ptr rhs) { + if (lhs->isConstant() && rhs->isConstant()) { + auto l = dynamic_pointer_cast(lhs); + auto r = dynamic_pointer_cast(rhs); + return make_shared(l->value | r->value); + } + else if (lhs->isConstant() && dynamic_pointer_cast(lhs)->value == 0) + return rhs; + else if (rhs->isConstant() && dynamic_pointer_cast(rhs)->value == 0) + return lhs; + + return make_shared(Operation::OR, lhs, rhs); +} +// ^ +shared_ptr operator^(shared_ptr lhs, shared_ptr rhs) { + if (lhs->isConstant() && rhs->isConstant()) { + auto l = dynamic_pointer_cast(lhs); + auto r = dynamic_pointer_cast(rhs); + return make_shared(l->value ^ r->value); + } + else if (lhs->isConstant() && dynamic_pointer_cast(lhs)->value == 0) + return rhs; + else if (rhs->isConstant() && dynamic_pointer_cast(rhs)->value == 0) + return lhs; + + return make_shared(Operation::XOR, lhs, rhs); +} +// << +shared_ptr operator<<(shared_ptr lhs, shared_ptr rhs) { + if (lhs->isConstant() && rhs->isConstant()) { + auto l = dynamic_pointer_cast(lhs); + auto r = dynamic_pointer_cast(rhs); + return make_shared(l->value << (r->value & 15)); + } + else if (lhs->isConstant() && dynamic_pointer_cast(lhs)->value == 0) + return make_shared(0); + else if (rhs->isConstant() && dynamic_pointer_cast(rhs)->value % 16 == 0) + return lhs; + + return make_shared(Operation::SHIFT_LEFT, lhs, rhs); +} +// >> +shared_ptr operator>>(shared_ptr lhs, shared_ptr rhs) { + if (lhs->isConstant() && rhs->isConstant()) { + auto l = dynamic_pointer_cast(lhs); + auto r = dynamic_pointer_cast(rhs); + return make_shared(l->value >> (r->value & 15)); + } + else if (lhs->isConstant() && dynamic_pointer_cast(lhs)->value == 0) + return make_shared(0); + else if (rhs->isConstant() && dynamic_pointer_cast(rhs)->value % 16 == 0) + return lhs; + + return make_shared(Operation::SHIFT_RIGHT, lhs, rhs); +} + +shared_ptr castToExpression(const any& value) { + if (value.type() == typeid(shared_ptr)) + return any_cast>(value); + + if (value.type() == typeid(shared_ptr)) + return any_cast>(value); + + if (value.type() == typeid(shared_ptr)) + return any_cast>(value); + + if (value.type() == typeid(shared_ptr)) + return any_cast>(value); + + if (value.type() == typeid(shared_ptr)) + return any_cast>(value); + + cout << value.type().name() << ";" << endl; + throw bad_any_cast(); +} + + +// 解析器类 +FormulaParser::FormulaParser() : parser(grammar) { + assert(static_cast(parser) == true); // debug + + // INPUT pattern + parser["INPUT"] = [](const SemanticValues& vs) { + return castToExpression(vs[0]); + }; + + // EXPRESSION pattern + parser["EXPRESSION"] = [](const SemanticValues& vs) { + auto result = castToExpression(vs[0]); + if (vs.size() > 1) { + auto ope = any_cast(vs[1]); + auto expr = castToExpression(vs[2]); + switch (ope) { + case '+': result = result + expr; break; + case '-': result = result - expr; break; + case '*': result = result * expr; break; + case '/': result = result / expr; break; + case '%': result = result % expr; break; + case '&': result = result & expr; break; + case '|': result = result | expr; break; + case '^': result = result ^ expr; break; + case '<': result = result << expr; break; + case '>': result = result >> expr; break; + } + } + return result; + }; + + // FUNCCALL pattern + parser["FUNCCALL"] = [](const SemanticValues& vs) -> shared_ptr { + auto name = any_cast(vs[0]); + + vector> args; + + if (vs.size() > 1) { + bool constant_flag = true; + + for (size_t i = 1; i < vs.size(); i++) { + shared_ptr expr = castToExpression(vs[i]); + // 添加参数 + args.push_back(expr); + // 检查函数的所有参数是否均为常数 + if (!expr->isConstant()) constant_flag = false; + } + + // 如果所有参数均为常数 + if (constant_flag) { + std::unordered_map empty_map; // 空的 unordered_map, 不占用实际空间 + FunctionType function = FormulaParser::function_dictionary.at(name).function; + return make_shared(function(args, empty_map, 1)[0]); + } + } + + return make_shared(name, args); + }; + + parser["FUNCCALL"].predicate = [](const SemanticValues& vs, const any&, string& msg) { + auto name = any_cast(vs[0]); + + // 检查函数的参数量是否合适 + FunctionWithBound function = FormulaParser::function_dictionary.at(name); + + if ((function.lower_bound >= 0 && vs.size() - 1 < function.lower_bound) || (function.upper_bound >= 0 && vs.size() - 1 > function.upper_bound)) { + msg = "The " + name + " function has an inappropriate number of parameters: " + to_string(vs.size() - 1) + "."; + return false; + } + return true; + }; + + // OPERATOR token + parser["OPERATOR"] = [](const SemanticValues& vs) { + return vs.token_to_string()[0]; + }; + + // NUMBER token + parser["NUMBER"] = [](const SemanticValues& vs) { + return make_shared(vs.token_to_number()); + }; + + // VAR token + parser["VAR"] = [](const SemanticValues& vs) { + return make_shared(vs.token_to_string()); + }; + + parser["VAR"].predicate = [](const SemanticValues& vs, const any&, string& msg) { + // 检查是否存在该名字的函数 + auto name = any_cast(vs.token_to_string()); + + if (FormulaParser::temp_vars.count(name) == 0) { + msg = "Unknown variable " + name + "."; + return false; + }; + return true; + }; + + // FUNCNAME token + parser["FUNCNAME"] = [](const SemanticValues& vs) { + return vs.token_to_string(); + }; + + parser["FUNCNAME"].predicate = [](const SemanticValues& vs, const any&, string& msg) { + // 检查是否存在该名字的函数 + auto name = any_cast(vs.token_to_string()); + + if (FormulaParser::function_dictionary.count(name) == 0) { + msg = "Unknown function " + name + "."; + return false; + }; + return true; + }; + + parser.enable_packrat_parsing(); +}; + +ParseResult FormulaParser::parse(string& input) { + + ParseResult result = { false, nullptr, 0, 0, "", "" }; + + parser.set_logger([&result](size_t line, size_t col, const string& msg, const string& rule) { + result = { false, nullptr, line, col, msg, rule }; + }); + + shared_ptr expr; + + try { + bool parse_success = parser.parse(input, expr); // logger 在此处被调用 + if (parse_success) + result = { true, expr, 0, 0, "", "" }; // 解析错误不会作为异常被抛出 + } + catch (const std::exception& e) { // 标准异常 + result = { false, nullptr, 0, 0, e.what(), "" }; + } + catch (...) { // 未知的潜在异常 + result = { false, nullptr, 0, 0, "Unknown Exception", "" }; + } + + return result; +}; + diff --git a/src/FormulaParser.h b/src/FormulaParser.h new file mode 100644 index 0000000..03f1644 --- /dev/null +++ b/src/FormulaParser.h @@ -0,0 +1,123 @@ +#ifndef PARSER_H +#define PARSER_H + +#include +#include +#include +#include + +#include +#include + +namespace fparse { + // ¶¨Òå²Ù×÷·ûµÄö¾Ù + enum class Operation { + NONE, // ´ú±íûÓвÙ×÷ + ADD, + SUBTRACT, + MULTIPLY, + DIVIDE, + MOD, + AND, + OR, + XOR, + SHIFT_LEFT, + SHIFT_RIGHT + }; + + using EvaluationResult = xt::xarray; + + // ±í´ïʽ»ùÀà + class Expression { + public: + virtual ~Expression() = default; + virtual bool isConstant() const = 0; // constant simplify + virtual EvaluationResult evaluate(const std::unordered_map& vars, size_t block_size) const = 0; // evaluation + }; + + // ÄäÃûº¯ÊýµÄÀàÐÍ + using FunctionType = std::function>&, const std::unordered_map&, size_t)>; + + // ÄäÃûº¯ÊýµÄº¯Êý²ÎÊý¶¨ÒåÀàÐÍ + struct FunctionWithBound { + FunctionType function; // º¯Êý±¾Ìå + int16_t lower_bound; // ²ÎÊýÁ¿ÉϽç + int16_t upper_bound; // ²ÎÊýÁ¿Ï½ç + }; + + + // ±äÁ¿Àà + class Variable : public Expression { + public: + std::string name; // Var name + + Variable(const std::string& name); + ~Variable() override {}; + bool isConstant() const override { return false; } // constant simplify + EvaluationResult evaluate(const std::unordered_map& vars, size_t block_size) const override; // evaluation + }; + + // ³£Á¿Àà + class Constant : public Expression { + public: + int32_t value; + + Constant(int32_t value); + ~Constant() override {} + bool isConstant() const override { return true; } // constant simplify + EvaluationResult evaluate(const std::unordered_map&, size_t block_size) const override; // evaluation + }; + + // ¶þÔª±í´ïʽÀà + class CompoundExpression : public Expression { + public: + std::shared_ptr l, r; // operands + Operation operation; + + CompoundExpression(Operation op, std::shared_ptr lhs, std::shared_ptr rhs); + ~CompoundExpression() override {} + bool isConstant() const override { return false; } // constant simplify + EvaluationResult evaluate(const std::unordered_map& vars, size_t block_size) const override; // evaluation + }; + + // º¯Êý±í´ïʽÀà + class FunctionExpression : public Expression { + public: + std::string name; // function name + FunctionWithBound function; // function + std::vector> args; // function arguments + + FunctionExpression(std::string function_name, std::vector> function_args); + ~FunctionExpression() override {} + bool isConstant() const override { return false; } // constant simplify + EvaluationResult evaluate(const std::unordered_map& vars, size_t block_size) const override; // evaluation + }; + + // ½âÎö½á¹û + struct ParseResult { + bool success; + std::shared_ptr expr; + size_t line; + size_t col; + std::string msg; + std::string rule; + }; + + // ½âÎöÆ÷Àà + class FormulaParser { + public: + static const char* grammar; + + static const EvaluationResult sine_table, triangle_table; + + static std::unordered_map temp_vars; + static std::unordered_map function_dictionary; + + FormulaParser(); + ParseResult parse(std::string& input); + + private: + peg::parser parser; + }; +}; +#endif \ No newline at end of file diff --git a/src/JuceHeader.h b/src/JuceHeader.h new file mode 100644 index 0000000..e92b24e --- /dev/null +++ b/src/JuceHeader.h @@ -0,0 +1,51 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + + This is the header file that your files should include in order to get all the + JUCE library headers. You should avoid including the JUCE headers directly in + your own source files, because that wouldn't pick up the correct configuration + options for your app. + +*/ + +#pragma once + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined (JUCE_PROJUCER_VERSION) && JUCE_PROJUCER_VERSION < JUCE_VERSION + /** If you've hit this error then the version of the Projucer that was used to generate this project is + older than the version of the JUCE modules being included. To fix this error, re-save your project + using the latest version of the Projucer or, if you aren't using the Projucer to manage your project, + remove the JUCE_PROJUCER_VERSION define. + */ + #error "This project was last saved using an outdated version of the Projucer! Re-save this project with the latest version to fix this error." +#endif + + +#if ! JUCE_DONT_DECLARE_PROJECTINFO +namespace ProjectInfo +{ + const char* const projectName = "BitAlchemy"; + const char* const companyName = ""; + const char* const versionString = "1.0.0"; + const int versionNumber = 0x10000; +} +#endif diff --git a/src/PluginEditor.cpp b/src/PluginEditor.cpp new file mode 100644 index 0000000..92f2b19 --- /dev/null +++ b/src/PluginEditor.cpp @@ -0,0 +1,164 @@ +/* + ============================================================================== + + This file contains the basic framework code for a JUCE plugin editor. + + ============================================================================== +*/ + +#include "PluginProcessor.h" +#include "PluginEditor.h" + +using namespace std; + +//============================================================================== +FormulaEditor::FormulaEditor(FormulaManager& m) : juce::TextEditor("FormulaEditor") +{ + setMultiLine(true, false); // 多行模式,不强制断行 + setScrollbarsShown(true); // 显示滚动条 + setTabKeyUsedAsCharacter(true); // 允许键入Tab + setReturnKeyStartsNewLine(true); // 允许换行 + + manager = &m; // audio processor + + updateText(); // 更新文本内容 + + onTextChange = [this, &m]() -> void { // 定义文本内容变化时的操作 + auto formula_string = getText().toStdString(); + m.setFormula(formula_string); // 写入 Formula + setOutlineColourToYellow(); + repaint(); + }; + + if (manager->isFormulaParsed()) // 初始化时更新颜色 + setOutlineColourToGreen(); + else + setOutlineColourToYellow(); + + repaint(); +}; + +// 处理特殊按键 +bool FormulaEditor::keyPressed(const juce::KeyPress& key) { + // 按下 shift + enter 的逻辑 + if (key == juce::KeyPress(juce::KeyPress::returnKey, juce::ModifierKeys::shiftModifier, NULL)) { + + auto result = manager->parse(); + + if (result.success) { + // parse 成功时的逻辑 + setOutlineColourToGreen(); + } + else { + // parse 失败时的逻辑 + setOutlineColourToRed(); + // 显示错误信息 + // ... + } + repaint(); + return true; + } + + // 按下其它按键的逻辑 + return juce::TextEditor::keyPressed(key); +}; + +// 更新文本 +inline void FormulaEditor::updateText() { + juce::TextEditor::setText(manager->getFormula()); +}; + +// 设置边框颜色 +inline void FormulaEditor::setOutlineColourToGreen() { + setColour(focusedOutlineColourId, juce::Colour(98, 228, 117)); + setColour(outlineColourId, juce::Colour(31, 183, 53)); +} + +inline void FormulaEditor::setOutlineColourToRed() { + setColour(focusedOutlineColourId, juce::Colour(228, 98, 98)); + setColour(outlineColourId, juce::Colour(183, 31, 31)); +} + +inline void FormulaEditor::setOutlineColourToYellow() { + setColour(focusedOutlineColourId, juce::Colour(231, 228, 98)); + setColour(outlineColourId, juce::Colour(187, 183, 31)); +} + +inline void FormulaEditor::attachToNewFormulaManager(FormulaManager& m) { + manager = &m; +} + +//============================================================================== +_8BitSynthAudioProcessorEditor::_8BitSynthAudioProcessorEditor(_8BitSynthAudioProcessor& p) + : AudioProcessorEditor(&p), audioProcessor(p), formula_editor(p.formula_manager), + w_attachment(p.apvts, "w", w_slider), + x_attachment(p.apvts, "x", x_slider), + y_attachment(p.apvts, "y", y_slider), + z_attachment(p.apvts, "z", z_slider) +{ + for (auto slider : getRotarySliders()) + addAndMakeVisible(slider); + addAndMakeVisible(formula_editor); + + setSize (600, 450); +} + + + +_8BitSynthAudioProcessorEditor::~_8BitSynthAudioProcessorEditor() +{ +} + +//============================================================================== +void _8BitSynthAudioProcessorEditor::paint (juce::Graphics& g) +{ + // (Our component is opaque, so we must completely fill the background with a solid colour) + g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId)); + + g.setColour (juce::Colours::white); + // g.setFont (juce::FontOptions (15.0f)); + //g.drawFittedText ("Hello World!", getLocalBounds(), juce::Justification::centred, 1); +} + +juce::Rectangle CenterBox(juce::Rectangle& area, double border_rate) { + auto border_width = area.getWidth() * border_rate; + auto border_height = area.getHeight() * border_rate; + + area.removeFromTop(border_height); + area.removeFromBottom(border_height); + area.removeFromLeft(border_width); + area.removeFromRight(border_width); + + return area; +} + +void _8BitSynthAudioProcessorEditor::resized() +{ + // This is generally where you'll want to lay out the positions of any + // subcomponents in your editor.. + auto bounds = getLocalBounds(); + + auto border_width = bounds.getWidth() * 0.05; + + auto formula_editor_area = bounds.removeFromTop(bounds.getHeight() * 0.75); + formula_editor_area.removeFromLeft(border_width); + formula_editor_area.removeFromRight(border_width); + formula_editor.setBounds(formula_editor_area); + + auto rotary_slider_area_width = bounds.getWidth() * 0.25; + + auto area = bounds.removeFromLeft((int)rotary_slider_area_width); + w_slider.setBounds(CenterBox(area, 0.2)); + x_slider.setBounds(CenterBox(area, 0.2)); + y_slider.setBounds(CenterBox(area, 0.2)); + z_slider.setBounds(CenterBox(area, 0.2)); +} + +std::vector _8BitSynthAudioProcessorEditor::getRotarySliders() { + return { + &w_slider, + &x_slider, + &y_slider, + &z_slider + }; +} \ No newline at end of file diff --git a/src/PluginEditor.h b/src/PluginEditor.h new file mode 100644 index 0000000..ba4cb31 --- /dev/null +++ b/src/PluginEditor.h @@ -0,0 +1,77 @@ +/* + ============================================================================== + + This file contains the basic framework code for a JUCE plugin editor. + + ============================================================================== +*/ + +#pragma once + +#include +#include "PluginProcessor.h" + +//============================================================================== +/** +*/ + +class RotarySlider : public juce::Slider { +public: + RotarySlider() : juce::Slider( + juce::Slider::SliderStyle::RotaryHorizontalVerticalDrag, + juce::Slider::TextEntryBoxPosition::TextBoxAbove + ) {}; +}; + +class FormulaEditor : public juce::TextEditor { +public: + FormulaEditor(FormulaManager& m); + + bool keyPressed(const juce::KeyPress& key); // 处理按键事件 + + inline void updateText(); // 从 Formula Manager 获取文本并更新 + + inline void attachToNewFormulaManager(FormulaManager& m); // 绑定到新的 FormulaManager 上 + + inline void setOutlineColourToGreen(); // 设置边框颜色 + inline void setOutlineColourToRed(); + inline void setOutlineColourToYellow(); + +private: + FormulaManager* manager; +}; + + +class _8BitSynthAudioProcessorEditor : public juce::AudioProcessorEditor +{ +public: + _8BitSynthAudioProcessorEditor (_8BitSynthAudioProcessor&); + ~_8BitSynthAudioProcessorEditor() override; + + //============================================================================== + void paint (juce::Graphics&) override; + void resized() override; + +private: + _8BitSynthAudioProcessor& audioProcessor; + + RotarySlider + w_slider, + x_slider, + y_slider, + z_slider; // wxyz 旋钮 + + FormulaEditor formula_editor; // 框 + + juce::AudioProcessorValueTreeState::SliderAttachment + w_attachment, + x_attachment, + y_attachment, + z_attachment; // 绑定参数 + + std::vector getRotarySliders(); // 便于获取 4 个旋钮的方法 + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (_8BitSynthAudioProcessorEditor) +}; + + diff --git a/src/PluginProcessor.cpp b/src/PluginProcessor.cpp new file mode 100644 index 0000000..a90b1d8 --- /dev/null +++ b/src/PluginProcessor.cpp @@ -0,0 +1,219 @@ +#include "PluginProcessor.h" +#include "PluginEditor.h" +#include + + +//============================================================================== +_8BitSynthAudioProcessor::_8BitSynthAudioProcessor() +#ifndef JucePlugin_PreferredChannelConfigurations + : AudioProcessor (BusesProperties() + #if ! JucePlugin_IsMidiEffect + #if ! JucePlugin_IsSynth + .withInput ("Input", juce::AudioChannelSet::stereo(), true) + #endif + .withOutput ("Output", juce::AudioChannelSet::stereo(), true) + #endif + ) +#endif +, formula_manager(parser) { + for (auto i = 0; i < 16; ++i) + synth.addVoice(new _8BitSynthVoice(formula_manager, apvts, bpm)); + + synth.addSound(new _8BitSynthSound()); +} + +_8BitSynthAudioProcessor::~_8BitSynthAudioProcessor() +{ + +} + +//============================================================================== +const juce::String _8BitSynthAudioProcessor::getName() const +{ + return "bit_alchemy"; +} + +bool _8BitSynthAudioProcessor::acceptsMidi() const +{ + #if JucePlugin_WantsMidiInput + return true; + #else + return false; + #endif +} + +bool _8BitSynthAudioProcessor::producesMidi() const +{ + #if JucePlugin_ProducesMidiOutput + return true; + #else + return false; + #endif +} + +bool _8BitSynthAudioProcessor::isMidiEffect() const +{ + #if JucePlugin_IsMidiEffect + return true; + #else + return false; + #endif +} + +double _8BitSynthAudioProcessor::getTailLengthSeconds() const +{ + return 0.0; +} + +int _8BitSynthAudioProcessor::getNumPrograms() +{ + return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, + // so this should be at least 1, even if you're not really implementing programs. +} + +int _8BitSynthAudioProcessor::getCurrentProgram() +{ + return 0; +} + +void _8BitSynthAudioProcessor::setCurrentProgram (int index) +{ +} + +const juce::String _8BitSynthAudioProcessor::getProgramName (int index) +{ + return {}; +} + +void _8BitSynthAudioProcessor::changeProgramName (int index, const juce::String& newName) +{ +} + +//============================================================================== +void _8BitSynthAudioProcessor::prepareToPlay (double currentSampleRate, int currentSamplesPerBlock) +{ + uint8_t oversampling_factor = apvts.getRawParameterValue("oversampling_factor")->load(); + + synth.setCurrentPlaybackSampleRate(currentSampleRate * oversampling_factor); + + constexpr auto filterType = juce::dsp::Oversampling::filterHalfBandPolyphaseIIR; + + oversampler = std::make_unique>(2, oversampling_factor, filterType); + oversampler->initProcessing(currentSamplesPerBlock); +} + +void _8BitSynthAudioProcessor::releaseResources() +{ + // When playback stops, you can use this as an opportunity to free up any + // spare memory, etc. +} + +#ifndef JucePlugin_PreferredChannelConfigurations +bool _8BitSynthAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const +{ + #if JucePlugin_IsMidiEffect + juce::ignoreUnused (layouts); + return true; + #else + // This is the place where you check if the layout is supported. + // In this template code we only support mono or stereo. + // Some plugin hosts, such as certain GarageBand versions, will only + // load plugins that support stereo bus layouts. + if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() + && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) + return false; + + // This checks if the input layout matches the output layout + #if ! JucePlugin_IsSynth + if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) + return false; + #endif + + return true; + #endif +} +#endif + +void _8BitSynthAudioProcessor::processBlock (juce::AudioBuffer& buffer, juce::MidiBuffer& midiMessages) +{ + buffer.clear(); + + // 获取播放头 + auto position = getPlayHead()->getPosition(); + + + // 获取 bpm + bpm = -1.; + + if (position.hasValue()) { + auto bpm_info = position->getBpm(); + if (bpm_info.hasValue()) + if (*bpm_info != 0.) + bpm = *bpm_info; + }; + + + // 重新设置滤波器 + auto currentSampleRate = getSampleRate(); + auto currentSamplesPerBlock = buffer.getNumSamples(); + + uint8_t oversampling_factor = apvts.getRawParameterValue("oversampling_factor")->load(); + + + // 过采样 + juce::dsp::AudioBlock block(buffer); + juce::dsp::AudioBlock osBlock = oversampler->processSamplesUp(block); + float* p[] = {osBlock.getChannelPointer(0), osBlock.getChannelPointer(1)}; + juce::AudioBuffer osBuffer(p, 2, static_cast (osBlock.getNumSamples())); + synth.setCurrentPlaybackSampleRate(currentSampleRate * oversampling_factor); + synth.renderNextBlock(osBuffer, midiMessages, 0, osBlock.getNumSamples()); + oversampler->processSamplesDown(block); + + osBlock.clear(); +} + +//============================================================================== +bool _8BitSynthAudioProcessor::hasEditor() const +{ + return true; // (change this to false if you choose to not supply an editor) +} + +juce::AudioProcessorEditor* _8BitSynthAudioProcessor::createEditor() +{ + return new _8BitSynthAudioProcessorEditor (*this); +} + +//============================================================================== +void _8BitSynthAudioProcessor::getStateInformation (juce::MemoryBlock& destData) +{ + // You should use this method to store your parameters in the memory block. + // You could do that either as raw data, or use the XML or ValueTree classes + // as intermediaries to make it easy to save and load complex data. +} + +void _8BitSynthAudioProcessor::setStateInformation (const void* data, int sizeInBytes) +{ + // You should use this method to restore your parameters from this memory block, + // whose contents will have been created by the getStateInformation() call. +} + +//============================================================================== +// This creates new instances of the plugin.. +juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() +{ + return new _8BitSynthAudioProcessor(); +} + +juce::AudioProcessorValueTreeState::ParameterLayout _8BitSynthAudioProcessor::createParameterLayout() { + + juce::AudioProcessorValueTreeState::ParameterLayout layout; + + layout.add(std::make_unique("w", "w", 0, 255, 0)); + layout.add(std::make_unique("x", "x", 0, 255, 0)); + layout.add(std::make_unique("y", "y", 0, 255, 0)); + layout.add(std::make_unique("z", "z", 0, 255, 0)); + + layout.add(std::make_unique("oversampling_factor", "oversampling_factor", 1, 16, 2)); + + return layout; +}; \ No newline at end of file diff --git a/src/PluginProcessor.h b/src/PluginProcessor.h new file mode 100644 index 0000000..c87b950 --- /dev/null +++ b/src/PluginProcessor.h @@ -0,0 +1,256 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include "FormulaParser.h" + + + +//============================================================================== +// 管理公式 +class FormulaManager { +private: + fparse::FormulaParser* parser; // parser + std::string formula; // formula + bool parsed; // 当前 formula 是否已被 parse 过 + std::shared_ptr cur_expr; // 当前使用的 parse 结果 + std::shared_ptr new_expr; // 即将更新的 parse 结果 + std::mutex new_expr_mutex; // new_expr 的锁 + + inline void update_expr() { + if (new_expr == nullptr) + return; + new_expr_mutex.lock(); // 锁 + cur_expr = new_expr; // 更新 cur_expr + new_expr = nullptr; // 更新 new_expr + new_expr_mutex.unlock(); // 解锁 + } + +public: + FormulaManager(fparse::FormulaParser& formula_parser) { // 构造函数 + parser = &formula_parser; + formula = ""; + parsed = false; + cur_expr = nullptr; + new_expr = nullptr; + }; + + inline std::string getFormula() { // 获取当前 formula + return formula; + }; + + inline void setFormula(std::string& formula_string) { // 设置当前 formula + formula = formula_string; + parsed = false; // 当前 formula 未被 parse + }; + + inline fparse::ParseResult parse() { // parse 当前 formula + fparse::ParseResult result = parser->parse(formula); + if (result.success) { // 如果执行成功,则当前 formula 已被 parse,储存 parse 的结果 + parsed = true; + new_expr_mutex.lock(); // 锁 + new_expr = result.expr; // 更新 new_expr + new_expr_mutex.unlock(); // 解锁 + } + return result; + }; + + inline bool isFormulaParsed() { // 返回当前 formula 是否已被 parse 过 + return parsed; + }; + + inline bool hasExpr() { // 返回当前 expr 对象是否为空指针 + return cur_expr != nullptr || new_expr != nullptr; + } + + inline fparse::EvaluationResult evaluate(const std::unordered_map& vars, size_t block_size) { + update_expr(); + return cur_expr->evaluate(vars, block_size); + } +}; + +//============================================================================== +// Sound 类 +class _8BitSynthSound : public juce::SynthesiserSound { +public: + _8BitSynthSound() {} + + bool appliesToNote(int) override { return true; } + bool appliesToChannel(int) override { return true; } +}; + + +//============================================================================== +// Voice 类 +class _8BitSynthVoice : public juce::SynthesiserVoice { +public: + _8BitSynthVoice(FormulaManager& m, juce::AudioProcessorValueTreeState& s, double& b) + : manager(m), apvts(s), bpm(b){ + vars["T"] = fparse::EvaluationResult(0); + vars["t"] = fparse::EvaluationResult(0); + vars["w"] = fparse::EvaluationResult(0); + vars["x"] = fparse::EvaluationResult(0); + vars["y"] = fparse::EvaluationResult(0); + vars["z"] = fparse::EvaluationResult(0); + }; + + + + bool canPlaySound(juce::SynthesiserSound* sound) override + { + return dynamic_cast<_8BitSynthSound*> (sound) != nullptr; + } + + void startNote(int midiNoteNumber, float velocity, + juce::SynthesiserSound*, int /*currentPitchWheelPosition*/) override { + frequency = juce::MidiMessage::getMidiNoteInHertz(midiNoteNumber); + time = 0.; + standard_time = 0.; + } + + void stopNote(float /*velocity*/, bool allowTailOff) override + { + //if (allowTailOff) + //{ + // // 音符结束的逻辑 + //} + //else + //{ + // + //} + clearCurrentNote(); + frequency = 0.; + time = 0.; + standard_time = 0.; + } + + void pitchWheelMoved(int) override {}; + void controllerMoved(int, int) override {}; + + // 修改 w x y z 的值 + inline void setMacro(const std::string macro_name, int value) { + vars[macro_name] = { value }; + }; + + void renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override { + if (!manager.hasExpr()) // Expr 对象为空指针 + return; + if (frequency == 0.) // 音符未按下 + return; + + double sample_rate = getSampleRate(); + + // 设置 t 参数 + double end_time = time + (numSamples - 1) * 256.0 * frequency / sample_rate; + double next_time = end_time + 256.0 * frequency / sample_rate; + vars["t"] = xt::cast(xt::linspace(time, end_time, numSamples)); + + double block_bpm = bpm; + if (block_bpm == -1.) { + block_bpm = 150.; // 默认bpm + } + + // 设置 T 参数 + double end_standard_time = standard_time + (numSamples - 1) * 256.0 * block_bpm / (sample_rate * 60.); + double next_standard_time = end_standard_time + 256.0 * block_bpm / (sample_rate * 60.); + vars["T"] = xt::cast(xt::linspace(standard_time, end_standard_time, numSamples)); + + // 设置 w x y z 参数 + vars["w"][0] = apvts.getRawParameterValue("w")->load(); + vars["x"][0] = apvts.getRawParameterValue("x")->load(); + vars["y"][0] = apvts.getRawParameterValue("y")->load(); + vars["z"][0] = apvts.getRawParameterValue("z")->load(); + + // 计算输出 + xt::xarray result = xt::cast(manager.evaluate(vars, numSamples) & 255) / 510.0f - 0.25f; + + // 将输出写入 buffer + for (size_t i = 0; i < numSamples; i++) { + for (auto channel = outputBuffer.getNumChannels(); --channel >= 0;) + outputBuffer.addSample(channel, startSample + i, result[i]); + } + + // 更新时间 + time = next_time; + standard_time = next_standard_time; + + } + +private: + double frequency = 0.; + double time = 0.; + double standard_time = 0.; + double& bpm; + + FormulaManager& manager; + std::unordered_map vars; + juce::AudioProcessorValueTreeState& apvts; +}; + + +//============================================================================== +// Audio Processor 类 +class _8BitSynthAudioProcessor : public juce::AudioProcessor +{ +public: + //============================================================================== + _8BitSynthAudioProcessor(); + ~_8BitSynthAudioProcessor() override; + + //============================================================================== + void prepareToPlay (double sampleRate, int samplesPerBlock) override; + void releaseResources() override; + + #ifndef JucePlugin_PreferredChannelConfigurations + bool isBusesLayoutSupported (const BusesLayout& layouts) const override; + #endif + + void processBlock (juce::AudioBuffer&, juce::MidiBuffer&) override; + + //============================================================================== + juce::AudioProcessorEditor* createEditor() override; + bool hasEditor() const override; + + //============================================================================== + const juce::String getName() const override; + + bool acceptsMidi() const override; + bool producesMidi() const override; + bool isMidiEffect() const override; + double getTailLengthSeconds() const override; + + //============================================================================== + int getNumPrograms() override; + int getCurrentProgram() override; + void setCurrentProgram (int index) override; + const juce::String getProgramName (int index) override; + void changeProgramName (int index, const juce::String& newName) override; + + //============================================================================== + void getStateInformation (juce::MemoryBlock& destData) override; + void setStateInformation (const void* data, int sizeInBytes) override; + + + static juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout(); + juce::AudioProcessorValueTreeState apvts{ *this, nullptr, "Parameters", createParameterLayout() }; + + //============================================================================== + FormulaManager formula_manager; // 公式管理器 + +private: + //============================================================================== + fparse::FormulaParser parser; // parser + juce::Synthesiser synth; // synth + double bpm = 0.; // bpm + + std::unique_ptr> oversampler; + + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (_8BitSynthAudioProcessor) +}; +