diff --git a/.gitignore b/.gitignore index 54dcf66..8396632 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ react/package-lock.json src/dbus/bluez_adaptor.h src/dbus/bluez_proxy.h + +CMakeFiles \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index d7afa68..a0f3d56 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -5,13 +5,14 @@ "includePath": [ "${default}", "${workspaceFolder}", - "${workspaceFolder}/src", + "${workspaceFolder}/build/src/**", + "${workspaceFolder}/src/**", + "${workspaceFolder}/**", "/usr/include/lilv-0", "/usr/include/x86_64-linux-gnu", - "/usr/lib", - "~/src/vst3sdk" + "/usr/lib" ], - "compilerPath": "/usr/bin/gcc", + "compilerPath": "/usr/bin/gcc-12", "cStandard": "c17", "cppStandard": "c++20", "intelliSenseMode": "linux-gcc-arm64", diff --git a/.vscode/launch.json b/.vscode/launch.json index 6e6cf77..4b516f5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -21,7 +21,9 @@ "request": "launch", // Resolved by CMake Tools: "program": "${command:cmake.launchTargetPath}", - "args": [ ], + "args": [ + "--no-sudo" + ], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [ @@ -138,7 +140,9 @@ //"[inverting_mutex_test]" // "[utf8_to_utf32]" //"[pipedal_alsa_test]" - "[wifi_channels_test]" + // "[wifi_channels_test]" + // "[locale]" + "[modFileTypes]" ], "stopAtEntry": false, @@ -266,6 +270,48 @@ } ] }, + { + "name": "(gdb) pipedalconfigX", + "type": "cppdbg", + "request": "launch", + // Resolved by CMake Tools: + "program": "/home/robin/src/pipedal/build/src/pipedalconfig", + "args": [ + "--nosudo", // run without sudo (which will fail because of perms, but allow us to debug deeper into install code.) + //"--get-current-port" + // "--uninstall" + //"--enable-p2p" , "CA", "PiPedalTest","12345678","14" + // "--alsa-devices" + "--prefix", "/usr", + "--install", "--port", "0" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { + "name": "PATH", + "value": "$PATH:${command:cmake.launchTargetDirectory}" + }, + { + "name": "ASAN_OPTIONS", + "value": "detect_leaks=0" + + }, + { + "name": "OTHER_VALUE", + "value": "Something something" + } + ], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, { @@ -312,46 +358,6 @@ } ] }, - { - "name": "(gdb) pipedalconfig", - "type": "cppdbg", - "request": "launch", - // Resolved by CMake Tools: - "program": "${command:cmake.launchTargetPath}", - "args": [ - "--nosudo", // run without sudo (which will fail because of perms, but allow us to debug deeper into install code.) - //"--get-current-port" - // "--uninstall" - //"--enable-p2p" , "CA", "PiPedalTest","12345678","14" - "--alsa-devices" - ], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [ - { - "name": "PATH", - "value": "$PATH:${command:cmake.launchTargetDirectory}" - }, - { - "name": "ASAN_OPTIONS", - "value": "detect_leaks=0" - - }, - { - "name": "OTHER_VALUE", - "value": "Something something" - } - ], - "externalConsole": false, - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - }, { "name": "(gdb) pipedal_alsa_check", "type": "cppdbg", diff --git a/.vscode/settings.json b/.vscode/settings.json index b305b9b..8e97e94 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -98,17 +98,41 @@ "p2p_i.h": "c", "p2p.h": "c", "hash_set": "cpp", - "barrier": "cpp" + "barrier": "cpp", + "format": "cpp", + "locale": "cpp", + "stdfloat": "cpp", + "text_encoding": "cpp" }, "cSpell.words": [ + "Alsa", + "flac", "Guitarix", "hostapd", + "Hotspot", + "invada", + "jmap", + "Lilv", + "midiclip", + "midisong", + "mlmodel", + "Pedalboard", "pipedal", "pipedal_0", "pipedalconfig", "pipedald", "pipedalshutdownd", "pipedaltest", - "ToobAmp" - ] + "pipedalui", + "rdfs", + "ToobAmp", + "urids", + "Writables", + "xruns" + ], + "C_Cpp.default.compilerPath": "/usr/bin/g++-13", + "cSpell.ignoreWords": [ + "nammodel" + ], + "cSpell.enabled": false } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e493246..7c637f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,39 +1,30 @@ cmake_minimum_required(VERSION 3.16.0) project(pipedal - VERSION 1.3.69 + VERSION 1.4.70 DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi" HOMEPAGE_URL "https://rerdavies.github.io/pipedal" ) EXECUTE_PROCESS( COMMAND dpkg --print-architecture COMMAND tr -d '\n' OUTPUT_VARIABLE DEBIAN_ARCHITECTURE ) -set (DISPLAY_VERSION "PiPedal v1.3.69-Release") +set (DISPLAY_VERSION "PiPedal v1.4.70-Beta") set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE}) set (CMAKE_INSTALL_PREFIX "/usr/") include(CTest) enable_testing() + #add_subdirectory("submodules/pipedal_p2pd") add_subdirectory("PiPedalCommon") -add_subdirectory("react") +add_subdirectory("vite") add_subdirectory("src") -# Replaced with hotspot. -#add_subdirectory("NetworkManagerP2P") -# install (TARGETS pipedal_p2pd DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin -# ) - -# install (TARGETS pipedal_nm_p2pd DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin -# ) - -# add_test(NAME Test COMMAND piddletest) - -set (REACT_BUILD_DIRECTORY ${PROJECT_SOURCE_DIR}/build/react/build/) +set (VITE_BUILD_DIRECTORY ${PROJECT_SOURCE_DIR}/vite/dist/) # install @@ -51,7 +42,7 @@ install ( FILES ${PROJECT_SOURCE_DIR}/src/polkit-1/rules/10-pipedal-networkmanager.rules DESTINATION /etc/polkit-1/rules.d/ ) install ( - DIRECTORY ${REACT_BUILD_DIRECTORY} DESTINATION /etc/pipedal/react + DIRECTORY ${VITE_BUILD_DIRECTORY} DESTINATION /etc/pipedal/react ) install (FILES ${PROJECT_SOURCE_DIR}/build/src/notices.txt DESTINATION /etc/pipedal/react/var/) @@ -98,7 +89,7 @@ endif() set(CPACK_GENERATOR "DEB") set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Robin E. R. Davies") # required -set(CPACK_PACKAGE_VENDOR "Robin Davies") +set(CPACK_PACKAGE_VENDOR "Robin E. R. Davies") set(CPACK_PACKAGE_DESCRIPTION "IoT guitar effect pedal for Raspberry Pi, with phone-friendly web interface.") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "IoT guitar effect pedal for Raspberry Pi") set(CPACK_DEBIAN_PACKAGE_SECTION sound) diff --git a/LICENSE.txt b/LICENSE.txt index 1125614..a0619cb 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright 2021-2024 Robin Davies +Copyright 2021-2024 Robin E. R. Davies Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/NetworkManagerP2P/src/main.cpp b/NetworkManagerP2P/src/main.cpp index c03d137..5ac848c 100644 --- a/NetworkManagerP2P/src/main.cpp +++ b/NetworkManagerP2P/src/main.cpp @@ -17,7 +17,7 @@ using namespace std; static void PrintHelp() { cout << "nm-pipedal-p2p - P2P Manager for NetworkManager network stack." << endl - << "Copyright (c) 2024 Robin Davies." << endl + << "Copyright (c) 2024 Robin E. R. Davies." << endl << endl << "Provides P2P connections on LINUX distributions that use NetworkManager." << endl << endl diff --git a/NetworkManagerP2P/src/ss.hpp b/NetworkManagerP2P/src/ss.hpp index 6b34c10..af6ce2e 100644 --- a/NetworkManagerP2P/src/ss.hpp +++ b/NetworkManagerP2P/src/ss.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Robin Davies +// Copyright (c) 2022 Robin E. R. Davies // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in diff --git a/PiPedalCommon/src/CMakeLists.txt b/PiPedalCommon/src/CMakeLists.txt index 3926a61..9ab01b6 100644 --- a/PiPedalCommon/src/CMakeLists.txt +++ b/PiPedalCommon/src/CMakeLists.txt @@ -57,6 +57,7 @@ add_library(PiPedalCommon STATIC include/dbus/org.freedesktop.NetworkManager.Device.Wireless.hpp include/dbus/org.freedesktop.NetworkManager.DHCP4Config.hpp + Utf8Utils.cpp include/Utf8Utils.hpp NetworkManagerInterfaces.cpp include/NetworkManagerInterfaces.hpp Lv2Log.cpp include/Lv2Log.hpp diff --git a/PiPedalCommon/src/HtmlHelper.cpp b/PiPedalCommon/src/HtmlHelper.cpp index ac7c218..9944bd5 100644 --- a/PiPedalCommon/src/HtmlHelper.cpp +++ b/PiPedalCommon/src/HtmlHelper.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Robin Davies +// Copyright (c) 2022 Robin E. R. Davies // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in diff --git a/PiPedalCommon/src/Lv2Log.cpp b/PiPedalCommon/src/Lv2Log.cpp index 7b43bec..686f1c4 100644 --- a/PiPedalCommon/src/Lv2Log.cpp +++ b/PiPedalCommon/src/Lv2Log.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Robin Davies +// Copyright (c) 2022 Robin E. R. Davies // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in diff --git a/PiPedalCommon/src/NetLinkChannelInfo.cpp b/PiPedalCommon/src/NetLinkChannelInfo.cpp index 7d24513..a88d343 100644 --- a/PiPedalCommon/src/NetLinkChannelInfo.cpp +++ b/PiPedalCommon/src/NetLinkChannelInfo.cpp @@ -3,7 +3,7 @@ Copyright (c) 2007, 2008 Johannes Berg Copyright (c) 2007 Andy Lutomirski Copyright (c) 2007 Mike Kershaw Copyright (c) 2008 Luis R. Rodriguez -Copyright (c) 2024 Robin Davies +Copyright (c) 2024 Robin E. R. Davies All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/PiPedalCommon/src/NetLinkChannelInfo.cpp.bak b/PiPedalCommon/src/NetLinkChannelInfo.cpp.bak index 0012cd6..8d0a8af 100644 --- a/PiPedalCommon/src/NetLinkChannelInfo.cpp.bak +++ b/PiPedalCommon/src/NetLinkChannelInfo.cpp.bak @@ -3,7 +3,7 @@ Copyright (c) 2007, 2008 Johannes Berg Copyright (c) 2007 Andy Lutomirski Copyright (c) 2007 Mike Kershaw Copyright (c) 2008 Luis R. Rodriguez -Copyright (c) 2024 Robin Davies +Copyright (c) 2024 Robin E. R. Davies All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/PiPedalCommon/src/SysExec.cpp b/PiPedalCommon/src/SysExec.cpp index 8506b34..2244cf3 100644 --- a/PiPedalCommon/src/SysExec.cpp +++ b/PiPedalCommon/src/SysExec.cpp @@ -306,3 +306,37 @@ SysExecOutput pipedal::sysExecForOutput(const std::string &program, const std::s } } + + +int pipedal::SudoExec(int argc, char **argv) +{ + // re-execute with SUDO in order to prompt for SUDO credentials once only. + std::vector args; + std::string pkexec = "/usr/bin/sudo"; // staged because "ISO C++ forbids converting a string constant to std::vector::value_type"(!) + args.push_back((char *)(pkexec.c_str())); + + std::string sPath = getSelfExePath(); + args.push_back(const_cast(sPath.c_str())); + for (int arg = 1; arg < argc; ++arg) + { + args.push_back(const_cast(argv[arg])); + } + + args.push_back(nullptr); + + char **newArgs = &args[0]; + + int pbPid; + int returnValue = 0; + + if ((pbPid = fork()) == 0) + { + return execv(newArgs[0], newArgs); + } + else + { + waitpid(pbPid, &returnValue, 0); + int exitStatus = WEXITSTATUS(returnValue); + return exitStatus; + } +} diff --git a/PiPedalCommon/src/Utf8Utils.cpp b/PiPedalCommon/src/Utf8Utils.cpp new file mode 100644 index 0000000..f9f85b9 --- /dev/null +++ b/PiPedalCommon/src/Utf8Utils.cpp @@ -0,0 +1,340 @@ +// Copyright (c) 2023 Robin E. R. Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#include "Utf8Utils.hpp" +#include "stdexcept" +#include + +using namespace std; +namespace pipedal +{ + + + static constexpr uint16_t UTF16_SURROGATE_TAG_MASK = 0xFC00; + static constexpr uint16_t UTF16_SURROGATE_1_BASE = 0xD800U; + static constexpr uint16_t UTF16_SURROGATE_2_BASE = 0xDC00U; + + + static constexpr uint32_t UTF8_ONE_BYTE_MASK = 0x80; + static constexpr uint32_t UTF8_ONE_BYTE_BITS = 0; + static constexpr uint32_t UTF8_TWO_BYTES_MASK = 0xE0; + static constexpr uint32_t UTF8_TWO_BYTES_BITS = 0xC0; + static constexpr uint32_t UTF8_THREE_BYTES_MASK = 0xF0; + static constexpr uint32_t UTF8_THREE_BYTES_BITS = 0xE0; + static constexpr uint32_t UTF8_FOUR_BYTES_MASK = 0xF8; + static constexpr uint32_t UTF8_FOUR_BYTES_BITS = 0xF0; + static constexpr uint32_t UTF8_CONTINUATION_MASK = 0xC0; + static constexpr uint32_t UTF8_CONTINUATION_BITS = 0x80; + static constexpr bool enforceValidUtf8Encoding = false; + + namespace implementation + { + void Utf8RangeError() + { + throw std::range_error("String index out of range."); + } + } + size_t Utf8Index(size_t size, const std::string &text) + { + size_t index = 0; + for (size_t i = 0; i < size; ++i) + { + index = Utf8Increment(index, text); + if (index >= text.size()) + break; + } + return index; + } + size_t Utf8Length(const std::string &text) + { + size_t index = 0; + size_t length = 0; + while (index < text.length()) + { + index = Utf8Increment(index, text); + ++length; + } + return length; + } + size_t Utf8Increment(size_t size, const std::string &text) + { + if (size >= text.length()) + { + implementation::Utf8RangeError(); + } + uint8_t uc = (uint8_t)text[size]; + if (uc < 0x80U) + { + return size + 1; + } + else + { + ++size; + while (size < text.size() && (((uint8_t)text[size]) & 0xC0) == 0x80) + { + ++size; + } + return size; + } + } + size_t Utf8Decrement(size_t size, const std::string &text) + { + if (size == 0) + { + implementation::Utf8RangeError(); + } + uint8_t c = text[size - 1]; + if (c < 0x80U) + { + return size - 1; + } + else + { + while ((uint8_t)(text[size - 1] & 0xC0) == 0x80) + { + --size; + } + if (size != 0 && (uint8_t)(text[size - 1]) > 0x80) + { + --size; + } + else + { + implementation::Utf8RangeError(); + } + + return size; + } + } + + std::string Utf8Erase(const std::string &text, size_t start, size_t end) + { + size_t uStart = Utf8Index(start, text); + size_t uEnd = Utf8Index(end, text); + std::string result; + result.reserve(uStart + text.size() - uEnd); + result.append(text.begin(), text.begin() + uStart); + result.append(text.begin() + uEnd, text.end()); + return result; + } + std::string Utf8Substring(const std::string &text, size_t start, size_t end) + { + size_t uStart = Utf8Index(start, text); + size_t uEnd; + if (end == (size_t)-1) + { + uEnd = text.length(); + } + else + { + uEnd = Utf8Index(end, text); + } + return text.substr(uStart, uEnd - uStart); + } + + static char32_t bits(int nBits, int shift, char32_t c) + { + return (c >> shift) & ((1 << nBits) - 1); + } + std::string Utf8FromUtf32(char32_t value) + { + std::stringstream ss; + if (value < 0x7F) + { + ss << (char)value; + } + else if (value <= 0x7FF) + { + ss << (char)(0xC0 + bits(5, 6, value)); + ss << (char)(0x80 + bits(6, 0, value)); + } + else if (value <= 0xFFFFul) + { + ss << (char)(0xE0 + bits(4, 12, value)); + ss << (char)(0x80 + bits(6, 6, value)); + ss << (char)(0x80 + bits(6, 0, value)); + } + else if (value <= 0x10FFFFul) + { + ss << (char)(0xF0 + bits(3, 18, value)); + ss << (char)(0x80 + bits(6, 12, value)); + ss << (char)(0x80 + bits(6, 6, value)); + ss << (char)(0x80 + bits(6, 0, value)); + } + else + { + throw std::runtime_error("Invalid unicode character."); + } + return ss.str(); + } + + std::string Utf16ToUtf8(const std::u16string_view&v) + { + std::stringstream ss; + for (auto i = v.begin(); i != v.end(); /**/) + { + char32_t value = *i++; + if ((value & UTF16_SURROGATE_TAG_MASK) == UTF16_SURROGATE_1_BASE) + { + if (i == v.end()) + { + throw std::runtime_error("Invalid UTF32 character sequence."); + } + char32_t value2 = *i++; + if ((value & UTF16_SURROGATE_TAG_MASK) != UTF16_SURROGATE_2_BASE) + { + throw std::runtime_error("Invalid UTF32 character sequence."); + } + value = (value << 10) + value2; + } + + if (value < 0x7F) + { + ss << (char)value; + } + else if (value <= 0x7FF) + { + ss << (char)(0xC0 + bits(5, 6, value)); + ss << (char)(0x80 + bits(6, 0, value)); + } + else if (value <= 0xFFFFul) + { + ss << (char)(0xE0 + bits(4, 12, value)); + ss << (char)(0x80 + bits(6, 6, value)); + ss << (char)(0x80 + bits(6, 0, value)); + } + else if (value <= 0x10FFFFul) + { + ss << (char)(0xF0 + bits(3, 18, value)); + ss << (char)(0x80 + bits(6, 12, value)); + ss << (char)(0x80 + bits(6, 6, value)); + ss << (char)(0x80 + bits(6, 0, value)); + } + else + { + throw std::runtime_error("Invalid unicode character."); + } + } + return ss.str(); + } + + + [[noreturn]] static void throw_encoding_error() + { + throw std::runtime_error("Invalid UTF8 character."); + } + + static uint32_t continuation_byte(std::string_view::iterator &p, std::string_view::const_iterator end) + { + if (p == end) + throw_encoding_error(); + uint8_t c = *p++; + if ((c & UTF8_CONTINUATION_MASK) != UTF8_CONTINUATION_BITS) + throw_encoding_error(); + return c & 0x3F; + } + + std::u16string Utf8ToUtf16(const std::string_view &v) + { + std::basic_stringstream os; + // convert to utf-32. + // convert utf-32 to normalized utf-16. + // write non-7-bit and unsafe characters as \uHHHH. + + auto p = v.begin(); + while (p != v.end()) + { + uint32_t uc; + uint8_t c = (uint8_t)*p++; + if ((c & UTF8_ONE_BYTE_MASK) == UTF8_ONE_BYTE_BITS) + { + uc = c; + } + else + { + uint32_t c2 = continuation_byte(p, v.end()); + + if ((c & UTF8_TWO_BYTES_MASK) == UTF8_TWO_BYTES_BITS) + { + uint32_t c1 = c & (uint32_t)(~UTF8_TWO_BYTES_MASK); + if (c1 <= 1 && enforceValidUtf8Encoding) + { + // overlong encoding. + throw_encoding_error(); + } + uc = (c1 << 6) | c2; + } + else + { + uint32_t c3 = continuation_byte(p, v.end()); + + if ((c & UTF8_THREE_BYTES_MASK) == UTF8_THREE_BYTES_BITS) + { + uint32_t c1 = c & (uint32_t)~UTF8_THREE_BYTES_MASK; + if (c1 == 0 && c2 < 0x20 && enforceValidUtf8Encoding) + { + // overlong encoding. + throw_encoding_error(); + } + + uc = (c1) << 12 | (c2 << 6) | c3; + } + else + { + uint32_t c4 = continuation_byte(p, v.end()); + if ((c & UTF8_FOUR_BYTES_MASK) == UTF8_FOUR_BYTES_BITS) + { + uint32_t c1 = c & (uint32_t)~UTF8_FOUR_BYTES_MASK; + if (c1 == 0 && c2 < 0x10 && enforceValidUtf8Encoding) + { + // overlong encoding. + throw_encoding_error(); + } + uc = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4; + } + else + { + // outside legal UCS range. + throw_encoding_error(); + } + } + } + } + if (uc < 0x10000ul) + { + os << (char16_t)uc; + } + else + { + // write UTF-16 surrogate pair. + uc -= 0x10000; + + char16_t s1 = (char16_t)(UTF16_SURROGATE_1_BASE + ((uc >> 10) & 0x3FFu)); + char16_t s2 = (char16_t)(UTF16_SURROGATE_2_BASE + (uc & 0x03FFu)); + // surrogate pair. + os << s1; + os << s2; + } + } + return os.str(); + } + + + +} \ No newline at end of file diff --git a/PiPedalCommon/src/include/SysExec.hpp b/PiPedalCommon/src/include/SysExec.hpp index 031b90d..7b0a1d2 100644 --- a/PiPedalCommon/src/include/SysExec.hpp +++ b/PiPedalCommon/src/include/SysExec.hpp @@ -43,5 +43,9 @@ namespace pipedal void sysExecTerminate(ProcessId pid_, int termTimeoutMs = 1000, int killTimeoutMs = 500); // returns the process's exit status. int sysExecWait(ProcessId pid); + // re-execute the current program with current argument, but with sudo. + int SudoExec(int argc, char **argv); + + std::string getSelfExePath(); } \ No newline at end of file diff --git a/PiPedalCommon/src/include/Utf8Utils.hpp b/PiPedalCommon/src/include/Utf8Utils.hpp new file mode 100644 index 0000000..5b31af4 --- /dev/null +++ b/PiPedalCommon/src/include/Utf8Utils.hpp @@ -0,0 +1,50 @@ +// Copyright (c) 2023 Robin E. R. Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#pragma once + + +#include +#include +#include + +namespace pipedal { + size_t Utf8Index(size_t size, const std::string &text); + size_t Utf8Decrement(size_t size, const std::string &text); + size_t Utf8Increment(size_t size, const std::string &text); + size_t Utf8Length(const std::string&text); + + std::string Utf8Erase(const std::string&text, size_t start, size_t end); + std::string Utf8Substring(const std::string&text, size_t start, size_t end); + inline std::string Utf8Substring(const std::string&text, size_t start) { + return Utf8Substring(text,start,(size_t)-1); + } + namespace implementation { + void Utf8RangeError(); + } + + std::string Utf8FromUtf32(char32_t value); + + + std::u16string Utf8ToUtf16(const std::string_view&s); + std::string Utf16ToUtf8(const std::u16string_view&s); + + +} + diff --git a/PiPedalCommon/src/include/json.hpp b/PiPedalCommon/src/include/json.hpp index 7b0860a..f47deba 100644 --- a/PiPedalCommon/src/include/json.hpp +++ b/PiPedalCommon/src/include/json.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #define DECLARE_JSON_MAP(CLASSNAME) \ static pipedal::json_map::storage_type jmap @@ -592,6 +593,17 @@ namespace pipedal write(obj.get()); } } + template + void write(const std::optional &obj) + { + if (!obj) { + write_raw("null"); + } else { + write(obj.get()); + } + } + + template void write(const std::weak_ptr &obj) { @@ -883,6 +895,20 @@ namespace pipedal } } template + void read(std::optional *pOptional) + { + if (peek() == 'n') + { + consumeToken("null", "Expecting '{' or 'null'."); + *pOptional = std::optional(); + + } else { + T value; + read(&value); + *pOptional = std::move(value); + } + } + template void read(std::weak_ptr *pUniquePtr) { throw std::domain_error("Can't read std::weak_ptr"); diff --git a/PiPedalCommon/src/include/util.hpp b/PiPedalCommon/src/include/util.hpp index 405b6e2..793e187 100644 --- a/PiPedalCommon/src/include/util.hpp +++ b/PiPedalCommon/src/include/util.hpp @@ -20,67 +20,105 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#pragma once +#pragma once #include #include #include #include #include +#include -namespace pipedal { +namespace pipedal +{ - inline bool endsWith(const std::string& str, const std::string& suffix) + inline bool endsWith(const std::string &str, const std::string &suffix) { - return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix); + return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix); } void SetThreadName(const std::string &name); std::u32string ToUtf32(const std::string &s); - inline bool isPathSeperator(char c) { return c == '/' || c == std::filesystem::path::preferred_separator;} + inline bool isPathSeparator(char c) { return c == '/' || c == std::filesystem::path::preferred_separator; } std::string GetHostName(); std::vector split(const std::string &value, char delimiter); - inline void ReadBarrier() { + inline void ReadBarrier() + { std::atomic_thread_fence(std::memory_order::acquire); } - inline void WriteBarrier() { + inline void WriteBarrier() + { std::atomic_thread_fence(std::memory_order::release); - } template - inline bool contains(const std::vector&vector,const T&value) + inline bool contains(const std::vector &vector, const T &value) { for (auto i = vector.begin(); i != vector.end(); ++i) { - if ((*i) == value) { + if ((*i) == value) + { return true; } } return false; } - inline bool contains(const std::vector&vector,const char*value) + inline bool contains(const std::vector &vector, const char *value) { - return contains(vector,std::string(value)); + return contains(vector, std::string(value)); } - class NoCopy { + class NoCopy + { public: - NoCopy() { } - NoCopy(const NoCopy&) = delete; - NoCopy&operator=(const NoCopy&) = delete; + NoCopy() {} + NoCopy(const NoCopy &) = delete; + NoCopy &operator=(const NoCopy &) = delete; ~NoCopy() {} }; - class NoMove { + class NoMove + { public: - NoMove() { } - NoMove(NoMove&&) = delete; - NoMove&operator=(NoMove&&) = delete; - ~NoMove() { } - + NoMove() {} + NoMove(NoMove &&) = delete; + NoMove &operator=(NoMove &&) = delete; + ~NoMove() {} }; + bool IsChildDirectory(const std::filesystem::path &path, const std::filesystem::path&rootPath); + + template + std::set Intersect(const std::set &left, const std::set &right) + { + std::set result; + for (const T &v : left) + { + if (right.contains(v)) + { + result.insert(v); + } + } + return result; + } + template + bool IsSubset(const std::set &subset, const std::set &set) + { + std::set result; + for (const T &v : subset) + { + if (!set.contains(v)) return false; + } + return true; + } + + // C locale to lower. Only does 'A'-'Z'. + std::string ToLower(const std::string&value); + + std::filesystem::path MakeRelativePath(const std::filesystem::path &path, const std::filesystem::path&parentPath); + + bool IsSubdirectory(const std::filesystem::path &path, const std::filesystem::path &basePath); + } diff --git a/PiPedalCommon/src/json.cpp b/PiPedalCommon/src/json.cpp index 5b55fc6..c25f5ae 100644 --- a/PiPedalCommon/src/json.cpp +++ b/PiPedalCommon/src/json.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Robin Davies +// Copyright (c) 2022 Robin E. R. Davies // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in diff --git a/PiPedalCommon/src/util.cpp b/PiPedalCommon/src/util.cpp index e01c179..08de1f2 100644 --- a/PiPedalCommon/src/util.cpp +++ b/PiPedalCommon/src/util.cpp @@ -21,6 +21,7 @@ * SOFTWARE. */ #include "util.hpp" +#include "ss.hpp" #include #include @@ -136,3 +137,82 @@ std::vector pipedal::split(const std::string &value, char delimiter } return result; } + + +bool pipedal::IsChildDirectory(const std::filesystem::path &path, const std::filesystem::path&rootPath) +{ + auto iter = path.begin(); + + for (auto i = rootPath.begin(); i != rootPath.end(); ++i) + { + if (iter == path.end() || *iter != *i) + { + return false; + } + ++iter; + } + return true; +} + + +std::string pipedal::ToLower(const std::string&value) +{ + std::string result; + result.resize(value.length()); + for (size_t i = 0; i < value.length(); ++i) + { + result[i] = (char)tolower(value[i]); + } + return result; +} + + +std::filesystem::path pipedal::MakeRelativePath(const std::filesystem::path &path, const std::filesystem::path&parentPath) +{ + if (path.is_relative()) return path; + + auto iter = path.begin(); + + for (auto i = parentPath.begin(); i != parentPath.end(); ++i) + { + if (iter == path.end() || *iter != *i) { + // not a child directory of parent directory. + throw std::runtime_error("Not a child directory."); + } + ++iter; + } + std::filesystem::path remander; + while (iter != path.end()) + { + remander /= *iter++; + } + return remander; + +} + +bool pipedal::IsSubdirectory(const std::filesystem::path &path, const std::filesystem::path &basePath) +{ + auto iPath = path.begin(); + for (auto i = basePath.begin(); i != basePath.end(); ++i) + { + if (iPath == path.end()) + { + return false; + } + + if ((*i) != (*iPath)) + { + return false; + } + ++iPath; + } + while (iPath != path.end()) + { + if (iPath->string() == "..") + { + return false; + } + ++iPath; + } + return true; +} diff --git a/README.md b/README.md index 558c9af..85d33da 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ -Download: v1.3.69 +Download: v1.4.70 Website: [https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal). Documentation: [https://rerdavies.github.io/pipedal/Documentation.html](https://rerdavies.github.io/pipedal/Documentation.html). @@ -15,7 +15,7 @@ Documentation: [https://rerdavies.github.io/pipedal/Documentation.html](htt #### Important notice: PiPedal has a submitted a request to support sharing of Pipedal presets at [PatchStorage.com](https:///patchstorage.com). We need your support! Please vote for PiPedal [here](https://patchstorage.com/requests/pipedal/). -#### NEW version 1.3.69 Release, providing [snapshots](https://rerdavies.github.io/pipedal/Snaphots.html), and a new Performance View. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. +#### NEW version 1.4.70 Beta. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details.   diff --git a/debian/copyright b/debian/copyright index 6efca97..45dd967 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,24 +1,24 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: pipedal -Upstream-Contact: Robin Davies +Upstream-Contact: Robin E. R. Davies Source: https://www.example.com/software/project Files: * -Copyright: 2021, Robin Davies +Copyright: 2021, Robin E. R. Davies License: Expat Files: src/* -Copyright: 2021, Robin Davies +Copyright: 2021, Robin E. R. Davies License: Expat Files: react/src -Copyright: 2020, Robin Davies. +Copyright: 2020, Robin E. R. Davies. License: Expat Files: src/Lv2EventBufferWriter.hpp Copyright: 2008-2014, David Robillard - 2021, Robin Davies + 2021, Robin E. R. Davies License: ISC and Expat Files: react/node_modules/@babel/* diff --git a/debian/fill.copyright.blanks.yml b/debian/fill.copyright.blanks.yml index 1760ec1..15f83f9 100644 --- a/debian/fill.copyright.blanks.yml +++ b/debian/fill.copyright.blanks.yml @@ -1,11 +1,11 @@ --- /: license: Expat - copyright: 2021, Robin Davies. + copyright: 2021, Robin E. R. Davies. src/react/: license: Expat - copyright: 2021, Robin Davies. + copyright: 2021, Robin E. R. Davies. react/node_modules/webpack/: license: Expat diff --git a/docs/BuildPrerequisites.md b/docs/BuildPrerequisites.md index c19beb8..40adbc5 100644 --- a/docs/BuildPrerequisites.md +++ b/docs/BuildPrerequisites.md @@ -4,7 +4,7 @@ Run the following commands to install build tools required by the PiPedal build. # install CMake sudo apt updatee - sudo apt install -y cmake ninja-build gcc git + sudo apt install -y cmake ninja-build build-essential g++ git The PiPedal build process also requires version 12 or later of `node.js`. Type `node --version` to see if you have a version of `node.js` installed already. Otherwise run the following commands as root to install the v14.x lts version of `node.js`: @@ -16,7 +16,7 @@ with # install NodeJS latest LTS release. curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - - sudo apt-get install -y nodejs + sudo apt-get install -y nodejs npm Run the following commands to install dependent libraries required by the PiPedal build. diff --git a/docs/BuildingPiPedalFromSource.md b/docs/BuildingPiPedalFromSource.md index 5188c4f..07f9b7d 100644 --- a/docs/BuildingPiPedalFromSource.md +++ b/docs/BuildingPiPedalFromSource.md @@ -5,7 +5,7 @@ page_icon: img/Compiling.jpg {% include pageIcon.html %} -PiPedal has only been tested on Raspberry Pi OS, and Ubuntu, but should run with some modifications on most Linux distributions. Pull requests to correct problems with building PiPedal on other versions of Linux are welcome. However, getting build pre-requisites installed on non-Debian-derived Linux distrubutions (distros that don't use `apt`) will be challenging. +PiPedal has only been tested on Raspberry Pi OS, and Ubuntu 24.x, but should run with some modifications on most Linux distributions. Pull requests to correct problems with building PiPedal on other versions of Linux are welcome. However, getting build pre-requisites installed on non-Debian-derived Linux distrubutions (distros that don't use `apt`) will be challenging. To build and debug PiPedal using Visual Studio Code, you need 8GB of memory. You can build PiPedal with 2GB of memory. diff --git a/docs/Configuring.md b/docs/Configuring.md index 3ca6a49..2580a1a 100644 --- a/docs/Configuring.md +++ b/docs/Configuring.md @@ -3,6 +3,22 @@ page_icon: img/Setup.jpg icon_width: 320px icon_float: left --- +## Selecting a real-time kernel on Ubuntu. + +This step is unneccesary on Raspberry Pi OS, which installs an RT_PREEMPT kernel by default. + +By default, Ubuntu installs a PREEMT_DYNAMIC kernel, configured to run with volutary preemption. You will need to change this. You can do this by running the following command after you have installed PiPedal: + +``` + pipedal_kconfig +``` + +In order to support real-time audio processing, select Preempt=full. + +An RT_PREEMPT kernel provides slightly better realtime audio than a PREEMPT_DYNAMIC/preempt=full kernel, but the PREEMPT_DYNAMIC should be perfectly adequate. If you are having problems with audio dropouts, you may want to try an RT_PREEMPT kernel instead. Ubuntu provides a pre-built RT_PREEMPT kernel; but on our test systems, you can acheive stable low-latency audio using only the PREEMPT_DYNAMIC/preempt=full kernel. The pre-build Ubuntu RT_PREEMPT +kernel is only available if yo have an Ubuntu Pro subscription. But it is relatively easy to build the Ubuntu RT_PREEMPT kernel from source. That is, however, outside the scope of this document. We recommend using the PREEMPT_DYNAMIC/preempt=full Ubuntu kernel. + + ## Configuring PiPedal After Installation diff --git a/docs/Debugging.md b/docs/Debugging.md index e694d33..1786a5f 100644 --- a/docs/Debugging.md +++ b/docs/Debugging.md @@ -2,7 +2,7 @@ PipPedal consists of the following components: -* A web application build in React, found in the react subdirectory. +* A web application built using vite/React, found in the `vite` subdirectory. * `pipedald` @@ -40,7 +40,7 @@ same /etc/pipedal and /var/pipedal directories as an instance of `pipedal` runn under systemd. Note that when running under systemd, `pipedald` runs under an unprivileged `piedal_d` service account, and relies it's group (also 'pipedal_d`) in order to access its data files, and to communicate with the `pipedaladmin` service, which does run with root -priveleges when `pipedald` needs to perform operations that do required root privileges. +privileges when `pipedald` needs to perform operations that do required root privileges (e.g. shutdown/reboot and starting and stopping WiFi services). For what it's worth, `pipedaldmin` is virtually undebuggable, becuase it does require root privileges to run. If you really _must_ debug pipedaladmin, you can fire up a sudo isntance of Visual Studio Code @@ -53,8 +53,8 @@ audio and Wi-Fi configuration changes) may fail if the pipedaladmind service is with pipedaladmin via Unix docket that can only be opened by members of the pipedal_d group. So if you have added your own account to the `pipedal_d` group, debug instances of `pipedald` will in fact work properly. -In production, the pipedald web server serves the PiPedal web socket, as well as static HTML from the built -react components. But while debugging, it is much more convenient to use the React debug server for +In production, the pipedald web server serves the PiPedal web socket, as well as static HTML from the built +vite/React components. But while debugging, it is much more convenient to use the Vite debug server for React sources, and configure pipedald to serve only the websocket. Note that a debug instance of `pipedald` cannot bind to port 80, since that requires either root privileges or @@ -72,23 +72,26 @@ make the systemd instance of `pipedald` bind to port 8080 instead of port 80: (which will also restart the `pipedald` service). -To start the React debug server, from a shell, `cd` to the react directory, and run `./start`. The react debug -server will detect any changes to React sources, and rebuild them automatically (no build step required). -Actual debugging is performed using the Chrome debugger (which is remarkably well integrated with React). +To start the web app debug server, from a shell, `cd` to the `./vite` directory, and run `npm run dev`. The +Vite debug server will automatically detect any changes to web app sources, and rebuild them automatically (no build step required). Note that the `pipedald` service must be running in order for the web app to function properly, either +as a the `pipedald` service, or by running `pipedald` in a debugger. +Actual debugging is performed using the Chrome debugger (which is remarkably well integrated with Vite/React). You won't actually see changes to the version of the systemd version of the static web app until you do a full _Release_ or _RelWithDebInfo_ build of PiPedal, followed by running `./install.sh` which pushes the built react app in the location where the systemd version of `pipedald` serves static web content. By default, the debug React app will attempt to contact the pipedald server on ws:*:8080 -- the address on which the debug version of pipedald listens on. This can be reconfigured -in the file `react/src/public/var/config.json` if desired. If you connect to the the pipedald server port (port 80), pipedald intercepts requests for this file and points the react app at itself, so the file has no effect when running in production. +in the file `react/src/public/var/config.json` if desired. If you connect to the the pipedald server port (port 80), pipedald intercepts requests for `http://./var/config.json` and points the react app at itself, so the file has no effect when running in production. The React app will display the message "Error: Failed to connect to the server", until you start the pipedald websocket server in the VSCode debugger. However, it's quite reasonable to point the react debug app at a systemd instance of the pipedald server instead, if you don't intend to debug C++ code. react/public/var/config.json: { ... - "socket_server_port": 8080, # (PiPedald's port number) + # (PiPedald's port number. 80 for the production service, 8080 (by default) for + # pipedal running under a debugger. + "socket_server_port": 8080, "socket_server_address": "*", ... } diff --git a/docs/Installing.md b/docs/Installing.md index 05c7088..dfbd312 100644 --- a/docs/Installing.md +++ b/docs/Installing.md @@ -13,24 +13,25 @@ page_icon: img/Install4.jpg Download the most recent Debian (.deb) package for your platform: -- [Raspberry Pi OS bookworm (64-bit) v1.3.69](https://github.com/rerdavies/pipedal/releases/download/) -- [Ubuntu/Raspberry Pi OS bullseyeye (64-bit) v1.2.31](https://github.com/rerdavies/pipedal/releases/download/v1.1.31/pipedal_1.1.31_arm64.deb) +- [Raspberry Pi OS bookworm (aarch64) v1.4.70 Beta](https://github.com/rerdavies/pipedal/releases/download/) +- [Ubuntu 24.x (aarch64) v1.4.70 Alpha](https://github.com/rerdavies/pipedal/releases/download/) +- [Ubuntu 24.x (amd64) v1.4.70 Alpha](https://github.com/rerdavies/pipedal/releases/download/) -Version 1.3.69 has not yet been tested on Ubuntu or Raspberry Pi OS bullseye. On these platforms, we recommend that you use version 1.1.31. -Install the package by running +Version 1.4.70 has been tested on Raspberry Pi OS bookworm, Ubuntu 24.04 (amd64), and Ubuntu 24.10 (aarch64). Download the appropriate package for your platform, and install using the following proceduer: ``` sudo apt update cd ~/Downloads - sudo apt-get install pipedal_1.3.69_arm64.deb + sudo apt-get install pipedal_1.4.70_arm64.deb ``` -Adjust accordingly if you have downloaded v1.1.31. +Adjust accordingly depending which package you downloaded. On Raspberry Pi OS, if you have a graphical desktop installed, you can also install the package by double-clicking on the downloaded package in the File Manager. -After installing, follow the instructions in [Configuring PiPedal after Installation](Configuring.md). +After installing, follow the instructions in [Configuring PiPedal after Installation](Configuring.md). If +you are using Ubuntu, you will need to reconfigure Ubuntu to use a real-time kernel. -------- diff --git a/docs/LicensePiPedal.md b/docs/LicensePiPedal.md index 516449c..8911e36 100644 --- a/docs/LicensePiPedal.md +++ b/docs/LicensePiPedal.md @@ -1,4 +1,4 @@ -Copyright 2021-2024 Robin Davies +Copyright 2021-2024 Robin E. R. Davies Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/docs/LicenseToobAmp.md b/docs/LicenseToobAmp.md index 279f498..f55edb4 100644 --- a/docs/LicenseToobAmp.md +++ b/docs/LicenseToobAmp.md @@ -1,4 +1,4 @@ -Copyright 2021-2023 Robin Davies +Copyright 2021-2023 Robin E. R. Davies Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index e68fbfc..620e52c 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -1,5 +1,18 @@ # Release Notes +## PiPedal 1.4.70 Beta + +- Ubuntu 24.x compatibility (arm64 and amd64) +- Ubuntu CPU Freq/governor/thermal status display. +- Support for WiFi on on Ubuntu +- TooB Plugin compatibility issues on x64/amd64 (handling of NaNs) +- Restart audio automatically after ALSA stalls. +- Improvements to File Upload UI for plugins that have complex file types (e.g. Ratatouille) +- Support for LV2 Trigger controls, and MIDI bindings to LV2 trigger controls (common in looper and recording/playback plugsin). +- MIDI bindings for LV2 trigger controls. +- Port Web app from CRA to Vite framework to avoid deprecated dependencies. +- ToobAmp 1.1.57: Correctly set file path of the default file property for Toob Convolution Reverb. + ## PiPedal 1.3.69 Bug fixes: diff --git a/docs/_includes/footer.html b/docs/_includes/footer.html index ee348ba..090570e 100644 --- a/docs/_includes/footer.html +++ b/docs/_includes/footer.html @@ -21,6 +21,10 @@ display: block; height: 24px; } + img-icon { + width: 24px; + height: + }