// // utility.h // // mt32-pi - A baremetal MIDI synthesizer for Raspberry Pi // Copyright (C) 2020-2023 Dale Whinham // // This file is part of mt32-pi. // // mt32-pi is free software: you can redistribute it and/or modify it under the // terms of the GNU General Public License as published by the Free Software // Foundation, either version 3 of the License, or (at your option) any later // version. // // mt32-pi is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // mt32-pi. If not, see . // #ifndef _utility_h #define _utility_h #include #include // Macro to extract the string representation of an enum #define CONFIG_ENUM_VALUE(VALUE, STRING) VALUE, // Macro to extract the enum value #define CONFIG_ENUM_STRING(VALUE, STRING) #STRING, // Macro to declare the enum itself #define CONFIG_ENUM(NAME, VALUES) enum class NAME { VALUES(CONFIG_ENUM_VALUE) } // Macro to declare an array of string representations for an enum #define CONFIG_ENUM_STRINGS(NAME, DATA) static const char* NAME##Strings[] = { DATA(CONFIG_ENUM_STRING) } namespace Utility { // Templated function for clamping a value between a minimum and a maximum template constexpr T Clamp(const T& nValue, const T& nMin, const T& nMax) { return (nValue < nMin) ? nMin : (nValue > nMax) ? nMax : nValue; } // Templated function for taking the minimum of two values template constexpr T Min(const T& nLHS, const T& nRHS) { return nLHS < nRHS ? nLHS : nRHS; } // Templated function for taking the maximum of two values template constexpr T Max(const T& nLHS, const T& nRHS) { return nLHS > nRHS ? nLHS : nRHS; } // Function for performing a linear interpolation of a value constexpr float Lerp(float nValue, float nMinA, float nMaxA, float nMinB, float nMaxB) { return nMinB + (nValue - nMinA) * ((nMaxB - nMinB) / (nMaxA - nMinA)); } // Return number of elements in an array template constexpr size_t ArraySize(const T(&)[N]) { return N; } // Returns whether some value is a power of 2 template constexpr bool IsPowerOfTwo(const T& nValue) { return nValue && ((nValue & (nValue - 1)) == 0); } // Rounds a number to a nearest multiple; only works for integer values/multiples template constexpr T RoundToNearestMultiple(const T& nValue, const T& nMultiple) { return ((nValue + nMultiple / 2) / nMultiple) * nMultiple; } // Convert between milliseconds and ticks of a 1MHz clock template constexpr T MillisToTicks(const T& nMillis) { return nMillis * 1000; } template constexpr T TicksToMillis(const T& nTicks) { return nTicks / 1000; } // Computes the Roland checksum constexpr u8 RolandChecksum(const u8* pData, size_t nSize) { u8 nSum = 0; for (size_t i = 0; i < nSize; ++i) nSum = (nSum + pData[i]) & 0x7F; return 128 - nSum; } // Comparators for sorting namespace Comparator { template using TComparator = bool (*)(const T&, const T&); template inline bool LessThan(const T& ObjectA, const T& ObjectB) { return ObjectA < ObjectB; } template inline bool GreaterThan(const T& ObjectA, const T& ObjectB) { return ObjectA > ObjectB; } inline bool CaseInsensitiveAscending(const CString& StringA, const CString& StringB) { return strcasecmp(StringA, StringB) < 0; } } // Swaps two objects in-place template inline void Swap(T& ObjectA, T& ObjectB) { u8 Buffer[sizeof(T)]; memcpy(Buffer, &ObjectA, sizeof(T)); memcpy(&ObjectA, &ObjectB, sizeof(T)); memcpy(&ObjectB, Buffer, sizeof(T)); } namespace { // Quicksort partition function (private) template size_t Partition(T* Items, Comparator::TComparator Comparator, size_t nLow, size_t nHigh) { const size_t nPivotIndex = (nHigh + nLow) / 2; T* Pivot = &Items[nPivotIndex]; while (true) { while (Comparator(Items[nLow], *Pivot)) ++nLow; while (Comparator(*Pivot, Items[nHigh])) --nHigh; if (nLow >= nHigh) return nHigh; Swap(Items[nLow], Items[nHigh]); // Update pointer if pivot was swapped if (nPivotIndex == nLow) Pivot = &Items[nHigh]; else if (nPivotIndex == nHigh) Pivot = &Items[nLow]; ++nLow; --nHigh; } } } // Sorts an array in-place using the Tony Hoare Quicksort algorithm template void QSort(T* Items, Comparator::TComparator Comparator, size_t nLow, size_t nHigh) { if (nLow < nHigh) { size_t p = Partition(Items, Comparator, nLow, nHigh); QSort(Items, Comparator, nLow, p); QSort(Items, Comparator, p + 1, nHigh); } } } #endif