From ea5296c5445f48d80c55807d23996fa7e4f0306d Mon Sep 17 00:00:00 2001 From: Brenton Bostick Date: Fri, 13 Dec 2024 10:18:46 -0500 Subject: [PATCH] WIP --- LICENSE | 2 +- README.md | 2 +- exe/CMakeLists.txt | 16 +- include/tbt-parser.h | 2 +- include/tbt-parser/rational.h | 75 ----- include/tbt-parser/tbt-parser-util.h | 4 - lib/CMakeLists.txt | 30 +- lib/midi.cpp | 3 +- lib/rational.cpp | 423 --------------------------- lib/tablature.cpp | 4 +- lib/tbt-parser-util.cpp | 28 -- lib/tbt.cpp | 3 +- test/CMakeLists.txt | 11 +- test/TestRational.cpp | 122 -------- 14 files changed, 48 insertions(+), 677 deletions(-) delete mode 100644 include/tbt-parser/rational.h delete mode 100644 lib/rational.cpp delete mode 100644 test/TestRational.cpp diff --git a/LICENSE b/LICENSE index 24d18ef..3dbfbce 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Brenton Bostick +Copyright (c) 2025 Brenton Bostick Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 5eebab3..2c76a83 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ tbt-parser depends on zlib and uses CMake for building. tbt-parser requires a C++20 compiler because of features such as `__VA_OPT__`. ``` -cd cpp +cd tbt-parser mkdir build cd build cmake .. diff --git a/exe/CMakeLists.txt b/exe/CMakeLists.txt index 0c6b553..1f9b0ad 100644 --- a/exe/CMakeLists.txt +++ b/exe/CMakeLists.txt @@ -19,6 +19,14 @@ cmake_minimum_required(VERSION 3.22.1) include(FetchContent) +# +# De-duplicate libraries on link lines based on linker capabilities. +# +# fixes Xcode complaining: +# ld: warning: ignoring duplicate libraries: 'libcommon.a' +# +cmake_policy(SET CMP0156 NEW) + FetchContent_Declare(common GIT_REPOSITORY https://github.com/bostick/common.git @@ -48,25 +56,25 @@ add_executable(tbt-info-exe target_link_libraries(tbt-converter-exe PRIVATE - tbt-parser-static-lib + tbt-parser-lib common-lib ) target_link_libraries(midi-info-exe PRIVATE - tbt-parser-static-lib + tbt-parser-lib common-lib ) target_link_libraries(tbt-printer-exe PRIVATE - tbt-parser-static-lib + tbt-parser-lib common-lib ) target_link_libraries(tbt-info-exe PRIVATE - tbt-parser-static-lib + tbt-parser-lib common-lib ) diff --git a/include/tbt-parser.h b/include/tbt-parser.h index cfd6145..b256081 100644 --- a/include/tbt-parser.h +++ b/include/tbt-parser.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include // for uint8_t #include // for size_t diff --git a/include/tbt-parser/rational.h b/include/tbt-parser/rational.h deleted file mode 100644 index 6ce0b2c..0000000 --- a/include/tbt-parser/rational.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2024 by Brenton Bostick -// -// 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 - - -class rational { -private: - - int64_t n; - int64_t d; - - void simplify(); - -public: - - rational(); - rational(int a); - rational(uint32_t a); - rational(int64_t a); - - rational(const rational &a); - rational(int64_t nIn, int64_t dIn); - - rational& operator=(const rational &a); - rational& operator=(rational &&a); - - int64_t numerator() const; - int64_t denominator() const; - - rational operator/(const rational &x) const; - rational operator-(const rational &x) const; - rational operator*(const rational &x) const; - rational operator+(const rational &x) const; - - bool operator==(const rational &x) const; - bool operator>(const rational &x) const; - bool operator<(const rational &x) const; - - bool is_nonnegative() const; - bool is_positive() const; - - rational& operator+=(const rational &x); - - rational& operator-=(const rational &x); - - rational& operator++(); - rational& operator--(); - - double to_double() const; - int16_t to_int16() const; - uint16_t to_uint16() const; - int32_t to_int32() const; - uint32_t to_uint32() const; - - rational floor() const; - rational round() const; -}; diff --git a/include/tbt-parser/tbt-parser-util.h b/include/tbt-parser/tbt-parser-util.h index 9663437..4be7a5e 100644 --- a/include/tbt-parser/tbt-parser-util.h +++ b/include/tbt-parser/tbt-parser-util.h @@ -86,10 +86,6 @@ std::string fromPascal2String(const char *data); std::string trim(const std::string &str); -int8_t euclidean_mod(int8_t a, int8_t b); - -int64_t euclidean_mod(int64_t a, int64_t b); - uint8_t width(int a); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 733b4b6..1d2d3a6 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -54,20 +54,29 @@ FetchContent_Declare(common ) FetchContent_MakeAvailable(common) +FetchContent_Declare(rational + GIT_REPOSITORY + https://github.com/bostick/rational.git + GIT_TAG + v0.1.0 + GIT_SHALLOW 1 + GIT_PROGRESS 1 +) +FetchContent_MakeAvailable(rational) + set(CPP_LIB_SOURCES midi.cpp - rational.cpp tbt.cpp tbt-parser-util.cpp tablature.cpp ) -add_library(tbt-parser-static-lib STATIC +add_library(tbt-parser-lib STATIC ${CPP_LIB_SOURCES} ) -target_include_directories(tbt-parser-static-lib +target_include_directories(tbt-parser-lib PUBLIC ../include # @@ -76,15 +85,16 @@ target_include_directories(tbt-parser-static-lib ../lib ) -target_link_libraries(tbt-parser-static-lib +target_link_libraries(tbt-parser-lib PRIVATE zlibstatic common-lib + rational-lib ) -set_target_properties(tbt-parser-static-lib +set_target_properties(tbt-parser-lib PROPERTIES - OUTPUT_NAME tbt-parser-static + OUTPUT_NAME tbt-parser CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS NO @@ -96,19 +106,19 @@ set_target_properties(tbt-parser-static-lib # https://www.foonathan.net/2018/10/cmake-warnings/ # if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") -target_compile_options(tbt-parser-static-lib PRIVATE +target_compile_options(tbt-parser-lib PRIVATE -Wall -Wextra -pedantic -Werror -Wconversion -Wsign-conversion ) elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") -target_compile_options(tbt-parser-static-lib PRIVATE +target_compile_options(tbt-parser-lib PRIVATE -Wall -Wextra -pedantic -Werror -Wconversion -Wsign-conversion ) elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") -target_compile_options(tbt-parser-static-lib PRIVATE +target_compile_options(tbt-parser-lib PRIVATE -Wall -Wextra -pedantic -Werror -Wconversion -Wsign-conversion ) elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") -target_compile_options(tbt-parser-static-lib PRIVATE +target_compile_options(tbt-parser-lib PRIVATE # # /Zc:preprocessor is needed for handling __VA_OPT__(,) # diff --git a/lib/midi.cpp b/lib/midi.cpp index ae0f60b..b79ccc0 100644 --- a/lib/midi.cpp +++ b/lib/midi.cpp @@ -16,10 +16,11 @@ // 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 "tbt-parser/rational.h" #include "tbt-parser/tbt-parser-util.h" #include "tbt-parser/tbt.h" +#include "rational/rational.h" + #undef NDEBUG #include "common/abort.h" diff --git a/lib/rational.cpp b/lib/rational.cpp deleted file mode 100644 index b40ad39..0000000 --- a/lib/rational.cpp +++ /dev/null @@ -1,423 +0,0 @@ -// Copyright (C) 2024 by Brenton Bostick -// -// 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 "tbt-parser/rational.h" - -#include "tbt-parser/tbt-parser-util.h" - -#undef NDEBUG - -#include "common/assert.h" -#include "common/logging.h" - -#include -#include - - -#define TAG "rational" - - -int64_t gcd(int64_t p, int64_t q); - - -rational::rational() : n(0), d(1) {} - -rational::rational(int a) : n(a), d(1) {} - -rational::rational(uint32_t a) : n(a), d(1) {} - -rational::rational(int64_t a) : n(a), d(1) {} - -rational::rational(const rational &a) = default; - -rational::rational(int64_t nIn, int64_t dIn) : n(nIn), d(dIn) { - simplify(); -} - - -rational& rational::operator=(const rational &a) = default; - -rational& rational::operator=(rational &&a) { - - n = a.n; - - d = a.d; - - return *this; -} - - -int64_t rational::numerator() const { - return n; -} - -int64_t rational::denominator() const { - return d; -} - - -void rational::simplify() { - - if (d < 0) { - n *= -1; - d *= -1; - } - - if (d == 1) { - - return; - - } else if (d == 2) { - - if (n % 2 == 0) { - - n >>= 1; - d = 1; - - return; - - } else { - - return; - } - - } else if (d == 3) { - - if (n % 3 == 0) { - - n /= 3; - d = 1; - - return; - - } else { - - return; - } - } - - // - // general case - // - - int64_t denom = gcd(n, d); - - ASSERT(0 <= denom); - - if (denom == 1) { - return; - } - - n /= denom; - d /= denom; -} - - -int64_t gcd(int64_t p, int64_t q) { - - if (q == 0) { - return p; - } - - return gcd(q, euclidean_mod(p, q)); -} - - -rational rational::operator/(const rational &x) const { - - if (x.n == 0) { - LOGE("DIVIDE BY 0"); - } - - return { n * x.d, d * x.n }; -} - -rational rational::operator-(const rational &x) const { - - if (d == x.d) { - - if (d == 1) { - return { n - x.n }; - } - - return { n - x.n, d }; - } - - return { n * x.d - x.n * d, d * x.d }; -} - -rational rational::operator*(const rational &x) const { - return { n * x.n, d * x.d }; -} - -rational rational::operator+(const rational &x) const { - - if (d == x.d) { - - if (d == 1) { - return { n + x.n }; - } - - return { n + x.n, d }; - } - - return { n * x.d + x.n * d, d * x.d }; -} - -bool rational::operator==(const rational &x) const { - return n == x.n && d == x.d; -} - -bool rational::operator>(const rational &x) const { - return n * x.d > d * x.n; -} - -bool rational::operator<(const rational &x) const { - return n * x.d < d * x.n; -} - - -bool rational::is_nonnegative() const { - return n >= 0; -} - -bool rational::is_positive() const { - return n > 0; -} - - -rational& rational::operator+=(const rational &x) { - - if (x.n == 0) { - return *this; - } - - if (d == x.d) { - - if (d == 1) { - - n += x.n; - - return *this; - - } else if (d == 2) { - - n += x.n; - - if (n % 2 == 0) { - - n >>= 1; - d = 1; - - return *this; - - } else { - - return *this; - } - } - - // - // general case - // - - n += x.n; - - simplify(); - - return *this; - } - - if (d == 1) { - - n *= x.d; - n += x.n; - - d = x.d; - - simplify(); - - return *this; - } - - n *= x.d; - n += x.n * d; - - d = d * x.d; - - simplify(); - - return *this; -} - -rational& rational::operator-=(const rational &x) { - - if (x.n == 0) { - return *this; - } - - if (d == x.d) { - - if (d == 1) { - - n -= x.n; - - return *this; - } - - n -= x.n; - - simplify(); - - return *this; - } - - n = n * x.d - x.n * d; - - d = d * x.d; - - simplify(); - - return *this; -} - -rational& rational::operator++() { - - if (d == 1) { - - n++; - - return *this; - } - - n += d; - - simplify(); - - return *this; -} - -rational& rational::operator--() { - - if (d == 1) { - - n--; - - return *this; - } - - n -= d; - - simplify(); - - return *this; -} - -double rational::to_double() const { - return static_cast(n) / static_cast(d); -} - -int16_t rational::to_int16() const { - - ASSERT(d == 1); - - ASSERT(n <= 0x7fff); - - return static_cast(n); -} - -uint16_t rational::to_uint16() const { - - ASSERT(n >= 0); - - ASSERT(d == 1); - - ASSERT(n <= 0xffff); - - return static_cast(n); -} - -int32_t rational::to_int32() const { - - ASSERT(d == 1); - - ASSERT(n <= 0x7fffffff); - - return static_cast(n); -} - -uint32_t rational::to_uint32() const { - - ASSERT(n >= 0); - - ASSERT(d == 1); - - ASSERT(n <= 0xffffffff); - - return static_cast(n); -} - -rational rational::floor() const { - - if (d == 1) { - return *this; - } - - return { n / d, 1 }; -} - - -rational rational::round() const { - - if (d == 1) { - - return *this; - - } else if (d == 2) { - - // - // we already know n is odd, since the denominator is 2 - // - - if (n % 4 == 1) { - - return { (n - 1) / 2, 1 }; - - } else { - - // - // n % 4 == 3 - // - return { (n + 1) / 2, 1 }; - } - } - - // - // general case - // - - return { static_cast(std::round(to_double())), 1 }; -} - - - - - - - - diff --git a/lib/tablature.cpp b/lib/tablature.cpp index f52950e..a5d13bf 100644 --- a/lib/tablature.cpp +++ b/lib/tablature.cpp @@ -18,15 +18,17 @@ #include "tbt-parser.h" -#include "tbt-parser/rational.h" #include "tbt-parser/tbt-parser-util.h" #include "tbt-parser/tbt.h" +#include "rational/rational.h" + #undef NDEBUG #include "common/abort.h" #include "common/assert.h" #include "common/logging.h" +#include "common/math_utils.h" #include diff --git a/lib/tbt-parser-util.cpp b/lib/tbt-parser-util.cpp index 1fe50db..255ae0e 100644 --- a/lib/tbt-parser-util.cpp +++ b/lib/tbt-parser-util.cpp @@ -571,34 +571,6 @@ std::string trim(const std::string &str) { } -// -// % operator may return negative values -// -// we want in the range 0 <= r < b -// -int8_t euclidean_mod(int8_t a, int8_t b) { - - auto r = static_cast(a % b); - - if (0 <= r) { - return r; - } - - return static_cast(r + b); -} - -int64_t euclidean_mod(int64_t a, int64_t b) { - - int64_t r = a % b; - - if (0 <= r) { - return r; - } - - return r + b; -} - - // // return number of base 10 digits // diff --git a/lib/tbt.cpp b/lib/tbt.cpp index 14fb74a..4df0c49 100644 --- a/lib/tbt.cpp +++ b/lib/tbt.cpp @@ -18,10 +18,11 @@ #include "tbt-parser.h" -#include "tbt-parser/rational.h" #include "tbt-parser/tbt-parser-util.h" #include "tbt-parser/tbt.h" +#include "rational/rational.h" + #undef NDEBUG #include "common/abort.h" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 28c1d4c..faee2bd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -21,10 +21,12 @@ include(FetchContent) FetchContent_Declare(googletest - GIT_REPOSITORY - https://github.com/google/googletest.git - GIT_TAG - v1.15.2 + GIT_REPOSITORY + https://github.com/google/googletest.git + GIT_TAG + v1.15.2 + GIT_SHALLOW 1 + GIT_PROGRESS 1 ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) @@ -45,7 +47,6 @@ FetchContent_MakeAvailable(common) set(CPP_TEST_SOURCES TestLastFound.cpp TestMidi.cpp - TestRational.cpp TestTbt.cpp TestUtil.cpp ) diff --git a/test/TestRational.cpp b/test/TestRational.cpp deleted file mode 100644 index da99fe8..0000000 --- a/test/TestRational.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (C) 2024 by Brenton Bostick -// -// 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 "tbt-parser.h" - -#include "tbt-parser/rational.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -class RationalTest : public ::testing::Test { -protected: - - static void SetUpTestSuite() { - - } - - static void TearDownTestSuite() { - - - } - - void SetUp() override { - - } - - void TearDown() override { - - } -}; - - -TEST_F(RationalTest, basic) { - - auto tick = rational{189024, 1}; - - auto lastMicrosPerTick = rational{110497, 64}; - - auto inc = tick * lastMicrosPerTick; - - EXPECT_EQ(inc.numerator(), 652705779); - EXPECT_EQ(inc.denominator(), 2); -} - - -TEST_F(RationalTest, pitchBend1) { - - // - // important that value of 0 goes to value of 0x2000 (8192) - // - auto a = (((rational(0) + 2400) * 16383) / (2 * 2400)).round(); - auto b = rational{8192, 1}; - EXPECT_EQ(a, b); - - a = (((rational(-2400) + 2400) * 16383) / (2 * 2400)).round(); - b = rational{0, 1}; - EXPECT_EQ(a, b); - - a = (((rational(2400) + 2400) * 16383) / (2 * 2400)).round(); - b = rational{16383, 1}; - EXPECT_EQ(a, b); -} - - -TEST_F(RationalTest, negative) { - - auto r = rational{-11, 5}; - - EXPECT_EQ(r.numerator(), -11); - EXPECT_EQ(r.denominator(), 5); - - r = rational{-12, 3}; - - EXPECT_EQ(r.numerator(), -4); - EXPECT_EQ(r.denominator(), 1); - - r = rational{12, -3}; - - EXPECT_EQ(r.numerator(), -4); - EXPECT_EQ(r.denominator(), 1); - - r = rational{-16, 4}; - - EXPECT_EQ(r.numerator(), -4); - EXPECT_EQ(r.denominator(), 1); - - r = rational{16, -4}; - - EXPECT_EQ(r.numerator(), -4); - EXPECT_EQ(r.denominator(), 1); -} - - - - - - - - - - - - - - - -