This commit is contained in:
Nanako 2024-11-13 21:48:35 +08:00
commit 7a7d83dde7
9 changed files with 1491 additions and 0 deletions

23
CMakeLists.txt Normal file
View File

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

View File

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

517
src/FormulaParser.cpp Normal file
View File

@ -0,0 +1,517 @@
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <assert.h>
#include <cstdint>
#include <peglib.h>
#include <xtensor/xarray.hpp>
#include <xtensor/xio.hpp>
#include <xtensor/xindex_view.hpp>
#include <xtensor/xrandom.hpp>
#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<string, EvaluationResult> 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<string, FunctionWithBound> FormulaParser::function_dictionary = {
{
"sin",
{ [](const vector<shared_ptr<Expression>>& args, const unordered_map<string, EvaluationResult>& 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<shared_ptr<Expression>>& args, const unordered_map<string, EvaluationResult>& 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<shared_ptr<Expression>>& args, const unordered_map<string, EvaluationResult>& 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<shared_ptr<Expression>>& args, const unordered_map<string, EvaluationResult>& vars, size_t block_size) -> EvaluationResult {
return xt::random::randint({block_size}, 0, 255);
}, 0 , 0}
},
{
"abs",
{ [](const vector<shared_ptr<Expression>>& args, const unordered_map<string, EvaluationResult>& vars, size_t block_size) -> EvaluationResult {
return xt::abs(args[0]->evaluate(vars, block_size));
}, 1 , 1}
},
{
"srand",
{ [](const vector<shared_ptr<Expression>>& args, const unordered_map<string, EvaluationResult>& 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<string, EvaluationResult>& 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<string, EvaluationResult>&, size_t block_size) const { // evaluation
return xt::broadcast(value, { block_size });
}
// 二元表达式类
CompoundExpression::CompoundExpression(Operation op, shared_ptr<Expression> lhs, shared_ptr<Expression> 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<string, EvaluationResult>& 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<shared_ptr<Expression>> function_args)
:name(function_name), function(FormulaParser::function_dictionary.at(function_name)), args(function_args) {
}
EvaluationResult FunctionExpression::evaluate(const unordered_map<string, EvaluationResult>& vars, size_t block_size) const {
return function.function(args, vars, block_size);
}
// +
shared_ptr<Expression> operator+(shared_ptr<Expression> lhs, shared_ptr<Expression> rhs) {
// Constant simplify
if (lhs->isConstant() && rhs->isConstant()) {
auto l = dynamic_pointer_cast<Constant>(lhs);
auto r = dynamic_pointer_cast<Constant>(rhs);
return make_shared<Constant>(l->value + r->value);
} // preprocess 0
else if (lhs->isConstant() && dynamic_pointer_cast<Constant>(lhs)->value == 0)
return rhs;
else if (rhs->isConstant() && dynamic_pointer_cast<Constant>(rhs)->value == 0)
return lhs;
// build new expression object
return make_shared<CompoundExpression>(Operation::ADD, lhs, rhs);
}
// -
shared_ptr<Expression> operator-(shared_ptr<Expression> lhs, shared_ptr<Expression> rhs) {
if (lhs->isConstant() && rhs->isConstant()) {
auto l = dynamic_pointer_cast<Constant>(lhs);
auto r = dynamic_pointer_cast<Constant>(rhs);
return make_shared<Constant>(l->value - r->value);
}
else if (rhs->isConstant() && dynamic_pointer_cast<Constant>(rhs)->value == 0)
return lhs;
return make_shared<CompoundExpression>(Operation::SUBTRACT, lhs, rhs);
}
// *
shared_ptr<Expression> operator*(shared_ptr<Expression> lhs, shared_ptr<Expression> rhs) {
if (lhs->isConstant() && rhs->isConstant()) {
auto l = dynamic_pointer_cast<Constant>(lhs);
auto r = dynamic_pointer_cast<Constant>(rhs);
return make_shared<Constant>(l->value * r->value);
}
else if (lhs->isConstant() && dynamic_pointer_cast<Constant>(lhs)->value == 0)
return make_shared<Constant>(0);
else if (rhs->isConstant() && dynamic_pointer_cast<Constant>(rhs)->value == 0)
return make_shared<Constant>(0);
return make_shared<CompoundExpression>(Operation::MULTIPLY, lhs, rhs);
}
// /
shared_ptr<Expression> operator/(shared_ptr<Expression> lhs, shared_ptr<Expression> rhs) {
if (lhs->isConstant() && rhs->isConstant()) {
auto l = dynamic_pointer_cast<Constant>(lhs);
auto r = dynamic_pointer_cast<Constant>(rhs);
if (r->value == 0)
return make_shared<Constant>(0);
return make_shared<Constant>(l->value / r->value);
}
else if (lhs->isConstant() && dynamic_pointer_cast<Constant>(lhs)->value == 0)
return make_shared<Constant>(0);
else if (rhs->isConstant() && dynamic_pointer_cast<Constant>(rhs)->value == 0)
return make_shared<Constant>(0);
return make_shared<CompoundExpression>(Operation::DIVIDE, lhs, rhs);
}
// %
shared_ptr<Expression> operator%(shared_ptr<Expression> lhs, shared_ptr<Expression> rhs) {
if (lhs->isConstant() && rhs->isConstant()) {
auto l = dynamic_pointer_cast<Constant>(lhs);
auto r = dynamic_pointer_cast<Constant>(rhs);
if (r->value == 0)
return make_shared<Constant>(0);
return make_shared<Constant>(l->value % r->value);
}
else if (lhs->isConstant() && dynamic_pointer_cast<Constant>(lhs)->value == 0)
return make_shared<Constant>(0);
else if (rhs->isConstant() && dynamic_pointer_cast<Constant>(rhs)->value == 0)
return make_shared<Constant>(0);
return make_shared<CompoundExpression>(Operation::MOD, lhs, rhs);
}
// &
shared_ptr<Expression> operator&(shared_ptr<Expression> lhs, shared_ptr<Expression> rhs) {
if (lhs->isConstant() && rhs->isConstant()) {
auto l = dynamic_pointer_cast<Constant>(lhs);
auto r = dynamic_pointer_cast<Constant>(rhs);
return make_shared<Constant>(l->value & r->value);
}
else if (lhs->isConstant() && dynamic_pointer_cast<Constant>(lhs)->value == 0)
return make_shared<Constant>(0);
else if (rhs->isConstant() && dynamic_pointer_cast<Constant>(rhs)->value == 0)
return make_shared<Constant>(0);
return make_shared<CompoundExpression>(Operation::AND, lhs, rhs);
}
// |
shared_ptr<Expression> operator|(shared_ptr<Expression> lhs, shared_ptr<Expression> rhs) {
if (lhs->isConstant() && rhs->isConstant()) {
auto l = dynamic_pointer_cast<Constant>(lhs);
auto r = dynamic_pointer_cast<Constant>(rhs);
return make_shared<Constant>(l->value | r->value);
}
else if (lhs->isConstant() && dynamic_pointer_cast<Constant>(lhs)->value == 0)
return rhs;
else if (rhs->isConstant() && dynamic_pointer_cast<Constant>(rhs)->value == 0)
return lhs;
return make_shared<CompoundExpression>(Operation::OR, lhs, rhs);
}
// ^
shared_ptr<Expression> operator^(shared_ptr<Expression> lhs, shared_ptr<Expression> rhs) {
if (lhs->isConstant() && rhs->isConstant()) {
auto l = dynamic_pointer_cast<Constant>(lhs);
auto r = dynamic_pointer_cast<Constant>(rhs);
return make_shared<Constant>(l->value ^ r->value);
}
else if (lhs->isConstant() && dynamic_pointer_cast<Constant>(lhs)->value == 0)
return rhs;
else if (rhs->isConstant() && dynamic_pointer_cast<Constant>(rhs)->value == 0)
return lhs;
return make_shared<CompoundExpression>(Operation::XOR, lhs, rhs);
}
// <<
shared_ptr<Expression> operator<<(shared_ptr<Expression> lhs, shared_ptr<Expression> rhs) {
if (lhs->isConstant() && rhs->isConstant()) {
auto l = dynamic_pointer_cast<Constant>(lhs);
auto r = dynamic_pointer_cast<Constant>(rhs);
return make_shared<Constant>(l->value << (r->value & 15));
}
else if (lhs->isConstant() && dynamic_pointer_cast<Constant>(lhs)->value == 0)
return make_shared<Constant>(0);
else if (rhs->isConstant() && dynamic_pointer_cast<Constant>(rhs)->value % 16 == 0)
return lhs;
return make_shared<CompoundExpression>(Operation::SHIFT_LEFT, lhs, rhs);
}
// >>
shared_ptr<Expression> operator>>(shared_ptr<Expression> lhs, shared_ptr<Expression> rhs) {
if (lhs->isConstant() && rhs->isConstant()) {
auto l = dynamic_pointer_cast<Constant>(lhs);
auto r = dynamic_pointer_cast<Constant>(rhs);
return make_shared<Constant>(l->value >> (r->value & 15));
}
else if (lhs->isConstant() && dynamic_pointer_cast<Constant>(lhs)->value == 0)
return make_shared<Constant>(0);
else if (rhs->isConstant() && dynamic_pointer_cast<Constant>(rhs)->value % 16 == 0)
return lhs;
return make_shared<CompoundExpression>(Operation::SHIFT_RIGHT, lhs, rhs);
}
shared_ptr<Expression> castToExpression(const any& value) {
if (value.type() == typeid(shared_ptr<Constant>))
return any_cast<shared_ptr<Constant>>(value);
if (value.type() == typeid(shared_ptr<Variable>))
return any_cast<shared_ptr<Variable>>(value);
if (value.type() == typeid(shared_ptr<CompoundExpression>))
return any_cast<shared_ptr<CompoundExpression>>(value);
if (value.type() == typeid(shared_ptr<FunctionExpression>))
return any_cast<shared_ptr<FunctionExpression>>(value);
if (value.type() == typeid(shared_ptr<Expression>))
return any_cast<shared_ptr<Expression>>(value);
cout << value.type().name() << ";" << endl;
throw bad_any_cast();
}
// 解析器类
FormulaParser::FormulaParser() : parser(grammar) {
assert(static_cast<bool>(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<char>(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<Expression> {
auto name = any_cast<string>(vs[0]);
vector<shared_ptr<Expression>> args;
if (vs.size() > 1) {
bool constant_flag = true;
for (size_t i = 1; i < vs.size(); i++) {
shared_ptr<Expression> expr = castToExpression(vs[i]);
// 添加参数
args.push_back(expr);
// 检查函数的所有参数是否均为常数
if (!expr->isConstant()) constant_flag = false;
}
// 如果所有参数均为常数
if (constant_flag) {
std::unordered_map<std::string, EvaluationResult> empty_map; // 空的 unordered_map, 不占用实际空间
FunctionType function = FormulaParser::function_dictionary.at(name).function;
return make_shared<Constant>(function(args, empty_map, 1)[0]);
}
}
return make_shared<FunctionExpression>(name, args);
};
parser["FUNCCALL"].predicate = [](const SemanticValues& vs, const any&, string& msg) {
auto name = any_cast<string>(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<Constant>(vs.token_to_number<int32_t>());
};
// VAR token
parser["VAR"] = [](const SemanticValues& vs) {
return make_shared<Variable>(vs.token_to_string());
};
parser["VAR"].predicate = [](const SemanticValues& vs, const any&, string& msg) {
// 检查是否存在该名字的函数
auto name = any_cast<string>(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<string>(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<Expression> 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;
};

123
src/FormulaParser.h Normal file
View File

@ -0,0 +1,123 @@
#ifndef PARSER_H
#define PARSER_H
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>
#include <peglib.h>
#include <xtensor/xarray.hpp>
namespace fparse {
// ¶¨Òå²Ù×÷·ûµÄö¾Ù
enum class Operation {
NONE, // ´ú±íûÓвÙ×÷
ADD,
SUBTRACT,
MULTIPLY,
DIVIDE,
MOD,
AND,
OR,
XOR,
SHIFT_LEFT,
SHIFT_RIGHT
};
using EvaluationResult = xt::xarray<int32_t>;
// ±í´ïʽ»ùÀà
class Expression {
public:
virtual ~Expression() = default;
virtual bool isConstant() const = 0; // constant simplify
virtual EvaluationResult evaluate(const std::unordered_map<std::string, EvaluationResult>& vars, size_t block_size) const = 0; // evaluation
};
// ÄäÃûº¯ÊýµÄÀàÐÍ
using FunctionType = std::function<EvaluationResult(const std::vector<std::shared_ptr<Expression>>&, const std::unordered_map<std::string, EvaluationResult>&, 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<std::string, EvaluationResult>& 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<std::string, EvaluationResult>&, size_t block_size) const override; // evaluation
};
// ¶þÔª±í´ïʽÀà
class CompoundExpression : public Expression {
public:
std::shared_ptr<Expression> l, r; // operands
Operation operation;
CompoundExpression(Operation op, std::shared_ptr<Expression> lhs, std::shared_ptr<Expression> rhs);
~CompoundExpression() override {}
bool isConstant() const override { return false; } // constant simplify
EvaluationResult evaluate(const std::unordered_map<std::string, EvaluationResult>& vars, size_t block_size) const override; // evaluation
};
// º¯Êý±í´ïʽÀà
class FunctionExpression : public Expression {
public:
std::string name; // function name
FunctionWithBound function; // function
std::vector<std::shared_ptr<Expression>> args; // function arguments
FunctionExpression(std::string function_name, std::vector<std::shared_ptr<Expression>> function_args);
~FunctionExpression() override {}
bool isConstant() const override { return false; } // constant simplify
EvaluationResult evaluate(const std::unordered_map<std::string, EvaluationResult>& vars, size_t block_size) const override; // evaluation
};
// ½âÎö½á¹û
struct ParseResult {
bool success;
std::shared_ptr<Expression> 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<std::string, EvaluationResult> temp_vars;
static std::unordered_map<std::string, FunctionWithBound> function_dictionary;
FormulaParser();
ParseResult parse(std::string& input);
private:
peg::parser parser;
};
};
#endif

51
src/JuceHeader.h Normal file
View File

@ -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 <juce_audio_basics/juce_audio_basics.h>
#include <juce_audio_devices/juce_audio_devices.h>
#include <juce_audio_formats/juce_audio_formats.h>
#include <juce_audio_plugin_client/juce_audio_plugin_client.h>
#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_audio_utils/juce_audio_utils.h>
#include <juce_core/juce_core.h>
#include <juce_data_structures/juce_data_structures.h>
#include <juce_dsp/juce_dsp.h>
#include <juce_events/juce_events.h>
#include <juce_graphics/juce_graphics.h>
#include <juce_gui_basics/juce_gui_basics.h>
#include <juce_gui_extra/juce_gui_extra.h>
#include <juce_midi_ci/juce_midi_ci.h>
#include <juce_osc/juce_osc.h>
#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

164
src/PluginEditor.cpp Normal file
View File

@ -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<int> CenterBox(juce::Rectangle<int>& 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<RotarySlider*> _8BitSynthAudioProcessorEditor::getRotarySliders() {
return {
&w_slider,
&x_slider,
&y_slider,
&z_slider
};
}

77
src/PluginEditor.h Normal file
View File

@ -0,0 +1,77 @@
/*
==============================================================================
This file contains the basic framework code for a JUCE plugin editor.
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
#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<RotarySlider*> getRotarySliders(); // 便于获取 4 个旋钮的方法
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (_8BitSynthAudioProcessorEditor)
};

219
src/PluginProcessor.cpp Normal file
View File

@ -0,0 +1,219 @@
#include "PluginProcessor.h"
#include "PluginEditor.h"
#include <cmath>
//==============================================================================
_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<float>::filterHalfBandPolyphaseIIR;
oversampler = std::make_unique<juce::dsp::Oversampling<float>>(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<float>& 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<float> block(buffer);
juce::dsp::AudioBlock<float> osBlock = oversampler->processSamplesUp(block);
float* p[] = {osBlock.getChannelPointer(0), osBlock.getChannelPointer(1)};
juce::AudioBuffer<float> osBuffer(p, 2, static_cast<int> (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<juce::AudioParameterInt>("w", "w", 0, 255, 0));
layout.add(std::make_unique<juce::AudioParameterInt>("x", "x", 0, 255, 0));
layout.add(std::make_unique<juce::AudioParameterInt>("y", "y", 0, 255, 0));
layout.add(std::make_unique<juce::AudioParameterInt>("z", "z", 0, 255, 0));
layout.add(std::make_unique<juce::AudioParameterInt>("oversampling_factor", "oversampling_factor", 1, 16, 2));
return layout;
};

256
src/PluginProcessor.h Normal file
View File

@ -0,0 +1,256 @@
#pragma once
#include <cstdint>
#include <mutex>
#include <xtensor/xarray.hpp>
#include <xtensor/xview.hpp>
#include <JuceHeader.h>
#include "FormulaParser.h"
//==============================================================================
// 管理公式
class FormulaManager {
private:
fparse::FormulaParser* parser; // parser
std::string formula; // formula
bool parsed; // 当前 formula 是否已被 parse 过
std::shared_ptr<fparse::Expression> cur_expr; // 当前使用的 parse 结果
std::shared_ptr<fparse::Expression> 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<std::string, fparse::EvaluationResult>& 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<int32_t>(xt::linspace<double>(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<int32_t>(xt::linspace<double>(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<float> result = xt::cast<float>(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<std::string, fparse::EvaluationResult> 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<float>&, 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<juce::dsp::Oversampling<float>> oversampler;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (_8BitSynthAudioProcessor)
};