/* * tuning.h * -------- * Purpose: Alternative sample tuning. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include #include "tuningbase.h" OPENMPT_NAMESPACE_BEGIN namespace Tuning { class CTuning { public: static constexpr char s_FileExtension[5] = ".tun"; static constexpr RATIOTYPE s_DefaultFallbackRatio = 1.0f; static constexpr NOTEINDEXTYPE s_NoteMinDefault = -64; static constexpr UNOTEINDEXTYPE s_RatioTableSizeDefault = 128; static constexpr USTEPINDEXTYPE s_RatioTableFineSizeMaxDefault = 1000; public: // To return ratio of certain note. RATIOTYPE GetRatio(const NOTEINDEXTYPE note) const; // To return ratio from a 'step'(noteindex + stepindex) RATIOTYPE GetRatio(const NOTEINDEXTYPE baseNote, const STEPINDEXTYPE baseFineSteps) const; //Tuning might not be valid for arbitrarily large range, //so this can be used to ask where it is valid. Tells the lowest and highest //note that are valid. MPT_FORCEINLINE NoteRange GetNoteRange() const { return NoteRange{m_NoteMin, static_cast(m_NoteMin + static_cast(m_RatioTable.size()) - 1)}; } // Return true if note is within note range MPT_FORCEINLINE bool IsValidNote(const NOTEINDEXTYPE n) const { return (GetNoteRange().first <= n && n <= GetNoteRange().last); } MPT_FORCEINLINE UNOTEINDEXTYPE GetGroupSize() const { return m_GroupSize; } RATIOTYPE GetGroupRatio() const {return m_GroupRatio;} // To return (fine)stepcount between two consecutive mainsteps. MPT_FORCEINLINE USTEPINDEXTYPE GetFineStepCount() const { return m_FineStepCount; } //To return 'directed distance' between given notes. STEPINDEXTYPE GetStepDistance(const NOTEINDEXTYPE& from, const NOTEINDEXTYPE& to) const {return (to - from)*(static_cast(GetFineStepCount())+1);} //To return 'directed distance' between given steps. STEPINDEXTYPE GetStepDistance(const NOTEINDEXTYPE& noteFrom, const STEPINDEXTYPE& stepDistFrom, const NOTEINDEXTYPE& noteTo, const STEPINDEXTYPE& stepDistTo) const {return GetStepDistance(noteFrom, noteTo) + stepDistTo - stepDistFrom;} //To set finestepcount between two consecutive mainsteps. //Finestep count == 0 means that //stepdistances become the same as note distances. void SetFineStepCount(const USTEPINDEXTYPE& fs); // Multiply all ratios by given number. bool Multiply(const RATIOTYPE r); bool SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r); MPT_FORCEINLINE Tuning::Type GetType() const { return m_TuningType; } mpt::ustring GetNoteName(const NOTEINDEXTYPE &x, bool addOctave = true) const; void SetNoteName(const NOTEINDEXTYPE &, const mpt::ustring &); static std::unique_ptr CreateDeserialize(std::istream &f, mpt::Charset defaultCharset) { std::unique_ptr pT = std::unique_ptr(new CTuning()); if(pT->InitDeserialize(f, defaultCharset) != SerializationResult::Success) { return nullptr; } return pT; } //Try to read old version (v.3) and return pointer to new instance if succesfull, else nullptr. static std::unique_ptr CreateDeserializeOLD(std::istream &f, mpt::Charset defaultCharset) { std::unique_ptr pT = std::unique_ptr(new CTuning()); if(pT->InitDeserializeOLD(f, defaultCharset) != SerializationResult::Success) { return nullptr; } return pT; } static std::unique_ptr CreateGeneral(const mpt::ustring &name) { std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); return pT; } static std::unique_ptr CreateGroupGeometric(const mpt::ustring &name, UNOTEINDEXTYPE groupsize, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) { std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); if(!pT->CreateGroupGeometric(groupsize, groupratio, 0)) { return nullptr; } pT->SetFineStepCount(finestepcount); return pT; } static std::unique_ptr CreateGroupGeometric(const mpt::ustring &name, const std::vector &ratios, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) { std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); NoteRange range = NoteRange{s_NoteMinDefault, static_cast(s_NoteMinDefault + s_RatioTableSizeDefault - 1)}; range.last = std::max(range.last, mpt::saturate_cast(ratios.size() - 1)); range.first = 0 - range.last - 1; if(!pT->CreateGroupGeometric(ratios, groupratio, range, 0)) { return nullptr; } pT->SetFineStepCount(finestepcount); return pT; } static std::unique_ptr CreateGeometric(const mpt::ustring &name, UNOTEINDEXTYPE groupsize, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) { std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); if(!pT->CreateGeometric(groupsize, groupratio)) { return nullptr; } pT->SetFineStepCount(finestepcount); return pT; } Tuning::SerializationResult Serialize(std::ostream& out) const; #ifdef MODPLUG_TRACKER bool WriteSCL(std::ostream &f, const mpt::PathString &filename) const; #endif bool ChangeGroupsize(const NOTEINDEXTYPE&); bool ChangeGroupRatio(const RATIOTYPE&); void SetName(const mpt::ustring &s) { m_TuningName = s; } mpt::ustring GetName() const { return m_TuningName; } private: CTuning(); SerializationResult InitDeserialize(std::istream &inStrm, mpt::Charset defaultCharset); //Try to read old version (v.3) and return pointer to new instance if succesfull, else nullptr. SerializationResult InitDeserializeOLD(std::istream &inStrm, mpt::Charset defaultCharset); //Create GroupGeometric tuning of *this using virtual ProCreateGroupGeometric. bool CreateGroupGeometric(const std::vector &v, const RATIOTYPE &r, const NoteRange &range, const NOTEINDEXTYPE &ratiostartpos); //Create GroupGeometric of *this using ratios from 'itself' and ratios starting from //position given as third argument. bool CreateGroupGeometric(const NOTEINDEXTYPE &s, const RATIOTYPE &r, const NOTEINDEXTYPE &startindex); //Create geometric tuning of *this using ratio(0) = 1. bool CreateGeometric(const UNOTEINDEXTYPE &p, const RATIOTYPE &r); bool CreateGeometric(const UNOTEINDEXTYPE &s, const RATIOTYPE &r, const NoteRange &range); void UpdateFineStepTable(); // GroupPeriodic-specific. // Get the corresponding note in [0, period-1]. // For example GetRefNote(-1) is to return note :'groupsize-1'. MPT_FORCEINLINE NOTEINDEXTYPE GetRefNote(NOTEINDEXTYPE note) const { MPT_ASSERT(GetType() == Type::GROUPGEOMETRIC || GetType() == Type::GEOMETRIC); return static_cast(mpt::wrapping_modulo(note, GetGroupSize())); } static bool IsValidRatio(RATIOTYPE ratio) { return (ratio > static_cast(0.0)); } private: Tuning::Type m_TuningType; //Noteratios std::vector m_RatioTable; //'Fineratios' std::vector m_RatioTableFine; // The lowest index of note in the table NOTEINDEXTYPE m_NoteMin; //For groupgeometric tunings, tells the 'group size' and 'group ratio' //m_GroupSize should always be >= 0. NOTEINDEXTYPE m_GroupSize; RATIOTYPE m_GroupRatio; USTEPINDEXTYPE m_FineStepCount; // invariant: 0 <= m_FineStepCount <= FINESTEPCOUNT_MAX mpt::ustring m_TuningName; std::map m_NoteNameMap; }; // class CTuning } // namespace Tuning OPENMPT_NAMESPACE_END