From d8ac788f8553c0557bf913bcbb202aa2fea53b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Fri, 24 Jan 2025 08:45:56 -0500 Subject: [PATCH] pico: add templates --- Pico/ApplicationPlugin.cpp | 10 + Pico/ApplicationPlugin.hpp | 1 + Pico/BasicSourcePrinter.cpp | 235 +++++++++++--- Pico/BasicSourcePrinter.hpp | 20 +- Pico/DeviceGraph.cpp | 21 ++ Pico/DeviceGraph.hpp | 6 + Pico/ExportDialog.cpp | 79 +++-- Pico/ExportDialog.hpp | 4 +- Pico/Pruner.cpp | 28 +- Pico/SourcePrinter.cpp | 17 - templates/common/ExprtkMapper.hpp | 143 ++++++++ templates/common/PolySynth.hpp | 322 +++++++++++++++++++ templates/common/ScoreHelpers.hpp | 114 +++++++ templates/common/ossia_embedded_api.hpp | 13 + templates/desktop-standalone/CMakeLists.txt | 1 + templates/desktop/Untitled-1.cpp | 82 +++++ templates/desktop/build.sh | 28 ++ templates/desktop/exprtk.cpp | 20 ++ templates/desktop/picodevice..hpp | 184 +++++++++++ templates/desktop/picotasks.hpp | 0 templates/desktop/scenario-struct.hpp | 97 ++++++ templates/desktop/test.score | 1 + templates/desktop/x.so | Bin 0 -> 120872 bytes templates/ossia-device/constants.hpp | 7 + templates/ossia-device/impl-manual.hpp | 74 +++++ templates/ossia-device/impl-ossia.hpp | 71 ++++ templates/ossia-device/ossia-device-test.ino | 46 +++ templates/ossia-device/utility.hpp | 35 ++ templates/teensy-minimal/.gitignore | 5 + templates/teensy-minimal/include/README | 39 +++ templates/teensy-minimal/lib/README | 46 +++ templates/teensy-minimal/platformio.ini | 46 +++ templates/teensy-minimal/src/main.cpp | 24 ++ templates/teensy-minimal/test/README | 11 + 34 files changed, 1732 insertions(+), 98 deletions(-) create mode 100644 templates/common/ExprtkMapper.hpp create mode 100644 templates/common/PolySynth.hpp create mode 100644 templates/common/ScoreHelpers.hpp create mode 100644 templates/common/ossia_embedded_api.hpp create mode 100644 templates/desktop-standalone/CMakeLists.txt create mode 100644 templates/desktop/Untitled-1.cpp create mode 100755 templates/desktop/build.sh create mode 100644 templates/desktop/exprtk.cpp create mode 100644 templates/desktop/picodevice..hpp create mode 100644 templates/desktop/picotasks.hpp create mode 100644 templates/desktop/scenario-struct.hpp create mode 100644 templates/desktop/test.score create mode 100755 templates/desktop/x.so create mode 100644 templates/ossia-device/constants.hpp create mode 100644 templates/ossia-device/impl-manual.hpp create mode 100644 templates/ossia-device/impl-ossia.hpp create mode 100644 templates/ossia-device/ossia-device-test.ino create mode 100644 templates/ossia-device/utility.hpp create mode 100644 templates/teensy-minimal/.gitignore create mode 100644 templates/teensy-minimal/include/README create mode 100644 templates/teensy-minimal/lib/README create mode 100644 templates/teensy-minimal/platformio.ini create mode 100644 templates/teensy-minimal/src/main.cpp create mode 100644 templates/teensy-minimal/test/README diff --git a/Pico/ApplicationPlugin.cpp b/Pico/ApplicationPlugin.cpp index bce1868..d1428a1 100644 --- a/Pico/ApplicationPlugin.cpp +++ b/Pico/ApplicationPlugin.cpp @@ -56,6 +56,16 @@ class DocPlug : public score::DocumentPlugin void play() { } }; */ +void AppPlug::on_loadedDocument(score::Document& doc) +{ + return; + //score::addDocumentPlugin(doc); + export_scenario(doc.context(), "/tmp/test"); + QTimer::singleShot(100, qApp, [] { + qApp->exit(); + std::terminate(); + }); +} void AppPlug::on_createdDocument(score::Document& doc) { diff --git a/Pico/ApplicationPlugin.hpp b/Pico/ApplicationPlugin.hpp index 8b41570..23e8582 100644 --- a/Pico/ApplicationPlugin.hpp +++ b/Pico/ApplicationPlugin.hpp @@ -10,6 +10,7 @@ class AppPlug : public QObject, public score::GUIApplicationPlugin using GUIApplicationPlugin::GUIApplicationPlugin; void on_createdDocument(score::Document& doc) override; + void on_loadedDocument(score::Document& doc) override; score::GUIElements makeGUIElements() override; diff --git a/Pico/BasicSourcePrinter.cpp b/Pico/BasicSourcePrinter.cpp index eeb4e01..fa7102e 100644 --- a/Pico/BasicSourcePrinter.cpp +++ b/Pico/BasicSourcePrinter.cpp @@ -5,44 +5,71 @@ #include #include +#include +#define OSSIA_PROTOCOL_SIMPLEIO 1 +#include +#include +#include + #include #include +#include + #include #include -#include - namespace Pico { static const std::string error_string = ":*:ERROR:*:"; static const std::string todo_string = ":*:TODO:*:"; -static std::string -address_to_device_read_index(const State::Address& addr, std::string var) +static std::string address_to_model_accessor(const State::Address& addr) { + QString device = addr.device; + ossia::net::sanitize_name(device); + + QString variable_name = addr.path.join("_"); + ossia::net::sanitize_name(variable_name); + return fmt::format( - "do_device_read<\"{}\">({})", addr.toString().toStdString(), var); + "ossia_model.{}.{}", device.toStdString(), variable_name.toStdString()); +} +static std::optional +address_to_device_read_index(const State::Address& addr, std::string var) +{ + if(addr.isSet()) + return fmt::format( + "value_adapt({}.value, {})", address_to_model_accessor(addr), var); + return std::nullopt; } -static std::string +static std::optional address_to_network_read_index(const State::Address& addr, std::string var) { - return fmt::format( - "do_network_read<\"{}\">({})", addr.toString().toStdString(), var); + if(addr.isSet()) + return fmt::format( + "value_adapt({}.value, {})", address_to_model_accessor(addr), var); + return std::nullopt; } -static std::string +static std::optional address_to_device_write_index(const State::Address& addr, std::string var) { - return fmt::format( - "do_device_write<\"{}\">({})", addr.toString().toStdString(), var); + if(addr.isSet()) + return fmt::format( + "value_adapt({0}, {1}.value); {1}.changed = true;", var, + address_to_model_accessor(addr)); + return std::nullopt; } -static std::string +static std::optional address_to_network_write_index(const State::Address& addr, std::string var) { - return fmt::format( - "do_network_write<\"{}\">({})", addr.toString().toStdString(), var); + if(addr.isSet()) + return fmt::format( + "value_adapt({0}, {1}.value); {1}.changed = true;", var, + address_to_model_accessor(addr)); + return std::nullopt; } QString BasicSourcePrinter::printTask( @@ -111,17 +138,15 @@ QString BasicSourcePrinter::printTask( { if (inl->address().address.device == device.name) { - c += fmt::format( - "{{ {}; }}\n", - address_to_device_read_index( - inl->address().address, wr->accessInlet(inl->id()))); + if(auto res = address_to_device_read_index( + inl->address().address, wr->accessInlet(inl->id()))) + c += fmt::format("{{ {}; }}\n", *res); } else if (!inl->address().address.device.isEmpty()) { - c += fmt::format( - "{{ {}; }}\n", - address_to_network_read_index( - inl->address().address, wr->accessInlet(inl->id()))); + if(auto res = address_to_network_read_index( + inl->address().address, wr->accessInlet(inl->id()))) + c += fmt::format("{{ {}; }}\n", *res); } } @@ -180,17 +205,15 @@ QString BasicSourcePrinter::printTask( { if (inl->address().address.device == device.name) { - c += fmt::format( - "{{ {}; }}\n", - address_to_device_write_index( - inl->address().address, wr->accessOutlet(inl->id()))); + if(auto res = address_to_device_write_index( + inl->address().address, wr->accessOutlet(inl->id()))) + c += fmt::format("{{ {}; }}\n", *res); } else if (!inl->address().address.device.isEmpty()) { - c += fmt::format( - "{{ {}; }}\n", - address_to_network_write_index( - inl->address().address, wr->accessOutlet(inl->id()))); + if(auto res = address_to_network_write_index( + inl->address().address, wr->accessOutlet(inl->id()))) + c += fmt::format("{{ {}; }}\n", *res); } } @@ -207,20 +230,21 @@ QString BasicSourcePrinter::printTask( } QString BasicSourcePrinter::print( - const Device& device, - const score::DocumentContext& context, - const GraphTasks& components) + const Device& device, const score::DocumentContext& context, const Graph& g) { // 4. Print source code std::string source; source += Pico::defaultIncludesGraph(); + source += fmt::format( + R"(#include "{}.ossia-model.generated.hpp")", device.name.toStdString()); + source += printDeviceInitialization(device).toStdString(); std::string task_creation; int task_n = 0; - qDebug() << " <<<<>>>> there are " << components.size() << " components "; + qDebug() << " <<<<>>>> there are " << g.tasks.size() << " components "; ; - for (auto& comp : components) + for(auto& comp : g.tasks) { source += "\n"; source += fmt::format("static void ossia_task_{}() {{\n", task_n); @@ -245,10 +269,149 @@ extern "C" void ossia_run_graph(void) return QString::fromStdString(source); } + +static constexpr auto data_model_template = R"_( +struct ossia_model_t +{{ + {} +}} ossia_model; +// struct +// { +// int foo; +// float bar; +// } net; +// struct +// { +// int foo; +// float bar; +// } dev; +// struct +// { +// uint32_t bar : 1; +// } net_bitset; +// struct +// { +// uint32_t bar : 1; +// } dev_bitset; + + +void do_read(auto& dest, auto& value) +{{ + value_adapt(value, ossia_model.net.foo); + if constexpr(0) {{ }} + {} + + // else if constexpr(std::string_view{str} == "osc:/foo") + // value_adapt(value, ossia_model.net.foo); +} + +template +void do_device_read(auto& value) +{ + if constexpr(0) {{ }} + {} + +} +template +void do_network_write(const auto& value) +{ + if constexpr(0) {{ }} + {} + +} +template +void do_device_write(auto& value) +{ + if constexpr(0) {{ }} + {} + + // else if constexpr(std::string_view{str} == "gpio:/bar") + // { + // value_adapt(ossia_model.dev.bar, value); + // ossia_model.dev_bitset.bar = 1; + // } +} +)_"; + QString BasicSourcePrinter::printDeviceInitialization(const Device& device) { QString str; - return str; } + +QString BasicSourcePrinter::printDeviceCommunication( + const Device& device, const score::DocumentContext& context, const Graph& components) +{ + auto& device_list = context.plugin().list(); + QString file; + // ossia_read_pins + { + std::string f; + + f += "void ossia_read_pins() {\n"; + // FIXME codewriter interface on devices + for(auto& [devname, paths] : components.in_addresses) + { + if(auto* d = device_list.findDevice(devname)) + { + if(auto proto = qobject_cast(d)) + { + } + } + } + for(auto& e : device.ios) + { + // If device + } + + f += "}\n"; + } + // ossia_read_net + // ossia_run_graph + + // ossia_write_pins + // ossia_write_net + + return file; +} + +QString BasicSourcePrinter::printDataModel(const Device& device, const Graph& g) +{ + QString str; + + // 2. Generate a model struct + QString model_struct; + for(const auto& [k, v] : g.merged_addresses) + { + QString structdef = "struct {\n"; + + for(const auto& vv : v) + { + std::string variable_name = vv.join("_").toStdString(); + ossia::net::sanitize_name(variable_name); + structdef += fmt::format( + "struct {{ {0} value = {{}}; bool changed = false; }} {1};\n", "float", + variable_name); + } + structdef += "} "; + + QString device_name = k; + ossia::net::sanitize_name(device_name); + structdef += k; + structdef += ";\n"; + + model_struct += structdef; + } + + // 3. Generate the accessor functions + + return QString::fromStdString(fmt::format( + R"_(#pragma once +struct ossia_model_t +{{ + {} +}} ossia_model; +)_", + model_struct.toStdString())); +} } diff --git a/Pico/BasicSourcePrinter.hpp b/Pico/BasicSourcePrinter.hpp index daebce2..cf5f765 100644 --- a/Pico/BasicSourcePrinter.hpp +++ b/Pico/BasicSourcePrinter.hpp @@ -14,14 +14,16 @@ struct Device; class BasicSourcePrinter { public: - QString print( - const Device& device, - const score::DocumentContext& context, - const GraphTasks& components); - QString printDeviceInitialization(const Device& device); - QString printTask( - const Device& device, - const score::DocumentContext& context, - const std::vector& procs); + QString print( + const Device& device, const score::DocumentContext& context, + const Graph& components); + QString printDeviceInitialization(const Device& device); + QString printDataModel(const Device& device, const Graph& components); + QString printDeviceCommunication( + const Device& device, const score::DocumentContext& context, + const Graph& components); + QString printTask( + const Device& device, const score::DocumentContext& context, + const std::vector& procs); }; } diff --git a/Pico/DeviceGraph.cpp b/Pico/DeviceGraph.cpp index cf9e957..993abf6 100644 --- a/Pico/DeviceGraph.cpp +++ b/Pico/DeviceGraph.cpp @@ -138,6 +138,27 @@ Graph processGraph( g.topo_order.push_back(process); } + + // 6. Store the used addresses + + for(auto& task : components) + { + for(auto& proc : task.processes) + { + for(auto inl : proc->inlets()) + if(const auto& addr = inl->address(); addr.isSet()) + { + g.in_addresses[addr.address.device].insert(addr.address.path); + g.merged_addresses[addr.address.device].insert(addr.address.path); + } + for(auto outl : proc->outlets()) + if(const auto& addr = outl->address(); addr.isSet()) + { + g.out_addresses[addr.address.device].insert(addr.address.path); + g.merged_addresses[addr.address.device].insert(addr.address.path); + } + } + } return g; } diff --git a/Pico/DeviceGraph.hpp b/Pico/DeviceGraph.hpp index bdbaacc..7ffc99c 100644 --- a/Pico/DeviceGraph.hpp +++ b/Pico/DeviceGraph.hpp @@ -2,6 +2,8 @@ #include +#include + #include namespace Process { @@ -22,6 +24,10 @@ struct Graph { GraphTasks tasks; std::vector topo_order; + + ossia::hash_map> in_addresses; + ossia::hash_map> out_addresses; + ossia::hash_map> merged_addresses; }; Graph processGraph( diff --git a/Pico/ExportDialog.cpp b/Pico/ExportDialog.cpp index 3355406..09664f1 100644 --- a/Pico/ExportDialog.cpp +++ b/Pico/ExportDialog.cpp @@ -51,6 +51,21 @@ static void copy_folder_recursively(const QString& src, const QString& dst) QDir{}.mkpath(abs_path); else if(fi.isFile() && !QFileInfo::exists(abs_path)) QFile::copy(fi.absoluteFilePath(), abs_path); + else if(fi.isFile() && QFileInfo::exists(abs_path)) + { + QFile f1{fi.filePath()}; + f1.open(QIODevice::ReadOnly); + QFile f2{abs_path}; + f2.open(QIODevice::ReadWrite); + auto f1_content = f1.readAll(); + auto f2_content = f2.readAll(); + if(f1_content != f2_content) + { + f2.seek(0); + f2.resize(0); + f2.write(f1_content); + } + } } } @@ -61,6 +76,7 @@ ExportDialog::ExportDialog(AppPlug& plug, QWidget* parent) auto lay = new QFormLayout{this}; m_mode = new QComboBox{this}; m_mode->addItems({tr("Remote-control"), tr("Full score")}); + m_mode->setCurrentIndex(1); lay->addRow(tr("Mode"), m_mode); m_template = new QLineEdit{this}; @@ -106,7 +122,7 @@ bool ExportDialog::copy_template_folder() return true; } -bool ExportDialog::export_device(const score::DocumentContext& ctx) +bool export_device(const score::DocumentContext& ctx, QString destination) { // For template ossia-device auto& devices = ctx.plugin(); @@ -132,8 +148,7 @@ bool ExportDialog::export_device(const score::DocumentContext& ctx) ret += wr.writePins(); { - QFile dst{ - m_destination->text() + QDir::separator() + "ossia-device.generated.cpp"}; + QFile dst{destination + QDir::separator() + "ossia-device.generated.cpp"}; dst.open(QIODevice::WriteOnly); dst.write(ret.data(), ret.size()); dst.flush(); @@ -146,9 +161,7 @@ bool ExportDialog::export_device(const score::DocumentContext& ctx) root_model_node.set(obj->settings()); auto ret = Pico::oscquery_generate_namespace(root_model_node); { - QFile dst{ - m_destination->text() + QDir::separator() - + "ossia-oscquery.generated.hpp"}; + QFile dst{destination + QDir::separator() + "ossia-oscquery.generated.hpp"}; dst.open(QIODevice::WriteOnly); dst.write(ret.toUtf8()); dst.flush(); @@ -161,7 +174,7 @@ bool ExportDialog::export_device(const score::DocumentContext& ctx) return false; } -bool ExportDialog::export_scenario(const score::DocumentContext& ctx) +bool export_scenario(const score::DocumentContext& ctx, QString destination) { // For template ossia-full-* Scenario::ScenarioDocumentModel& base @@ -171,15 +184,15 @@ bool ExportDialog::export_scenario(const score::DocumentContext& ctx) if(baseInterval.processes.size() == 0) return false; - QString root = m_destination->text() + QDir::separator(); + QString root = destination + QDir::separator(); // - ProcessScenario ps{ctx}; - { - QString scenario = ps.process(baseInterval); - QFile f(root + "src/ossia-scenario.generated.cpp"); - f.open(QIODevice::WriteOnly); - f.write(scenario.toUtf8()); - } + // ProcessScenario ps{ctx}; + // { + // QString scenario = ps.process(baseInterval); + // QFile f(root + "src/ossia-scenario.generated.cpp"); + // f.open(QIODevice::WriteOnly); + // f.write(scenario.toUtf8()); + // } // // IntervalSplit is{ctx}; // { @@ -190,16 +203,16 @@ bool ExportDialog::export_scenario(const score::DocumentContext& ctx) // f.write(task_text.toUtf8()); // } - // ComponentBasedSplit p{ctx}; - // { - // auto res = p.process(baseInterval); - // for(const auto& [devname, content] : res) - // { - // QFile f(root + "src/ossia-component." + devname + ".generated.cpp"); - // f.open(QIODevice::WriteOnly); - // f.write(content.toUtf8()); - // } - // } + ComponentBasedSplit p{ctx}; + { + auto res = p.process(baseInterval); + for(const auto& [filename, content] : res) + { + QFile f(root + "src/" + filename); + f.open(QIODevice::WriteOnly); + f.write(content.toUtf8()); + } + } return true; } @@ -219,9 +232,9 @@ bool ExportDialog::on_export() switch(static_cast(m_mode->currentIndex())) { case RemoteControl: - return export_device(doc->context()); + return export_device(doc->context(), m_destination->text()); case FullScore: - return export_scenario(doc->context()); + return export_scenario(doc->context(), m_destination->text()); } return false; } @@ -238,6 +251,17 @@ void ExportDialog::append_stderr(QString s) void ExportDialog::on_build() { + /* + TODOS + + - scheduler + - midi notes + - arpeggiator + - sound + - value_adapt for midi + - synth api + - synthimi: process -> create an interface for codewriter and overide +*/ if(!on_export()) return; @@ -271,5 +295,4 @@ void ExportDialog::on_build() delete p; } } - } diff --git a/Pico/ExportDialog.hpp b/Pico/ExportDialog.hpp index 53381ac..199ff8b 100644 --- a/Pico/ExportDialog.hpp +++ b/Pico/ExportDialog.hpp @@ -27,8 +27,6 @@ class ExportDialog : public QDialog private: bool copy_template_folder(); - bool export_device(const score::DocumentContext& ctx); - bool export_scenario(const score::DocumentContext& ctx); void append_stdout(QString); void append_stderr(QString); @@ -38,4 +36,6 @@ class ExportDialog : public QDialog QPlainTextEdit* m_textArea{}; QDialogButtonBox* m_buttons{}; }; +bool export_device(const score::DocumentContext& ctx, QString dest); +bool export_scenario(const score::DocumentContext& ctx, QString dest); } diff --git a/Pico/Pruner.cpp b/Pico/Pruner.cpp index b28a400..e27b173 100644 --- a/Pico/Pruner.cpp +++ b/Pico/Pruner.cpp @@ -62,11 +62,27 @@ ComponentBasedSplit::process(const Scenario::IntervalModel& root) device.second.ios.push_back(DeviceIO{ .type = DeviceIO::PWM, .direction = DeviceIO::Output, .pin = 21}); - auto [components, order] = processGraph(context, device.second.processes); + const auto& g = processGraph(context, device.second.processes); + BasicSourcePrinter p; - QString src = p.print(device.second, context, components); - QString filename = device.first; - res.emplace_back(filename, src); + // Print the data model + { + QString src = p.printDataModel(device.second, g); + QString filename = device.first + ".ossia-model.generated.hpp"; + res.emplace_back(filename, src); + } + // Print the device access + { + QString src = p.printDeviceCommunication(device.second, context, g); + QString filename = device.first + ".ossia-device.generated.hpp"; + res.emplace_back(filename, src); + } + // Print the main graph tasks file + { + QString src = p.print(device.second, context, g); + QString filename = device.first + ".ossia-graph.generated.hpp"; + res.emplace_back(filename, src); + } } return res; } @@ -133,8 +149,8 @@ QString IntervalSplit::process(const Scenario::IntervalModel& root) v.processes.push_back(&p); // Topo sort them - auto [components, order] = processGraph(context, v.processes); - v.processes = std::move(order); + auto g = processGraph(context, v.processes); + v.processes = std::move(g.topo_order); // Save the task to_process.push_back(std::move(v)); diff --git a/Pico/SourcePrinter.cpp b/Pico/SourcePrinter.cpp index 8b092aa..966dbe1 100644 --- a/Pico/SourcePrinter.cpp +++ b/Pico/SourcePrinter.cpp @@ -6,24 +6,7 @@ namespace Pico std::string defaultIncludesGraph() { return R"_( -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include - - )_"; } diff --git a/templates/common/ExprtkMapper.hpp b/templates/common/ExprtkMapper.hpp new file mode 100644 index 0000000..384b085 --- /dev/null +++ b/templates/common/ExprtkMapper.hpp @@ -0,0 +1,143 @@ +#pragma once +#include +#include +#include +#include +#include + +struct exprtk_arithmetic +{ + constexpr exprtk_arithmetic() = default; + constexpr exprtk_arithmetic(const exprtk_arithmetic&) = default; + constexpr exprtk_arithmetic(exprtk_arithmetic&&) = default; + constexpr exprtk_arithmetic& operator=(const exprtk_arithmetic&) = default; + constexpr exprtk_arithmetic& operator=(exprtk_arithmetic&&) = default; + constexpr exprtk_arithmetic(float val): v{val} { } + constexpr operator float&() noexcept { return v; } + constexpr operator const float&() const noexcept { return v; } + float v{}; + + bool operator==(const exprtk_arithmetic& rhs) const noexcept = default; + auto operator<=>(const exprtk_arithmetic& rhs) const noexcept = default; + friend exprtk_arithmetic operator+(exprtk_arithmetic lhs, exprtk_arithmetic rhs) noexcept { return lhs.v + rhs.v; } + friend exprtk_arithmetic operator-(exprtk_arithmetic lhs, exprtk_arithmetic rhs) noexcept { return lhs.v - rhs.v; } + friend exprtk_arithmetic operator*(exprtk_arithmetic lhs, exprtk_arithmetic rhs) noexcept { return lhs.v * rhs.v; } + friend exprtk_arithmetic operator/(exprtk_arithmetic lhs, exprtk_arithmetic rhs) noexcept { return lhs.v / rhs.v; } + friend exprtk_arithmetic operator%(exprtk_arithmetic lhs, exprtk_arithmetic rhs) noexcept { return std::fmod(lhs.v, rhs.v); } + + friend exprtk_arithmetic operator+(exprtk_arithmetic lhs, float rhs) noexcept { return lhs.v + rhs; } + friend exprtk_arithmetic operator-(exprtk_arithmetic lhs, float rhs) noexcept { return lhs.v - rhs; } + friend exprtk_arithmetic operator*(exprtk_arithmetic lhs, float rhs) noexcept { return lhs.v * rhs; } + friend exprtk_arithmetic operator/(exprtk_arithmetic lhs, float rhs) noexcept { return lhs.v / rhs; } + friend exprtk_arithmetic operator%(exprtk_arithmetic lhs, float rhs) noexcept { return std::fmod(lhs.v, rhs); } + friend exprtk_arithmetic operator+(float lhs, exprtk_arithmetic rhs) noexcept { return lhs + rhs.v; } + friend exprtk_arithmetic operator-(float lhs, exprtk_arithmetic rhs) noexcept { return lhs - rhs.v; } + friend exprtk_arithmetic operator*(float lhs, exprtk_arithmetic rhs) noexcept { return lhs * rhs.v; } + friend exprtk_arithmetic operator/(float lhs, exprtk_arithmetic rhs) noexcept { return lhs / rhs.v; } + friend exprtk_arithmetic operator%(float lhs, exprtk_arithmetic rhs) noexcept { return std::fmod(lhs, rhs.v); } + + friend exprtk_arithmetic operator+(exprtk_arithmetic lhs, double rhs) noexcept { return lhs.v + rhs; } + friend exprtk_arithmetic operator-(exprtk_arithmetic lhs, double rhs) noexcept { return lhs.v - rhs; } + friend exprtk_arithmetic operator*(exprtk_arithmetic lhs, double rhs) noexcept { return lhs.v * rhs; } + friend exprtk_arithmetic operator/(exprtk_arithmetic lhs, double rhs) noexcept { return lhs.v / rhs; } + friend exprtk_arithmetic operator%(exprtk_arithmetic lhs, double rhs) noexcept { return std::fmod(lhs.v, rhs); } + friend exprtk_arithmetic operator+(double lhs, exprtk_arithmetic rhs) noexcept { return lhs + rhs.v; } + friend exprtk_arithmetic operator-(double lhs, exprtk_arithmetic rhs) noexcept { return lhs - rhs.v; } + friend exprtk_arithmetic operator*(double lhs, exprtk_arithmetic rhs) noexcept { return lhs * rhs.v; } + friend exprtk_arithmetic operator/(double lhs, exprtk_arithmetic rhs) noexcept { return lhs / rhs.v; } + friend exprtk_arithmetic operator%(double lhs, exprtk_arithmetic rhs) noexcept { return std::fmod(lhs, rhs.v); } + + friend exprtk_arithmetic operator+(exprtk_arithmetic lhs, int rhs) noexcept { return lhs.v + rhs; } + friend exprtk_arithmetic operator-(exprtk_arithmetic lhs, int rhs) noexcept { return lhs.v - rhs; } + friend exprtk_arithmetic operator*(exprtk_arithmetic lhs, int rhs) noexcept { return lhs.v * rhs; } + friend exprtk_arithmetic operator/(exprtk_arithmetic lhs, int rhs) noexcept { return lhs.v / rhs; } + friend exprtk_arithmetic operator%(exprtk_arithmetic lhs, int rhs) noexcept { return std::fmod(lhs.v, rhs); } + friend exprtk_arithmetic operator+(int lhs, exprtk_arithmetic rhs) noexcept { return lhs + rhs.v; } + friend exprtk_arithmetic operator-(int lhs, exprtk_arithmetic rhs) noexcept { return lhs - rhs.v; } + friend exprtk_arithmetic operator*(int lhs, exprtk_arithmetic rhs) noexcept { return lhs * rhs.v; } + friend exprtk_arithmetic operator/(int lhs, exprtk_arithmetic rhs) noexcept { return lhs / rhs.v; } + friend exprtk_arithmetic operator%(int lhs, exprtk_arithmetic rhs) noexcept { return std::fmod(lhs, rhs.v); } + + friend exprtk_arithmetic operator+(exprtk_arithmetic lhs, int64_t rhs) noexcept { return lhs.v + rhs; } + friend exprtk_arithmetic operator-(exprtk_arithmetic lhs, int64_t rhs) noexcept { return lhs.v - rhs; } + friend exprtk_arithmetic operator*(exprtk_arithmetic lhs, int64_t rhs) noexcept { return lhs.v * rhs; } + friend exprtk_arithmetic operator/(exprtk_arithmetic lhs, int64_t rhs) noexcept { return lhs.v / rhs; } + friend exprtk_arithmetic operator%(exprtk_arithmetic lhs, int64_t rhs) noexcept { return std::fmod(lhs.v, rhs); } + friend exprtk_arithmetic operator+(int64_t lhs, exprtk_arithmetic rhs) noexcept { return lhs + rhs.v; } + friend exprtk_arithmetic operator-(int64_t lhs, exprtk_arithmetic rhs) noexcept { return lhs - rhs.v; } + friend exprtk_arithmetic operator*(int64_t lhs, exprtk_arithmetic rhs) noexcept { return lhs * rhs.v; } + friend exprtk_arithmetic operator/(int64_t lhs, exprtk_arithmetic rhs) noexcept { return lhs / rhs.v; } + friend exprtk_arithmetic operator%(int64_t lhs, exprtk_arithmetic rhs) noexcept { return std::fmod(lhs, rhs.v); } + +}; + +struct ExprtkMapper_iS_oS +{ + struct I { + struct P0 { exprtk_arithmetic value; } in; + struct P1 { exprtk_arithmetic value; } a; + struct P2 { exprtk_arithmetic value; } b; + struct P3 { exprtk_arithmetic value; } c; + struct P4 { exprtk_arithmetic value; } m1; + struct P5 { exprtk_arithmetic value; } m2; + struct P6 { exprtk_arithmetic value; } m3; + } inputs; + + struct { + + struct { exprtk_arithmetic value; } out; + } outputs; +}; + +struct ExprtkMapper_iS_oV +{ + struct I { + struct P0 { exprtk_arithmetic value; } in; + struct P1 { exprtk_arithmetic value; } a; + struct P2 { exprtk_arithmetic value; } b; + struct P3 { exprtk_arithmetic value; } c; + struct P4 { exprtk_arithmetic value; } m1; + struct P5 { exprtk_arithmetic value; } m2; + struct P6 { exprtk_arithmetic value; } m3; + } inputs; + + struct { + + struct { std::vector< exprtk_arithmetic> value; } out; + } outputs; +}; + +struct ExprtkMapper_iV_oS +{ + struct I { + struct P0 { std::span< exprtk_arithmetic> value; } in; + struct P1 { exprtk_arithmetic value; } a; + struct P2 { exprtk_arithmetic value; } b; + struct P3 { exprtk_arithmetic value; } c; + struct P4 { exprtk_arithmetic value; } m1; + struct P5 { exprtk_arithmetic value; } m2; + struct P6 { exprtk_arithmetic value; } m3; + } inputs; + + struct { + + struct { exprtk_arithmetic value; } out; + } outputs; +}; +struct ExprtkMapper_iV_oV +{ + struct I { + struct P0 { std::span< exprtk_arithmetic> value; } in; + struct P1 { exprtk_arithmetic value; } a; + struct P2 { exprtk_arithmetic value; } b; + struct P3 { exprtk_arithmetic value; } c; + struct P4 { exprtk_arithmetic value; } m1; + struct P5 { exprtk_arithmetic value; } m2; + struct P6 { exprtk_arithmetic value; } m3; + } inputs; + + struct { + + struct { std::vector< exprtk_arithmetic> value; } out; + } outputs; +}; diff --git a/templates/common/PolySynth.hpp b/templates/common/PolySynth.hpp new file mode 100644 index 0000000..0745a3d --- /dev/null +++ b/templates/common/PolySynth.hpp @@ -0,0 +1,322 @@ +#pragma once + +#include "ScoreHelpers.hpp" + +#include + +#include + +struct synth +{ + // GUItool: begin automatically generated code + AudioSynthWaveformDc lfoenvelope; // xy=589.047534942627,966.6665487289429 + AudioSynthWaveform lfo; // xy=677.4761581420898,1172.523769378662 + AudioMixer4 mixer1; // xy=720.9047355651855,1027.666706085205 + AudioAnalyzePeak peak1; // xy=949.0476112365723,890.8095207214355 + AudioSynthWaveform voice4a; // xy=1206.6190299987793,925.6666355133057 + AudioSynthWaveform voice4b; // xy=1207.6190299987793,963.6666355133057 + AudioSynthNoiseWhite voice4n; // xy=1207.6190299987793,998.6666355133057 + AudioSynthWaveform voice3b; // xy=1228.6190299987793,715.6666355133057 + AudioSynthNoiseWhite voice3n; // xy=1228.6190299987793,750.6666355133057 + AudioSynthWaveform voice3a; // xy=1233.6190299987793,672.6666355133057 + AudioSynthWaveform voice1b; // xy=1258.6190299987793,249.66663551330566 + AudioSynthNoiseWhite voice1n; // xy=1261.6190299987793,293.66663551330566 + AudioSynthWaveform voice2b; // xy=1261.6190299987793,483.66663551330566 + AudioSynthNoiseWhite voice2n; // xy=1261.6190299987793,518.6666355133057 + AudioSynthWaveform voice1a; // xy=1263.6190299987793,206.66663551330566 + AudioSynthWaveform voice2a; // xy=1263.6190299987793,445.66663551330566 + AudioSynthWaveformDc voice4filterenv; // xy=1335.6190299987793,1109.6666355133057 + AudioSynthWaveformDc voice4env; // xy=1349.6190299987793,1048.6666355133057 + AudioMixer4 voice4mix; // xy=1352.6190299987793,983.6666355133057 + AudioSynthWaveformDc voice3filterenv; // xy=1356.6190299987793,861.6666355133057 + AudioSynthWaveformDc voice3env; // xy=1370.6190299987793,800.6666355133057 + AudioMixer4 voice3mix; // xy=1373.6190299987793,735.6666355133057 + AudioSynthWaveformDc voice1filterenv; // xy=1387.6190299987793,385.66663551330566 + AudioSynthWaveformDc voice2filterenv; // xy=1389.6190299987793,629.6666355133057 + AudioMixer4 voice1mix; // xy=1403.6190299987793,269.66663551330566 + AudioSynthWaveformDc voice2env; // xy=1403.6190299987793,568.6666355133057 + AudioSynthWaveformDc voice1env; // xy=1404.6190299987793,334.66663551330566 + AudioMixer4 voice2mix; // xy=1406.6190299987793,503.66663551330566 + AudioEffectMultiply voice4multiply; // xy=1516.6190299987793,1021.6666355133057 + AudioMixer4 voice4filtermodmixer; // xy=1526.6190299987793,1137.6666355133057 + AudioEffectMultiply voice3multiply; // xy=1537.6190299987793,773.6666355133057 + AudioMixer4 voice3filtermodmixer; // xy=1554.6190299987793,883.6666355133057 + AudioEffectMultiply voice1multiply; // xy=1567.6190299987793,307.66663551330566 + AudioEffectMultiply voice2multiply; // xy=1570.6190299987793,541.6666355133057 + AudioMixer4 voice2filtermodmixer; // xy=1580.6190299987793,657.6666355133057 + AudioMixer4 voice1filtermodmixer; // xy=1584.6190299987793,417.66663551330566 + AudioFilterStateVariable voice4filter; // xy=1699.6190299987793,1044.6666355133057 + AudioFilterStateVariable voice3filter; // xy=1717.6190299987793,817.6666355133057 + AudioFilterStateVariable voice2filter; // xy=1753.6190299987793,564.6666355133057 + AudioFilterStateVariable voice1filter; // xy=1770.6190299987793,359.66663551330566 + AudioMixer4 first4premix; // xy=2178.6190299987793,1210.6666355133057 + AudioFilterStateVariable delayFilter; // xy=2627.6190299987793,1404.6666355133057 + AudioMixer4 mainOutMixer; // xy=2698.6190299987793,1287.6666355133057 + AudioEffectDelay delay1; // xy=2756.6190299987793,1599.6666355133057 + AudioOutputI2S i2s1; // xy=2924.6190299987793,1285.6666355133057 + AudioConnection patchCord1{lfoenvelope, 0, mixer1, 0}; + AudioConnection patchCord2{lfo, 0, voice1filtermodmixer, 1}; + AudioConnection patchCord3{lfo, 0, voice2filtermodmixer, 1}; + AudioConnection patchCord4{lfo, 0, voice3filtermodmixer, 1}; + AudioConnection patchCord5{lfo, 0, voice4filtermodmixer, 1}; + AudioConnection patchCord10{lfo, 0, mixer1, 1}; + AudioConnection patchCord11{mixer1, peak1}; + AudioConnection patchCord15{voice4a, 0, voice4mix, 0}; + AudioConnection patchCord16{voice4b, 0, voice4mix, 1}; + AudioConnection patchCord17{voice4n, 0, voice4mix, 2}; + AudioConnection patchCord27{voice3b, 0, voice3mix, 1}; + AudioConnection patchCord28{voice3n, 0, voice3mix, 2}; + AudioConnection patchCord29{voice3a, 0, voice3mix, 0}; + AudioConnection patchCord30{voice1b, 0, voice1mix, 1}; + AudioConnection patchCord31{voice1n, 0, voice1mix, 2}; + AudioConnection patchCord32{voice2b, 0, voice2mix, 1}; + AudioConnection patchCord33{voice2n, 0, voice2mix, 3}; + AudioConnection patchCord34{voice1a, 0, voice1mix, 0}; + AudioConnection patchCord35{voice2a, 0, voice2mix, 0}; + AudioConnection patchCord39{voice4filterenv, 0, voice4filtermodmixer, 0}; + AudioConnection patchCord42{voice4env, 0, voice4multiply, 1}; + AudioConnection patchCord45{voice4mix, 0, voice4multiply, 0}; + AudioConnection patchCord47{voice3filterenv, 0, voice3filtermodmixer, 0}; + AudioConnection patchCord52{voice3env, 0, voice3multiply, 1}; + AudioConnection patchCord53{voice3mix, 0, voice3multiply, 0}; + AudioConnection patchCord54{voice1filterenv, 0, voice1filtermodmixer, 0}; + AudioConnection patchCord55{voice2filterenv, 0, voice2filtermodmixer, 0}; + AudioConnection patchCord56{voice1mix, 0, voice1multiply, 0}; + AudioConnection patchCord57{voice2env, 0, voice2multiply, 1}; + AudioConnection patchCord58{voice1env, 0, voice1multiply, 1}; + AudioConnection patchCord59{voice2mix, 0, voice2multiply, 0}; + AudioConnection patchCord62{voice4multiply, 0, voice4filter, 0}; + AudioConnection patchCord64{voice4filtermodmixer, 0, voice4filter, 1}; + AudioConnection patchCord68{voice3multiply, 0, voice3filter, 0}; + AudioConnection patchCord71{voice3filtermodmixer, 0, voice3filter, 1}; + AudioConnection patchCord72{voice1multiply, 0, voice1filter, 0}; + AudioConnection patchCord73{voice2multiply, 0, voice2filter, 0}; + AudioConnection patchCord74{voice2filtermodmixer, 0, voice2filter, 1}; + AudioConnection patchCord75{voice1filtermodmixer, 0, voice1filter, 1}; + AudioConnection patchCord78{voice4filter, 0, first4premix, 3}; + AudioConnection patchCord81{voice3filter, 0, first4premix, 2}; + AudioConnection patchCord82{voice2filter, 0, first4premix, 1}; + AudioConnection patchCord83{voice1filter, 0, first4premix, 0}; + AudioConnection patchCord85{first4premix, 0, mainOutMixer, 0}; + AudioConnection patchCord86{delayFilter, 0, mainOutMixer, 3}; + AudioConnection patchCord87{mainOutMixer, 0, i2s1, 0}; + AudioConnection patchCord88{mainOutMixer, 0, i2s1, 1}; + AudioConnection patchCord89{mainOutMixer, delay1}; + AudioConnection patchCord90{delay1, 0, delayFilter, 0}; + AudioControlSGTL5000 sgtl5000_1; // xy=2661.6190299987793,1054.6666355133057 + // GUItool: end automatically generated code + + synth() + { + + AudioMemory(20); + Serial.begin(115200); + sgtl5000_1.enable(); + sgtl5000_1.volume(.7); + + // mix + first4premix.gain(0, .25); + first4premix.gain(1, .25); + first4premix.gain(2, .25); + first4premix.gain(3, .25); + + // Voice 1 + voice1a.begin(.3, 440, WAVEFORM_SQUARE); + voice1b.begin(.3, 440, WAVEFORM_SAWTOOTH); + // Voice 2 + voice2a.begin(.3, 440, WAVEFORM_SQUARE); + voice2b.begin(.3, 440, WAVEFORM_SAWTOOTH); + // Voice 3 + voice3a.begin(.3, 440, WAVEFORM_SQUARE); + voice3b.begin(.3, 440, WAVEFORM_SAWTOOTH); + // Voice 4 + voice4a.begin(.3, 440, WAVEFORM_SQUARE); + voice4b.begin(.3, 440, WAVEFORM_SAWTOOTH); + + delayFilter.frequency(3000); + delayFilter.resonance(1); + delay1.delay(0, 0); + mainOutMixer.gain(3, 0); + + // LFO + lfo.begin(1, 3, WAVEFORM_SINE); + + mainOutMixer.gain(0, .5); + lfoenvelope.amplitude(1); + } + void heh() + { + + for(;;) + for(int i = 0; i < 8; i++) + { + // audio startup + // if(i < 4) + { + voice1env.amplitude(.05, 1); + voice1a.frequency(rand() % 100 + 200); + voice1b.frequency(rand() % 100 + 400); + voice1n.amplitude(rand() % 100 / 100.f); + delay(200); + voice1env.amplitude(0, 0); + } + } + } +}; + +namespace Synthimi +{ +// Let's define a generic port for the waveform chooser +struct Waveform +{ + halp_meta(columns, 5) + enum enum_type + { + Sine, + Square, + Triangle, + Sawtooth, + Noise + } value; + enum widget + { + enumeration, + list, + combobox + }; + + struct range + { + std::string_view values[5]{"Sine", "Square", "Tri", "Saw", "Noise"}; + std::string_view pixmaps[10]{ + ":/icons/wave_sin_off.png", ":/icons/wave_sin_on.png", + ":/icons/wave_square_off.png", ":/icons/wave_square_on.png", + ":/icons/wave_triangle_off.png", ":/icons/wave_triangle_on.png", + ":/icons/wave_saw_off.png", ":/icons/wave_saw_on.png", + ":/icons/wave_noise1_off.png", ":/icons/wave_noise1_on.png"}; + enum_type init = Sine; + }; + + operator enum_type&() noexcept { return value; } + operator enum_type() const noexcept { return value; } + auto& operator=(enum_type t) noexcept + { + value = t; + return *this; + } +}; + +inline auto m2f(double m) +{ + return (440.0 * std::exp2((m - 69.0) / 12.0)); +} + +class Synthimi +{ +public: + halp_meta(name, "Synthimi") + halp_meta(category, "Audio/Synth") + halp_meta(author, "Jean-MichaĆ«l Celerier") + halp_meta( + description, + "A basic synthesizer. Modeled after the meows of the developer's cat, Shashimi.") + halp_meta(manual_url, "https://ossia.io/score-docs/processes/synthimi.html") + halp_meta(c_name, "synthimi") + halp_meta(uuid, "d4008ff6-73b9-4575-80a4-60e3da095db7") + + struct ins + { + halp::midi_bus<"In"> midi; + + // Here we'll define our parameters - very simply, and then we'll try to refactor + // that afterwards :-) + halp::knob_f32<"Osc 1 Amp.", halp::range{0., 1., 1.}> osc0_amp; + struct : Waveform + { + halp_meta(name, "Osc 1 Wave") + } osc0_waveform; + struct : halp::knob_f32<"Osc 1 Pitch", halp::range{-12, 12, 0.}> + { + } osc0_pitch; + struct : halp::knob_i32<"Osc 1 Oct", halp::irange{-5, 5, 0}> + { + } osc0_oct; + + halp::knob_f32<"Osc 2 Amp.", halp::range{0., 1., 1.}> osc1_amp; + struct : Waveform + { + halp_meta(name, "Osc 2 Wave") + } osc1_waveform; + struct : halp::knob_f32<"Osc 2 Pitch", halp::range{-12, 12, 0.}> + { + } osc1_pitch; + struct : halp::knob_i32<"Osc 2 Oct", halp::irange{-5, 5, 0}> + { + } osc1_oct; + + halp::knob_f32<"Osc 3 Amp.", halp::range{0., 1., 1.}> osc2_amp; + struct : Waveform + { + halp_meta(name, "Osc 3 Wave") + } osc2_waveform; + struct : halp::knob_f32<"Osc 3 Pitch", halp::range{-12, 12, 0.}> + { + } osc2_pitch; + struct : halp::knob_i32<"Osc 3 Oct", halp::irange{-5, 5, 0}> + { + } osc2_oct; + + halp::knob_f32<"Osc 4 Amp.", halp::range{0., 1., 1.}> osc3_amp; + struct : Waveform + { + halp_meta(name, "Osc 4 Wave") + } osc3_waveform; + struct : halp::knob_f32<"Osc 4 Pitch", halp::range{-12, 12, 0.}> + { + } osc3_pitch; + struct : halp::knob_i32<"Osc 4 Oct", halp::irange{-5, 5, 0}> + { + } osc3_oct; + + halp::knob_f32<"Amp. Attack", halp::range{0., 1., 0.1}> amp_attack; + halp::knob_f32<"Amp. Decay", halp::range{0., 1., 0.1}> amp_decay; + halp::knob_f32<"Amp. Sustain", halp::range{0., 1., 0.5}> amp_sustain; + halp::knob_f32<"Amp. Release", halp::range{0., 1., 0.2}> amp_release; + + struct + { + halp__enum_combobox("Type", LPF, LPF, HPF) + } filt_type; + halp::knob_f32<"Cutoff", halp::range{20., 20000., 2000.}> filt_cutoff; + halp::knob_f32<"Reso", halp::range{0., 0., 1.}> filt_res; + halp::knob_f32<"Flt. Attack", halp::range{0., 1., 0.1}> filt_attack; + halp::knob_f32<"Flt. Decay", halp::range{0., 1., 0.1}> filt_decay; + halp::knob_f32<"Flt. Sustain", halp::range{0., 1., 0.5}> filt_sustain; + halp::knob_f32<"Flt. Release", halp::range{0., 1., 0.2}> filt_release; + + struct + { + halp__enum_combobox("Polyphony", Poly, Mono, Poly) + } poly_mode; + halp::knob_f32<"Porta", halp::range{0., 1., 0.}> portamento; + + halp::toggle<"Filter", halp::toggle_setup{true}> filt_env; + halp::knob_f32<"Drive", halp::range{0., 1., 0.}> drive; + + struct + { + halp__enum_combobox("Modmatrix", S, S, C, CSP, CST, CSC) + } matrix; + halp::knob_i32<"Unison", halp::irange{0, 16, 0}> unison; + } inputs; + + struct + { + } outputs; + + halp::setup settings; + void prepare(halp::setup info) { settings = info; } + using tick = halp::tick; + void operator()(halp::tick t) { } +}; +} diff --git a/templates/common/ScoreHelpers.hpp b/templates/common/ScoreHelpers.hpp new file mode 100644 index 0000000..5b36b8f --- /dev/null +++ b/templates/common/ScoreHelpers.hpp @@ -0,0 +1,114 @@ +#pragma once +#include "ExprtkMapper.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +struct g_tick_t +{ + int frames{}; + float relative_position{}; +} g_tick; + +static int get_frames(const g_tick_t& tk) +{ + return tk.frames; +} +static float get_relative_position(const g_tick_t& tk) +{ + return tk.relative_position; +} + +static constexpr void avnd_call(auto& proc) +{ + if constexpr(requires { proc(g_tick.frames); }) + proc(g_tick.frames); + else if constexpr(requires { proc(); }) + proc(); +} +using namespace ao; +struct Dummy +{ + static inline int variable; +}; +template +struct static_string +{ + consteval static_string(const char (&str)[N]) noexcept + { + for(int i = 0; i < N; i++) + { + value[i] = str[i]; + } + } + + static_assert(N > 0); + static constexpr int length = N - 1; + char value[N]; +}; + +static inline constexpr void value_adapt(auto&& to, auto&& from) +{ + to = from; +} +static inline constexpr void +value_adapt(avnd::midi_port auto&& to, avnd::midi_port auto&& from) +{ + to = from; +} +static inline constexpr void +value_adapt(avnd::parameter auto&& to, avnd::parameter auto&& from) +{ + to.value = from.value; +} +template +static inline constexpr void value_adapt(auto& to, std::optional& from) +{ + if(from) + to = *from; +} +template +static inline constexpr void value_adapt(auto& to, const std::optional& from) +{ + if(from) + to = *from; +} +static inline constexpr void value_adapt(auto& to, auto& from, double factor) +{ + to = from * factor; +} +template +static inline constexpr void value_adapt(auto& to, std::optional& from, double factor) +{ + if(from) + to = *from * factor; +} + +template +static inline constexpr void +value_adapt(auto& to, const std::optional& from, double factor) +{ + if(from) + to = *from * factor; +} diff --git a/templates/common/ossia_embedded_api.hpp b/templates/common/ossia_embedded_api.hpp new file mode 100644 index 0000000..cb13112 --- /dev/null +++ b/templates/common/ossia_embedded_api.hpp @@ -0,0 +1,13 @@ +#pragma once + +extern "C" { +void ossia_init_board(); + +void ossia_read_pins(); +void ossia_write_pins(); + +void ossia_read_net(); +void ossia_write_net(); + +void ossia_run_graph(); +} diff --git a/templates/desktop-standalone/CMakeLists.txt b/templates/desktop-standalone/CMakeLists.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/templates/desktop-standalone/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/templates/desktop/Untitled-1.cpp b/templates/desktop/Untitled-1.cpp new file mode 100644 index 0000000..e446f5f --- /dev/null +++ b/templates/desktop/Untitled-1.cpp @@ -0,0 +1,82 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ExprtkMapper.hpp" +#include +#include + +#include +using namespace ossia; +using namespace ao; +struct Dummy { + static inline int variable; + }; + +struct +{ + int frames{}; + float relative_position{}; +} g_tick; + +int get_frames(const ossia::token_request& tok) { return (tok.date - tok.prev_date).impl; } + +float get_relative_position(const ossia::token_request& tok) { return tok.position(); } +template +struct static_string +{ + consteval static_string(const char (&str)[N]) noexcept + { + for(int i = 0; i < N; i++) + { + value[i] = str[i]; + } + } + + static_assert(N > 0); + static constexpr int length = N - 1; + char value[N]; +}; + +static inline constexpr void value_adapt(auto&& to, auto&& from) +{ + if_possible(to = from) + else if constexpr(requires { to = *from; }) { if(from) to = *from; } +} + + +template +static inline constexpr void do_device_read(auto& val) +{ + fprintf(stderr, "Reading: %s : %f\n", address.value, (double) val); + val = double(rand()) / RAND_MAX; +} + +template +static inline constexpr void do_device_write(const auto& val) +{ + fprintf(stderr, "Writing: %s : %f\n", address.value, (double) val); +} + +template +static inline constexpr void do_device_write(const std::optional& val) +{ + if(val) + fprintf(stderr, "Writing: %s : %f\n", address.value, (double) *val); +} + +static constexpr void avnd_call(auto& proc) +{ + if constexpr(requires { proc(g_tick.frames); }) + proc(g_tick.frames); + else if constexpr(requires { proc(); }) + proc(); +} + +#include "scenario-struct.hpp" diff --git a/templates/desktop/build.sh b/templates/desktop/build.sh new file mode 100755 index 0000000..fb60b1c --- /dev/null +++ b/templates/desktop/build.sh @@ -0,0 +1,28 @@ +# -target amd64-linux-none-gnueabi \ + +clang++ \ + -shared -Ofast -flto \ + Untitled-1.cpp \ + -I ~/ossia/score/3rdparty/avendish/examples \ + -I ~/ossia/score/3rdparty/avendish/include \ + -I ~/ossia/score/3rdparty/libossia/src/ \ + -I $PWD \ + -std=c++20 \ + -isystem ~/ossia/score/3rdparty/libossia/3rdparty/unordered_dense/include \ + -isystem ~/ossia/score/3rdparty/libossia/3rdparty/SmallFunction/smallfun/include/ \ + -isystem ~/ossia/score/3rdparty/libossia/3rdparty/span/include \ + -isystem ~/ossia/score/3rdparty/libossia/3rdparty/nano-signal-slot/include/ \ + -isystem ~/ossia/score/3rdparty/libossia/3rdparty/rnd/include \ + -ferror-limit=3 \ + -DOSSIA_CALLBACK_CONTAINER_MUTEX=std::mutex \ + -fno-plt -fno-ident \ + -Bsymbolic \ + -fno-exceptions \ + -fno-rtti \ + -fno-unwind-tables \ + -fvisibility=internal \ + -fno-stack-protector \ + -o x.so \ + -fno-pic \ + -s \ + -fPIC diff --git a/templates/desktop/exprtk.cpp b/templates/desktop/exprtk.cpp new file mode 100644 index 0000000..4ee9ad2 --- /dev/null +++ b/templates/desktop/exprtk.cpp @@ -0,0 +1,20 @@ +#include +#include + +namespace Nodes +{ +std::string exprtk_to_cpp(std::string exprtk) noexcept; +} + +int main(int argc, char** argv) +{ + std::string content; + + while(!std::cin.eof()) { + char arr[1024]; + std::cin.read(arr,sizeof(arr)); + int s = std::cin.gcount(); + content.append(arr,s); + } + std::cerr << Nodes::exprtk_to_cpp(content) << std::endl; +} \ No newline at end of file diff --git a/templates/desktop/picodevice..hpp b/templates/desktop/picodevice..hpp new file mode 100644 index 0000000..0151c92 --- /dev/null +++ b/templates/desktop/picodevice..hpp @@ -0,0 +1,184 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void ossia_task_0() { + { + static ao::ColorAutomation p0{ + .inputs = {.curve = {.value = {{ + {0.0117096018735363, {0, 0, 0}}, + {0.8, {100, -5.3357444, 5.433189}}, + }}}}}; // Color automation + static avnd_tools::PatternUnfolder p1{.inputs = { + .Pattern = {0}, + }}; // Pattern applier + /// INPUT READ + + /// p: Color automation + /// p: Pattern applier + { + do_device_read<"">( + (avnd::input_introspection::field<0>( + p1.inputs) + .value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<1>( + p1.inputs) + .value)); + } + /// GRAPH PROCESSING + + /// p: Color automation + p0({.frames = g_tick.frames, + .relative_position = g_tick.relative_position}); + /// p: Pattern applier + { + value_adapt( + (avnd::input_introspection::field<0>( + p1.inputs) + .value), + p0.outputs.value.value); + } + avnd_call(p1); + /// OUTPUT WRITE + + /// p: Color automation + { do_network_write<"OSC:/0/led/color">(p0.outputs.value.value); } + /// p: Pattern applier + } +} + +static void ossia_task_1() { + { + static LFO p0{.inputs = + { + .freq = {1}, + .ampl = {0}, + .offset = {0.9722222}, + .jitter = {0}, + .phase = {0}, + .waveform = {ao::LFO::Inputs::Waveform::enum_type(0)}, + }, + .setup = {.rate = 1e6}}; // LFO + /// INPUT READ + + /// p: LFO + { + do_device_read<"">( + (avnd::input_introspection::field<0>(p0.inputs).value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<1>(p0.inputs).value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<2>(p0.inputs).value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<3>(p0.inputs).value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<4>(p0.inputs).value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<5>(p0.inputs).value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<6>(p0.inputs).value)); + } + /// GRAPH PROCESSING + + /// p: LFO + avnd_call(p0); + /// OUTPUT WRITE + + /// p: LFO + { + do_network_write<"OSC:/*/motor/*/speed">( + (avnd::output_introspection::field<0>(p0.outputs).value)); + } + } +} + +static void ossia_task_2() { + { + static LFO p0{.inputs = + { + .freq = {1}, + .ampl = {0.5}, + .offset = {0.5}, + .jitter = {0}, + .phase = {0}, + .waveform = {ao::LFO::Inputs::Waveform::enum_type(0)}, + }, + .setup = {.rate = 1e6}}; // LFO.3 + /// INPUT READ + + /// p: LFO.3 + { + do_device_read<"">( + (avnd::input_introspection::field<0>(p0.inputs).value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<1>(p0.inputs).value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<2>(p0.inputs).value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<3>(p0.inputs).value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<4>(p0.inputs).value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<5>(p0.inputs).value)); + } + { + do_device_read<"">( + (avnd::input_introspection::field<6>(p0.inputs).value)); + } + /// GRAPH PROCESSING + + /// p: LFO.3 + avnd_call(p0); + /// OUTPUT WRITE + + /// p: LFO.3 + { + do_network_write<"OSC:/*/led/bright">( + (avnd::output_introspection::field<0>(p0.outputs).value)); + } + } +} + +extern "C" void run_graph(void) { + ossia_task_0(); + ossia_task_1(); + ossia_task_2(); +} diff --git a/templates/desktop/picotasks.hpp b/templates/desktop/picotasks.hpp new file mode 100644 index 0000000..e69de29 diff --git a/templates/desktop/scenario-struct.hpp b/templates/desktop/scenario-struct.hpp new file mode 100644 index 0000000..fe9065a --- /dev/null +++ b/templates/desktop/scenario-struct.hpp @@ -0,0 +1,97 @@ +std::vector process_exec_table[4] = { }; +struct scenario_t { + std::shared_ptr itv_0_sc_1 = std::make_shared(); + const std::shared_ptr& itv_0_sc_1_ts_0 = itv_0_sc_1->get_start_time_sync(); + std::shared_ptr itv_0_sc_1_ts_1 = std::make_shared(); + std::shared_ptr itv_0_sc_1_ts_2 = std::make_shared(); + std::shared_ptr itv_0_sc_1_ts_3 = std::make_shared(); + std::shared_ptr itv_0_sc_1_ev_0 = std::make_shared(ossia::time_event::exec_callback{}, *itv_0_sc_1_ts_0, ossia::expressions::make_expression_true()); + std::shared_ptr itv_0_sc_1_ev_1 = std::make_shared(ossia::time_event::exec_callback{}, *itv_0_sc_1_ts_1, ossia::expressions::make_expression_true()); + std::shared_ptr itv_0_sc_1_ev_2 = std::make_shared(ossia::time_event::exec_callback{}, *itv_0_sc_1_ts_2, ossia::expressions::make_expression_true()); + std::shared_ptr itv_0_sc_1_ev_3 = std::make_shared(ossia::time_event::exec_callback{}, *itv_0_sc_1_ts_3, ossia::expressions::make_expression_true()); + std::shared_ptr itv_0_sc_1_itv_1 = ossia::time_interval::create({}, *itv_0_sc_1_ev_0, *itv_0_sc_1_ev_1, 7973223416_tv, 7973223416_tv, 7973223416_tv); + std::shared_ptr itv_0_sc_1_itv_1_Gradient_1 = std::make_shared(process_exec_table[0]); + std::shared_ptr itv_0_sc_1_itv_1_avnd_pattern_apply_2 = std::make_shared(process_exec_table[1]); + std::shared_ptr itv_0_sc_1_itv_2 = ossia::time_interval::create({}, *itv_0_sc_1_ev_0, *itv_0_sc_1_ev_2, 8210849957_tv, 0_tv, 4611686018427387903_tv); + std::shared_ptr itv_0_sc_1_itv_2_p_1 = std::make_shared(process_exec_table[2]); + std::shared_ptr itv_0_sc_1_itv_2_p_2 = std::make_shared(process_exec_table[3]); + std::shared_ptr itv_0_sc_1_itv_3 = ossia::time_interval::create({}, *itv_0_sc_1_ev_0, *itv_0_sc_1_ev_3, 3175200000_tv, 3175200000_tv, 3175200000_tv); + + scenario_t() { + itv_0_sc_1_ts_1->set_expression(ossia::expressions::make_expression_true()); + itv_0_sc_1->add_time_sync(itv_0_sc_1_ts_1); + itv_0_sc_1_ts_2->set_expression(ossia::expressions::make_expression_true()); + itv_0_sc_1->add_time_sync(itv_0_sc_1_ts_2); + itv_0_sc_1_ts_3->set_expression(ossia::expressions::make_expression_true()); + itv_0_sc_1->add_time_sync(itv_0_sc_1_ts_3); + itv_0_sc_1_ts_0->insert(itv_0_sc_1_ts_0->get_time_events().end(), itv_0_sc_1_ev_0); + itv_0_sc_1_ts_1->insert(itv_0_sc_1_ts_1->get_time_events().end(), itv_0_sc_1_ev_1); + itv_0_sc_1_ts_2->insert(itv_0_sc_1_ts_2->get_time_events().end(), itv_0_sc_1_ev_2); + itv_0_sc_1_ts_3->insert(itv_0_sc_1_ts_3->get_time_events().end(), itv_0_sc_1_ev_3); + itv_0_sc_1->add_time_interval(itv_0_sc_1_itv_1); + itv_0_sc_1_itv_1->add_time_process(itv_0_sc_1_itv_1_Gradient_1); + itv_0_sc_1_itv_1->add_time_process(itv_0_sc_1_itv_1_avnd_pattern_apply_2); + itv_0_sc_1->add_time_interval(itv_0_sc_1_itv_2); + itv_0_sc_1_itv_2->add_time_process(itv_0_sc_1_itv_2_p_1); + itv_0_sc_1_itv_2->add_time_process(itv_0_sc_1_itv_2_p_2); + itv_0_sc_1->add_time_interval(itv_0_sc_1_itv_3); + + } +} scenario; +struct graph_t { + ao::ColorAutomation itv_0_sc_1_itv_1_Gradient_1 = { + .inputs = { .curve = { .value = { { { 0.0117096018735363, { 0, 0, 0 } }, { 0.8, { 100, -5.3357444, 5.433189 } }, } } } } + }; + avnd_tools::PatternUnfolder itv_0_sc_1_itv_1_avnd_pattern_apply_2 = { + .inputs = { .Pattern = { 0 }, } + }; + LFO itv_0_sc_1_itv_2_p_1 = { + .inputs = { .freq = { 1 }, .ampl = { 0 }, .offset = { 0.9722222 }, .jitter = { 0 }, .phase = { 0 }, .waveform = { ao::LFO::Inputs::Waveform::enum_type(0) }, }, .setup = { .rate = 1e6 } + }; + LFO itv_0_sc_1_itv_2_p_2 = { + .inputs = { .freq = { 1 }, .ampl = { 0.5 }, .offset = { 0.5 }, .jitter = { 0 }, .phase = { 0 }, .waveform = { ao::LFO::Inputs::Waveform::enum_type(0) }, }, .setup = { .rate = 1e6 } + }; + + graph_t() { + + } + void execute() { + { + // Process: itv_0_sc_1_itv_1_Gradient_1 (N8Gradient12ProcessModelE) + for(const auto& g_tick : process_exec_table[0]) { + bool cable_read = false; + itv_0_sc_1_itv_1_Gradient_1( { .frames = g_tick.frames, .relative_position = g_tick.relative_position } ); + { do_device_write<"OSC:/0/led/color">(itv_0_sc_1_itv_1_Gradient_1.outputs.value.value); } + } + process_exec_table[0].clear(); + } + { + // Process: itv_0_sc_1_itv_1_avnd_pattern_apply_2 (N4oscr12ProcessModelIN10avnd_tools15PatternUnfolderEEE) + for(const auto& g_tick : process_exec_table[1]) { + bool cable_read = false; + { if (!process_exec_table[0].empty()) { value_adapt((avnd::input_introspection::field<0>(itv_0_sc_1_itv_1_avnd_pattern_apply_2.inputs).value), itv_0_sc_1_itv_1_Gradient_1.outputs.value.value); cable_read = true; } } + avnd_call(itv_0_sc_1_itv_1_avnd_pattern_apply_2); + } + process_exec_table[1].clear(); + } + { + // Process: itv_0_sc_1_itv_2_p_1 (N7Control14ControlProcessIN5Nodes3LFO2v24NodeENS_10is_controlEEE) + for(const auto& g_tick : process_exec_table[2]) { + bool cable_read = false; + avnd_call(itv_0_sc_1_itv_2_p_1); + { do_device_write<"OSC:/*/motor/*/speed">((avnd::output_introspection::field<0>(itv_0_sc_1_itv_2_p_1.outputs).value)); } + } + process_exec_table[2].clear(); + } + { + // Process: itv_0_sc_1_itv_2_p_2 (N7Control14ControlProcessIN5Nodes3LFO2v24NodeENS_10is_controlEEE) + for(const auto& g_tick : process_exec_table[3]) { + bool cable_read = false; + avnd_call(itv_0_sc_1_itv_2_p_2); + { do_device_write<"OSC:/*/led/bright">((avnd::output_introspection::field<0>(itv_0_sc_1_itv_2_p_2.outputs).value)); } + } + process_exec_table[3].clear(); + } + + } +} graph; diff --git a/templates/desktop/test.score b/templates/desktop/test.score new file mode 100644 index 0000000..e780706 --- /dev/null +++ b/templates/desktop/test.score @@ -0,0 +1 @@ +{"Document":{"ObjectName":"Scenario::ScenarioDocumentModel","id":1,"BaseScenario":{"ObjectName":"Scenario::BaseScenario","id":0,"Constraint":{"ObjectName":"Scenario::IntervalModel","id":0,"Metadata":{"ScriptingName":"test","Comment":"","Color":"Transparent1","Label":"","Touched":true},"Inlet":{"uuid":"a1574bb0-cbd4-4c7d-9417-0c25cfd1187b","ObjectName":"Inlet","id":0,"Hidden":false,"Custom":"Audio in","Exposed":"audio in"},"Outlet":{"uuid":"a1d97535-18ac-444a-8417-0cbc1692d897","ObjectName":"Outlet","id":0,"Hidden":false,"Custom":"Audio out","Exposed":"audio out","GainInlet":{"uuid":"9a13fb32-269a-47bf-99a9-930188c1f19c","ObjectName":"Inlet","id":10000,"Hidden":false,"Custom":"Gain","Exposed":"gain","Value":{},"Domain":{"Float":{"Min":0.0,"Max":1.0}}},"PanInlet":{"uuid":"9a13fb32-269a-47bf-99a9-930188c1f19c","ObjectName":"Inlet","id":10001,"Hidden":false,"Custom":"Pan","Exposed":"pan","Value":{},"Domain":{}},"Gain":1.0,"Pan":[1.0,1.0],"Propagate":true},"Processes":[{"uuid":"de035912-5b03-49a8-bc4d-b2cba68e21d9","ObjectName":"Scenario","id":1,"Metadata":{"ScriptingName":"Scenario.1","Comment":"","Color":"Transparent1","Label":"","Touched":false},"Duration":10584000000,"Height":1500.0,"StartOffset":0,"LoopDuration":10584000000,"Pos":[40.0,40.0],"Size":[200.0,100.0],"Loops":false,"Inlet":{"uuid":"a1574bb0-cbd4-4c7d-9417-0c25cfd1187b","ObjectName":"Inlet","id":0,"Hidden":false,"Custom":"In","Exposed":"in"},"Outlet":{"uuid":"a1d97535-18ac-444a-8417-0cbc1692d897","ObjectName":"Outlet","id":0,"Hidden":false,"Custom":"Out","Exposed":"out","GainInlet":{"uuid":"9a13fb32-269a-47bf-99a9-930188c1f19c","ObjectName":"Inlet","id":10000,"Hidden":false,"Custom":"Gain","Exposed":"gain","Value":{},"Domain":{"Float":{"Min":0.0,"Max":1.0}}},"PanInlet":{"uuid":"9a13fb32-269a-47bf-99a9-930188c1f19c","ObjectName":"Inlet","id":10001,"Hidden":false,"Custom":"Pan","Exposed":"pan","Value":{},"Domain":{}},"Gain":1.0,"Pan":[1.0,1.0],"Propagate":true},"StartTimeNodeId":0,"StartEventId":0,"StartStateId":0,"Exclusive":false,"TimeNodes":[{"ObjectName":"Scenario::TimeSyncModel","id":0,"Metadata":{"ScriptingName":"Sync.start","Comment":"","Color":"Gray","Label":"","Touched":true},"Date":0,"Events":[0],"MusicalSync":-1.0,"AutoTrigger":false,"Start":true,"Active":false,"Expression":" { true == false } "},{"ObjectName":"Scenario::TimeSyncModel","id":1,"Metadata":{"ScriptingName":"Sync.acid82","Comment":"","Color":"Gray","Label":"","Touched":true},"Date":1294469470,"Events":[1],"MusicalSync":-1.0,"AutoTrigger":false,"Start":false,"Active":false,"Expression":" { true == false } "},{"ObjectName":"Scenario::TimeSyncModel","id":2,"Metadata":{"ScriptingName":"Sync.love35","Comment":"","Color":"Gray","Label":"","Touched":true},"Date":3499583162,"Events":[2],"MusicalSync":-1.0,"AutoTrigger":false,"Start":false,"Active":false,"Expression":" { true == false } "}],"Events":[{"ObjectName":"Scenario::EventModel","id":0,"Metadata":{"ScriptingName":"Event.start","Comment":"","Color":"Emphasis4","Label":"","Touched":true},"TimeNode":0,"States":[0,1],"Condition":"","Date":0,"Offset":0},{"ObjectName":"Scenario::EventModel","id":1,"Metadata":{"ScriptingName":"Event.jury8","Comment":"","Color":"Emphasis4","Label":"","Touched":true},"TimeNode":1,"States":[2],"Condition":"","Date":1294469470,"Offset":0},{"ObjectName":"Scenario::EventModel","id":2,"Metadata":{"ScriptingName":"Event.side51","Comment":"","Color":"Emphasis4","Label":"","Touched":true},"TimeNode":2,"States":[3],"Condition":"","Date":3499583162,"Offset":0}],"States":[{"ObjectName":"Scenario::StateModel","id":0,"Metadata":{"ScriptingName":"State.start","Comment":"","Color":"Base1","Label":"","Touched":true},"Event":0,"PreviousConstraint":null,"NextConstraint":null,"HeightPercentage":0.02,"Messages":{"Name":"","Accessors":[],"Unit":"none","Previous":[],"Following":[],"User":null,"Priorities":[1,2,0]},"Controls":[],"StateProcesses":[]},{"ObjectName":"Scenario::StateModel","id":1,"Metadata":{"ScriptingName":"State.jets55","Comment":"","Color":"Base1","Label":"","Touched":true},"Event":0,"PreviousConstraint":null,"NextConstraint":1,"HeightPercentage":0.06,"Messages":{"Name":"","Accessors":[],"Unit":"none","Previous":[],"Following":[],"User":null,"Priorities":[1,2,0],"Children":[{"Name":"OSC","Accessors":[],"Unit":"none","Previous":[],"Following":[],"User":null,"Priorities":[1,2,0],"Children":[{"Name":"foo","Accessors":[],"Unit":"none","Previous":[],"Following":[{"Process":1,"Value":{"Float":0.0}}],"User":null,"Priorities":[1,2,0]}]}]},"Controls":[],"StateProcesses":[]},{"ObjectName":"Scenario::StateModel","id":2,"Metadata":{"ScriptingName":"State.flex61","Comment":"","Color":"Base1","Label":"","Touched":true},"Event":1,"PreviousConstraint":1,"NextConstraint":2,"HeightPercentage":0.06,"Messages":{"Name":"","Accessors":[],"Unit":"none","Previous":[],"Following":[],"User":null,"Priorities":[1,2,0],"Children":[{"Name":"OSC","Accessors":[],"Unit":"none","Previous":[],"Following":[],"User":null,"Priorities":[1,2,0],"Children":[{"Name":"foo","Accessors":[],"Unit":"none","Previous":[{"Process":1,"Value":{"Float":0.0}}],"Following":[],"User":null,"Priorities":[1,2,0]}]}]},"Controls":[],"StateProcesses":[]},{"ObjectName":"Scenario::StateModel","id":3,"Metadata":{"ScriptingName":"State.lobe10","Comment":"","Color":"Base1","Label":"","Touched":true},"Event":2,"PreviousConstraint":2,"NextConstraint":null,"HeightPercentage":0.06,"Messages":{"Name":"","Accessors":[],"Unit":"none","Previous":[],"Following":[],"User":null,"Priorities":[1,2,0],"Children":[{"Name":"OSC","Accessors":[],"Unit":"none","Previous":[],"Following":[],"User":null,"Priorities":[1,2,0]}]},"Controls":[],"StateProcesses":[]}],"Constraints":[{"ObjectName":"Scenario::IntervalModel","id":1,"Metadata":{"ScriptingName":"Interval.orin76","Comment":"","Color":"Transparent1","Label":"","Touched":true},"Inlet":{"uuid":"a1574bb0-cbd4-4c7d-9417-0c25cfd1187b","ObjectName":"Inlet","id":0,"Hidden":false,"Custom":"Audio in","Exposed":"audio in"},"Outlet":{"uuid":"a1d97535-18ac-444a-8417-0cbc1692d897","ObjectName":"Outlet","id":0,"Hidden":false,"Custom":"Audio out","Exposed":"audio out","GainInlet":{"uuid":"9a13fb32-269a-47bf-99a9-930188c1f19c","ObjectName":"Inlet","id":10000,"Hidden":false,"Custom":"Gain","Exposed":"gain","Value":{},"Domain":{"Float":{"Min":0.0,"Max":1.0}}},"PanInlet":{"uuid":"9a13fb32-269a-47bf-99a9-930188c1f19c","ObjectName":"Inlet","id":10001,"Hidden":false,"Custom":"Pan","Exposed":"pan","Value":{},"Domain":{}},"Gain":1.0,"Pan":[1.0,1.0],"Propagate":true},"Processes":[{"uuid":"d2a67bd8-5d3f-404e-b6e9-e350cf2a833f","ObjectName":"Automation","id":1,"Metadata":{"ScriptingName":"Automation (float).1","Comment":"","Color":"Transparent1","Label":"","Touched":false},"Duration":1294469470,"Height":300.0,"StartOffset":0,"LoopDuration":1294469470,"Pos":[40.0,40.0],"Size":[200.0,100.0],"Loops":false,"Outlet":{"uuid":"047e4cc2-4d99-4e8b-bf98-206018d02274","ObjectName":"Outlet","id":0,"Hidden":false,"Custom":"Out","Exposed":"out","Address":"OSC:/foo","MinInlet":{"uuid":"af2b4fc3-aecb-4c15-a5aa-1c573a239925","ObjectName":"Inlet","id":0,"Hidden":false,"Custom":"Control","Exposed":"control","Value":{"Float":0.0},"Domain":{}},"MaxInlet":{"uuid":"af2b4fc3-aecb-4c15-a5aa-1c573a239925","ObjectName":"Inlet","id":1,"Hidden":false,"Custom":"Control","Exposed":"control","Value":{"Float":1.0},"Domain":{}}},"Curve":{"ObjectName":"CurveModel","id":45345,"Segments":[{"uuid":"1e7cb83f-4e47-4b14-814d-2242a9c75991","ObjectName":"CurveSegmentModel","id":1267710395,"Previous":null,"Following":1,"Start":[0.0,0.0],"End":[0.18604651162790695,0.78],"Power":1.0},{"uuid":"1e7cb83f-4e47-4b14-814d-2242a9c75991","ObjectName":"CurveSegmentModel","id":1,"Previous":1267710395,"Following":null,"Start":[0.18604651162790695,0.78],"End":[1.0,0.0],"Power":1.0}]},"Tween":false}],"SmallViewRack":[{"Processes":[1],"Process":1,"Height":50.0,"Nodal":false}],"FullViewRack":[{"Process":1,"Nodal":false}],"DefaultDuration":1294469470,"MinDuration":1294469470,"MaxDuration":1294469470,"GuiDuration":1423916417,"Speed":1.0,"Rigidity":true,"MinNull":false,"MaxInf":false,"Signatures":[],"StartState":1,"EndState":2,"StartDate":0,"HeightPercentage":0.06,"NodalSlotHeight":100.0,"QuantizationRate":-1.0,"Zoom":-1.0,"Center":0,"ViewMode":0,"SmallViewShown":true,"HasSignature":false},{"ObjectName":"Scenario::IntervalModel","id":2,"Metadata":{"ScriptingName":"Interval.hadn51","Comment":"","Color":"Transparent1","Label":"","Touched":true},"Inlet":{"uuid":"a1574bb0-cbd4-4c7d-9417-0c25cfd1187b","ObjectName":"Inlet","id":0,"Hidden":false,"Custom":"Audio in","Exposed":"audio in"},"Outlet":{"uuid":"a1d97535-18ac-444a-8417-0cbc1692d897","ObjectName":"Outlet","id":0,"Hidden":false,"Custom":"Audio out","Exposed":"audio out","GainInlet":{"uuid":"9a13fb32-269a-47bf-99a9-930188c1f19c","ObjectName":"Inlet","id":10000,"Hidden":false,"Custom":"Gain","Exposed":"gain","Value":{},"Domain":{"Float":{"Min":0.0,"Max":1.0}}},"PanInlet":{"uuid":"9a13fb32-269a-47bf-99a9-930188c1f19c","ObjectName":"Inlet","id":10001,"Hidden":false,"Custom":"Pan","Exposed":"pan","Value":{},"Domain":{}},"Gain":1.0,"Pan":[1.0,1.0],"Propagate":true},"Processes":[{"uuid":"25c64b87-a44a-4fed-9f60-0a48906fd3ec","ObjectName":"Process::ProcessModel","id":3,"Metadata":{"ScriptingName":"Micromap","Comment":"","Color":"Transparent1","Label":"","Touched":true},"Duration":2205113692,"Height":300.0,"StartOffset":0,"LoopDuration":2205113692,"Pos":[325.9815699610441,-33.55802048110627],"Size":[115.0,23.0],"Loops":false,"Inlets":[{"uuid":"769dd38a-bfb3-4dc6-b52a-b6abb7afe2a3","ObjectName":"Inlet","id":0,"Hidden":false,"Custom":"in","Exposed":"in","Address":"OSC:/toto"},{"uuid":"9ae797ea-d94c-4792-acec-9ec1932bae5d","ObjectName":"Inlet","id":1,"Hidden":true,"Custom":"Expression","Exposed":"expression","Value":{"String":"x / 127"},"Domain":{}}],"Outlets":[{"uuid":"cff96158-cc72-46d7-99dc-b6038171375b","ObjectName":"Outlet","id":0,"Hidden":false,"Custom":"out","Exposed":"out"}]},{"uuid":"1e17e479-3513-44c8-a8a7-017be9f6ac8a","ObjectName":"Process::ProcessModel","id":2,"Metadata":{"ScriptingName":"LFO","Comment":"","Color":"Transparent1","Label":"","Touched":true},"Duration":2205113692,"Height":300.0,"StartOffset":0,"LoopDuration":2205113692,"Pos":[280.0,40.0],"Size":[330.0,130.0],"Loops":false,"Inlets":[{"uuid":"5554eb67-bcc8-45ab-8ec2-37a3f191aa64","ObjectName":"Inlet","id":0,"Hidden":true,"Custom":"Frequency","Exposed":"frequency","Value":{"Float":1.0},"Domain":{"Float":{"Min":0.009999999776482582,"Max":100.0}}},{"uuid":"82427d27-084a-4ab6-9c4e-db83929a1200","ObjectName":"Inlet","id":1,"Hidden":true,"Custom":"Ampl.","Exposed":"ampl.","Value":{"Float":0.5},"Domain":{"Float":{"Min":0.0,"Max":2.0}}},{"uuid":"82427d27-084a-4ab6-9c4e-db83929a1200","ObjectName":"Inlet","id":2,"Hidden":true,"Custom":"Offset","Exposed":"offset","Value":{"Float":0.5},"Domain":{"Float":{"Min":-1.0,"Max":1.0}}},{"uuid":"82427d27-084a-4ab6-9c4e-db83929a1200","ObjectName":"Inlet","id":3,"Hidden":true,"Custom":"Jitter","Exposed":"jitter","Value":{"Float":0.0},"Domain":{"Float":{"Min":0.0,"Max":1.0}}},{"uuid":"82427d27-084a-4ab6-9c4e-db83929a1200","ObjectName":"Inlet","id":4,"Hidden":true,"Custom":"Phase","Exposed":"phase","Value":{"Float":0.0},"Domain":{"Float":{"Min":-1.0,"Max":1.0}}},{"uuid":"8b1d76c4-3838-4ac0-9b9c-c12bc5db8e8a","ObjectName":"Inlet","id":5,"Hidden":true,"Custom":"Waveform","Exposed":"waveform","Value":{"String":"Sin"},"Domain":{"String":{"Values":["Sin","Triangle","Saw","Square","Sample & Hold","Noise 1","Noise 2","Noise 3"]}},"Values":["Sin","Triangle","Saw","Square","Sample & Hold","Noise 1","Noise 2","Noise 3"],"Pixmaps":[":/icons/wave_sin_off.png",":/icons/wave_sin_on.png",":/icons/wave_triangle_off.png",":/icons/wave_triangle_on.png",":/icons/wave_saw_off.png",":/icons/wave_saw_on.png",":/icons/wave_square_off.png",":/icons/wave_square_on.png",":/icons/wave_sample_and_hold_off.png",":/icons/wave_sample_and_hold_on.png",":/icons/wave_noise1_off.png",":/icons/wave_noise1_on.png",":/icons/wave_noise2_off.png",":/icons/wave_noise2_on.png",":/icons/wave_noise3_off.png",":/icons/wave_noise3_on.png"]},{"uuid":"485680cc-b8b9-4a01-acc7-3e8334bdc016","ObjectName":"Inlet","id":6,"Hidden":true,"Custom":"Quantification","Exposed":"quantification","Value":{"Float":0.5},"Domain":{"Float":{"Values":[0.0,1.0,0.5,0.25,0.125,0.0625,0.03125,0.015625,0.75,0.375,0.1875,0.09375,0.046875]}},"Values":[["None",{"Float":0.0}],["Whole",{"Float":1.0}],["Half",{"Float":0.5}],["4th",{"Float":0.25}],["8th",{"Float":0.125}],["16th",{"Float":0.0625}],["32th",{"Float":0.03125}],["64th",{"Float":0.015625}],["Dotted Half",{"Float":0.75}],["Dotted 4th",{"Float":0.375}],["Dotted 8th",{"Float":0.1875}],["Dotted 16th",{"Float":0.09375}],["Dotted 32th",{"Float":0.046875}]]}],"Outlets":[{"uuid":"cff96158-cc72-46d7-99dc-b6038171375b","ObjectName":"Outlet","id":0,"Hidden":false,"Custom":"out","Exposed":"out","Address":"OSC:/bar"}]},{"uuid":"d2a67bd8-5d3f-404e-b6e9-e350cf2a833f","ObjectName":"Automation","id":1,"Metadata":{"ScriptingName":"Automation (float).1","Comment":"","Color":"Transparent1","Label":"","Touched":false},"Duration":2205113692,"Height":300.0,"StartOffset":0,"LoopDuration":2205113692,"Pos":[40.0,40.0],"Size":[200.0,100.0],"Loops":false,"Outlet":{"uuid":"047e4cc2-4d99-4e8b-bf98-206018d02274","ObjectName":"Outlet","id":0,"Hidden":false,"Custom":"Out","Exposed":"out","MinInlet":{"uuid":"af2b4fc3-aecb-4c15-a5aa-1c573a239925","ObjectName":"Inlet","id":0,"Hidden":false,"Custom":"Control","Exposed":"control","Value":{"Float":0.0},"Domain":{}},"MaxInlet":{"uuid":"af2b4fc3-aecb-4c15-a5aa-1c573a239925","ObjectName":"Inlet","id":1,"Hidden":false,"Custom":"Control","Exposed":"control","Value":{"Float":1.0},"Domain":{}}},"Curve":{"ObjectName":"CurveModel","id":45345,"Segments":[{"uuid":"1e7cb83f-4e47-4b14-814d-2242a9c75991","ObjectName":"CurveSegmentModel","id":-2128885755,"Previous":null,"Following":1,"Start":[0.0,0.0],"End":[0.8737201365187713,0.78],"Power":1.0},{"uuid":"1e7cb83f-4e47-4b14-814d-2242a9c75991","ObjectName":"CurveSegmentModel","id":1,"Previous":-2128885755,"Following":null,"Start":[0.8737201365187713,0.78],"End":[1.0,0.0],"Power":1.0}]},"Tween":false}],"SmallViewRack":[{"Processes":[1],"Process":1,"Height":50.0,"Nodal":false},{"Processes":[2,3],"Process":null,"Height":389.0,"Nodal":true}],"FullViewRack":[{"Process":1,"Nodal":false},{"Process":0,"Nodal":true}],"DefaultDuration":2205113692,"MinDuration":2205113692,"MaxDuration":2205113692,"GuiDuration":2425625061,"Speed":1.0,"Rigidity":true,"MinNull":false,"MaxInf":false,"Signatures":[],"StartState":2,"EndState":3,"StartDate":1294469470,"HeightPercentage":0.06,"NodalSlotHeight":100.0,"QuantizationRate":-1.0,"Zoom":-1.0,"Center":0,"ViewMode":0,"SmallViewShown":true,"HasSignature":false}],"Comments":[]}],"SmallViewRack":[],"FullViewRack":[{"Process":1,"Nodal":false}],"DefaultDuration":10584000000,"MinDuration":10584000000,"MaxDuration":11113200000,"GuiDuration":11642400000,"Speed":1.0,"Rigidity":false,"MinNull":false,"MaxInf":true,"Signatures":[[0,[4,4]]],"StartState":0,"EndState":1,"StartDate":0,"HeightPercentage":0.0,"NodalSlotHeight":100.0,"QuantizationRate":-1.0,"Zoom":15120000.0,"Center":0,"ViewMode":0,"SmallViewShown":false,"HasSignature":true},"StartTimeNode":{"ObjectName":"Scenario::TimeSyncModel","id":0,"Metadata":{"ScriptingName":"Sync.start","Comment":"","Color":"Gray","Label":"","Touched":true},"Date":0,"Events":[0],"MusicalSync":-1.0,"AutoTrigger":false,"Start":true,"Active":false,"Expression":" { true == false } "},"EndTimeNode":{"ObjectName":"Scenario::TimeSyncModel","id":1,"Metadata":{"ScriptingName":"Sync.end","Comment":"","Color":"Gray","Label":"","Touched":true},"Date":10584000000,"Events":[1],"MusicalSync":-1.0,"AutoTrigger":false,"Start":false,"Active":true,"Expression":" { true == false } "},"StartEvent":{"ObjectName":"Scenario::EventModel","id":0,"Metadata":{"ScriptingName":"Event.start","Comment":"","Color":"Emphasis4","Label":"","Touched":true},"TimeNode":0,"States":[0],"Condition":"","Date":0,"Offset":0},"EndEvent":{"ObjectName":"Scenario::EventModel","id":1,"Metadata":{"ScriptingName":"Event.end","Comment":"","Color":"Emphasis4","Label":"","Touched":true},"TimeNode":1,"States":[1],"Condition":"","Date":10584000000,"Offset":0},"StartState":{"ObjectName":"Scenario::StateModel","id":0,"Metadata":{"ScriptingName":"State.start","Comment":"","Color":"Base1","Label":"","Touched":true},"Event":0,"PreviousConstraint":null,"NextConstraint":0,"HeightPercentage":0.0,"Messages":{"Name":"","Accessors":[],"Unit":"none","Previous":[],"Following":[],"User":null,"Priorities":[1,2,0]},"Controls":[],"StateProcesses":[]},"EndState":{"ObjectName":"Scenario::StateModel","id":1,"Metadata":{"ScriptingName":"State.end","Comment":"","Color":"Base1","Label":"","Touched":true},"Event":1,"PreviousConstraint":0,"NextConstraint":null,"HeightPercentage":0.0,"Messages":{"Name":"","Accessors":[],"Unit":"none","Previous":[],"Following":[],"User":null,"Priorities":[1,2,0]},"Controls":[],"StateProcesses":[]}},"Speed":1.0,"Cables":[{"ObjectName":"Process::Cable","id":1,"Type":0,"Source":[{"ObjectName":"Scenario::ScenarioDocumentModel","ObjectId":1},{"ObjectName":"Scenario::BaseScenario","ObjectId":0},{"ObjectName":"Scenario::IntervalModel","ObjectId":0},{"ObjectName":"Scenario","ObjectId":1},{"ObjectName":"Scenario::IntervalModel","ObjectId":2},{"ObjectName":"Automation","ObjectId":1},{"ObjectName":"Outlet","ObjectId":0}],"Sink":[{"ObjectName":"Scenario::ScenarioDocumentModel","ObjectId":1},{"ObjectName":"Scenario::BaseScenario","ObjectId":0},{"ObjectName":"Scenario::IntervalModel","ObjectId":0},{"ObjectName":"Scenario","ObjectId":1},{"ObjectName":"Scenario::IntervalModel","ObjectId":2},{"ObjectName":"Process::ProcessModel","ObjectId":2},{"ObjectName":"Inlet","ObjectId":0}]},{"ObjectName":"Process::Cable","id":2,"Type":0,"Source":[{"ObjectName":"Scenario::ScenarioDocumentModel","ObjectId":1},{"ObjectName":"Scenario::BaseScenario","ObjectId":0},{"ObjectName":"Scenario::IntervalModel","ObjectId":0},{"ObjectName":"Scenario","ObjectId":1},{"ObjectName":"Scenario::IntervalModel","ObjectId":2},{"ObjectName":"Process::ProcessModel","ObjectId":3},{"ObjectName":"Outlet","ObjectId":0}],"Sink":[{"ObjectName":"Scenario::ScenarioDocumentModel","ObjectId":1},{"ObjectName":"Scenario::BaseScenario","ObjectId":0},{"ObjectName":"Scenario::IntervalModel","ObjectId":0},{"ObjectName":"Scenario","ObjectId":1},{"ObjectName":"Scenario::IntervalModel","ObjectId":2},{"ObjectName":"Process::ProcessModel","ObjectId":2},{"ObjectName":"Inlet","ObjectId":1}]}],"BusIntervals":[]},"Plugins":[{"uuid":"1f923578-08c3-49be-9ba9-69c144ee2e32","Refresh":false,"Reconnect":false,"MidiRatio":1.0},{"uuid":"6e610e1f-9de2-4c36-90dd-0ef570002a21","RootNode":{},"Children":[{"Device":{"Name":"OSC","Protocol":"9a42de4b-f6eb-4bca-9564-01b975f601b9","Config":{"Mode":1,"Version":0,"Framing":1,"Transport":{"UDP":{"Local":{"Host":"0.0.0.0","Port":9997},"Remote":{"Host":"127.0.0.1","Port":9996}}}}},"Children":[{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Float":0.0},"Domain":{"Float":{"Min":0.0,"Max":1.0}},"Name":"foo"}}]}]}],"Version":4,"Commit":"ef19aa8e705dc664c93ca45257788893afa8dabe","Tag":"3.1.11"} \ No newline at end of file diff --git a/templates/desktop/x.so b/templates/desktop/x.so new file mode 100755 index 0000000000000000000000000000000000000000..4d93ced8491f5bb4db012a09b5f1f8d992369bf4 GIT binary patch literal 120872 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ýKO8r6og&L(vy%hvE)LTnWaVw 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 b+FcoC)w|!eSJZ+}d+qPcjpW%c???Y%sPpxT literal 0 HcmV?d00001 diff --git a/templates/ossia-device/constants.hpp b/templates/ossia-device/constants.hpp new file mode 100644 index 0000000..112572b --- /dev/null +++ b/templates/ossia-device/constants.hpp @@ -0,0 +1,7 @@ +#pragma once + +static constexpr auto wifi_ssid = "Tenda"; +static constexpr auto wifi_pass = "adminadmin"; +static constexpr auto local_osc_udp_port = 5567; +static constexpr auto remote_osc_udp_host = "192.168.2.104"; +static constexpr auto remote_osc_udp_port = 7765; diff --git a/templates/ossia-device/impl-manual.hpp b/templates/ossia-device/impl-manual.hpp new file mode 100644 index 0000000..deaeee7 --- /dev/null +++ b/templates/ossia-device/impl-manual.hpp @@ -0,0 +1,74 @@ +#pragma once +#include "ossia_embedded_api.hpp" +#include "constants.hpp" +#include "utility.hpp" +#include +#include + + +DacESP32 ossia_dac(DAC_CHANNEL_1); + +WiFiUDP ossia_udp_socket; +OSCBundle ossia_osc_bundle; + +struct ossia_data_model +{ + // Read + float sensor{}; + + // Write + float led{}; +} ossia_model; + +void ossia_init_board() +{ + // UDP + { Initialize _("UDP"); + ossia_udp_socket.begin(local_osc_udp_port); + } +} + +void ossia_read_pins() +{ + model.sensor = analogRead(36) / 4095.f; +} + +void ossia_write_pins() +{ + ossia_dac.outputVoltage(ossia_model.led * 3.3f); +} + +void ossia_process() +{ + model.led = model.sensor; +} + +void ossia_read_osc() +{ + OSCMessage inmsg; + int size = ossia_udp_socket.parsePacket(); + if(size <= 0) + return; + + while(size--) + inmsg.fill(ossia_udp_socket.read()); + + if(inmsg.hasError()) + return; + +#define read_variable(addr, var) \ + inmsg.dispatch(addr, [](OSCMessage& msg) { var = get_float(msg); }); + + read_variable("/led", ossia_model.led); +} + + +void ossia_write_osc() +{ + ossia_osc_bundle.add("/sensor").add(ossia_model.sensor); + ossia_udp_socket.beginPacket(remote_osc_udp_host, remote_osc_udp_port); + ossia_osc_bundle.send(ossia_udp_socket); + ossia_udp_socket.endPacket(); + //Udp.flush(); + ossia_osc_bundle.empty(); +} diff --git a/templates/ossia-device/impl-ossia.hpp b/templates/ossia-device/impl-ossia.hpp new file mode 100644 index 0000000..013f73f --- /dev/null +++ b/templates/ossia-device/impl-ossia.hpp @@ -0,0 +1,71 @@ +#pragma once +#include "ossia_embedded_api.hpp" +#include "constants.hpp" +#include "utility.hpp" + +#include + +#include +#include +#include + +WiFiUDP ossia_udp_socket; +OSCBundle ossia_osc_bundle; + +// Global variables +DacESP32 ossia_dac_1(DAC_CHANNEL_1); + +struct ossia_data_model +{ +float v_led1_A; +bool v_pot1_SIG; + +} ossia_model; + +void ossia_init_board() { + + // UDP + { Initialize _("UDP"); + ossia_udp_socket.begin(local_osc_udp_port); + } + + // Per-device init + +} + +void ossia_read_osc() { + OSCMessage inmsg; + int size = ossia_udp_socket.parsePacket(); + if(size <= 0) + return; + + while(size--) + inmsg.fill(ossia_udp_socket.read()); + + if(inmsg.hasError()) + return; + + inmsg.dispatch("/led1/A", [](OSCMessage& msg) { ossia_model.v_led1_A = msg.getFloat(0); }); + +} + +void ossia_read_pins() { + ossia_model.v_pot1_SIG = digitalRead(0); + +} + +void ossia_write_osc() { + ossia_osc_bundle.add("/pot1/SIG").add(ossia_model.v_pot1_SIG); + + + ossia_udp_socket.beginPacket(remote_osc_udp_host, remote_osc_udp_port); + ossia_osc_bundle.send(ossia_udp_socket); + ossia_udp_socket.endPacket(); + //Udp.flush(); + ossia_osc_bundle.empty(); +} + +void ossia_write_pins() { + ossia_dac_1.outputVoltage(ossia_model.v_led1_A * 3.3f); + +} diff --git a/templates/ossia-device/ossia-device-test.ino b/templates/ossia-device/ossia-device-test.ino new file mode 100644 index 0000000..5cc8999 --- /dev/null +++ b/templates/ossia-device/ossia-device-test.ino @@ -0,0 +1,46 @@ + +#include "ossia_embedded_api.hpp" +#include "impl-ossia.hpp" +#include "utility.hpp" +#include "constants.hpp" + +#include + +void setup() { + Serial.setTimeout(10); + Serial.begin(9600); + + { Initialize _("Wi-Fi"); + // Wi-Fi + WiFi.begin(wifi_ssid, wifi_pass); + int wifi_retries_count = 40; + while(WiFi.status() != WL_CONNECTED) + { + delay(300); + if(wifi_retries_count-- == 0) + { + _.error(); + return; + } + } + } + + Serial.println(WiFi.localIP()); + + ossia_init_board(); +} + + +void loop() { + +ossia_read_pins(); +ossia_read_osc(); + +// ossia_process(); + +// do custom processing here. + +ossia_write_pins(); +ossia_write_osc(); + +} diff --git a/templates/ossia-device/utility.hpp b/templates/ossia-device/utility.hpp new file mode 100644 index 0000000..a2afca7 --- /dev/null +++ b/templates/ossia-device/utility.hpp @@ -0,0 +1,35 @@ +#pragma once +#include +#include + +// To display boot text +struct Initialize { + bool m_error = false; + explicit Initialize(const char* arg) { + Serial.print("Initialize "); + Serial.print(arg); + } + + void error() { m_error = true; } + + ~Initialize() + { + if(m_error) + Serial.println(" ... KO!"); + else + Serial.println(" ... OK!"); + } +}; + + +float get_float(OSCMessage& msg) +{ + if(msg.isFloat(0)) + return msg.getFloat(0); + else if(msg.isInt(0)) + return msg.getInt(0); + else if(msg.isBoolean(0)) + return msg.getBoolean(0) ? 1.f : 0.f; + else + return -10.; +} \ No newline at end of file diff --git a/templates/teensy-minimal/.gitignore b/templates/teensy-minimal/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/templates/teensy-minimal/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/templates/teensy-minimal/include/README b/templates/teensy-minimal/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/templates/teensy-minimal/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/templates/teensy-minimal/lib/README b/templates/teensy-minimal/lib/README new file mode 100644 index 0000000..2593a33 --- /dev/null +++ b/templates/teensy-minimal/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/templates/teensy-minimal/platformio.ini b/templates/teensy-minimal/platformio.ini new file mode 100644 index 0000000..f5256a8 --- /dev/null +++ b/templates/teensy-minimal/platformio.ini @@ -0,0 +1,46 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:teensy31] +platform = teensy +board = teensy31 +framework = arduino +build_flags = -std=gnu++2a -fconcepts -DWIFI -Wno-volatile + -I /home/jcelerier/ossia/score/src/addons/score-addon-pico/templates/common + -I /home/jcelerier/ossia/score/3rdparty/avendish/include + -I /home/jcelerier/ossia/score/3rdparty/avendish/examples + -I /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/boost_1_87_0 + -I /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/rnd/include + -I /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/dno/include + -I /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/tuplet/include + -I /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/span/includede + -I /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/mdspan/include + -I /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/magic_enum/include + -I /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/rnd/include + -I /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/unordered_dense/include + -I /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/span/include + -I /home/jcelerier/ossia/score/src/plugins/score-plugin-fx + -I /home/jcelerier/ossia/score/3rdparty/libossia/src + -I /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/libremidi/include +build_unflags = -std=gnu++11 -std=gnu++14 -std=gnu++17 +; +; [env:tinypico] +; platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.03/platform-espressif32.zip +; board = tinypico +; framework = arduino +; monitor_speed = 115200 +; build_flags = -std=gnu++2a -fconcepts -DWIFI +; build_unflags = -std=gnu++11 -std=gnu++14 -std=gnu++17 +; lib_deps = +; https://github.com/guillaumeriousat/ESP32-ESP32S2-AnalogWrite +; BluetoothSerial +; networking-for-arduino/EthernetESP32@^1.0.2 +; cnmat/OSC@^1.0.0 +; me-no-dev/ESP Async WebServer@^1.2.4 diff --git a/templates/teensy-minimal/src/main.cpp b/templates/teensy-minimal/src/main.cpp new file mode 100644 index 0000000..48e66fe --- /dev/null +++ b/templates/teensy-minimal/src/main.cpp @@ -0,0 +1,24 @@ +#include "PolySynth.hpp" +#include "ossia_embedded_api.hpp" +#include "pico.ossia-graph.generated.hpp" + +#include + +#include +void setup() +{ + ossia_init_board(); +} + +void loop() +{ + ossia_read_pins(); + ossia_read_net(); + + ossia_run_graph(); + + ossia_write_pins(); + ossia_write_net(); + + delay(100); +} diff --git a/templates/teensy-minimal/test/README b/templates/teensy-minimal/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/templates/teensy-minimal/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html