32 return pow > 0 ? 10 *
Get10Pow(pow - 1) : 1;
45 static constexpr std::array<size_t, 3> MIN_DIGITS { 3, 2, 2 };
46 static constexpr std::array<size_t, 3> UPPER_BOUNDS {
52 : mContext { context }
53 , mFracPart { fracPart }
54 , mFieldValueOffset { timeFormat ? 1 : 0 }
56 auto project = mContext.GetProject();
67 mTimeSignatureChangedSubscription =
74 UpdateFormat(*mContext.GetProject());
80 bool CheckField(
size_t fieldIndex,
int value)
const noexcept
82 if (fieldIndex >= mFields.size())
85 const auto digitsCount = mFields[fieldIndex].digits;
88 const auto lowerRange =
89 digitsCount > MIN_DIGITS[fieldIndex] ?
Get10Pow(digitsCount - 1) : 0;
91 const auto upperRange =
Get10Pow(digitsCount);
93 return value >= int(lowerRange) && value < int(upperRange);
98 if (mFracPart > newLts)
99 return CheckField(2, mFracPart / mLowerTimeSignature);
101 return mFields.size() == 2;
113 barsField.label = L
" " + mBarString + L
" ";
121 std::max<size_t>(UPPER_BOUNDS[1], mUpperTimeSignature + 1)));
123 beatsField.label = L
" " + mBeatString;
125 const auto hasFracPart = mFracPart > mLowerTimeSignature;
129 beatsField.label += L
" ";
132 std::max(11, mFracPart / mLowerTimeSignature + 1)));
137 for (
size_t i = 0; i < mFields.size(); i++)
139 mFields[i].pos = pos;
141 for (
size_t j = 0; j < mFields[i].digits; j++)
143 mDigits.push_back(
DigitInfo { i, j, pos });
147 pos += mFields[i].label.length();
155 const double newTempo = timeSignature.GetTempo();
156 const int newUts = timeSignature.GetUpperTimeSignature();
157 const int newLts = timeSignature.GetLowerTimeSignature();
159 if (newTempo == mTempo && newUts == mUpperTimeSignature && newLts == mLowerTimeSignature)
162 const bool formatOk = CheckField(1, newUts) && CheckFracField(newLts);
165 mUpperTimeSignature = newUts;
166 mLowerTimeSignature = newLts;
169 const auto quarterLength = 60.0 / mTempo;
170 const auto beatLength = quarterLength * 4.0 / mLowerTimeSignature;
171 const auto barLength = mUpperTimeSignature * beatLength;
173 mFieldLengths[0] = barLength;
174 mFieldLengths[1] = beatLength;
176 const auto hasFracPart = mFracPart > mLowerTimeSignature;
180 const auto fracLength = beatLength * mLowerTimeSignature / mFracPart;
181 mFieldLengths[2] = fracLength;
187 UpdateFields(MIN_DIGITS[0]);
193 value = std::max(0.0, value);
196 const auto barsCount =
201 static_cast<int>(std::floor(value / mFieldLengths[0]));
204 barsCount,
true, MIN_DIGITS[0]);
206 const auto oldDigits = mFields[0].digits;
208 const bool updateNeeded = canShrink ? oldDigits != barsField.digits :
209 oldDigits < barsField.digits;
214 UpdateFields(barsField.digits);
215 Publish({ value, oldDigits > mFields[0].digits });
220 for (
size_t fieldIndex = 0; fieldIndex < mFields.size(); ++fieldIndex)
234 for (
size_t fieldIndex = 0; fieldIndex < mFields.size (); ++fieldIndex)
236 const auto digitsCount = mFields[fieldIndex].digits;
238 for (
int digitIndex = 0; digitIndex < digitsCount; ++digitIndex)
242 UpdateResultString(result);
250 1.0 + std::max(1.0, value) * std::numeric_limits<double>::epsilon();
252 for (
size_t fieldIndex = 0; fieldIndex < mFields.size(); ++fieldIndex)
254 const auto fieldLength = mFieldLengths[fieldIndex];
255 const auto fieldValue = std::max(
256 0,
static_cast<int>(std::floor(value * eps / fieldLength)));
259 mFields[fieldIndex].formatStr, fieldValue + mFieldValueOffset);
261 value = value - fieldValue * fieldLength;
264 UpdateResultString(result);
268 std::optional<double>
StringToValue(
const wxString& valueString)
const override
271 mFields.size() > 0 &&
272 valueString.Mid(mFields[0].pos, 1) == wxChar(
'-'))
276 size_t lastIndex = 0;
278 for (
size_t i = 0; i < mFields.size(); i++)
280 const auto&
field = mFields[i];
282 const size_t labelIndex =
field.label.empty() ?
284 valueString.find(
field.label, lastIndex);
288 const auto fieldStringValue = valueString.Mid(
290 labelIndex ==
wxString::npos ? labelIndex : labelIndex - lastIndex);
292 if (!fieldStringValue.ToLong(&val))
295 t += (val - mFieldValueOffset) * mFieldLengths[i];
297 lastIndex = labelIndex +
field.label.Length();
303 double SingleStep(
double value,
int digitIndex,
bool upwards)
const override
305 if (digitIndex < 0 ||
size_t(digitIndex) >= mDigits.size())
308 const auto& digit = mDigits[digitIndex];
309 const auto& fieldIndex = digit.field;
310 const auto&
field = mFields[fieldIndex];
312 const auto stepSize = mFieldLengths[fieldIndex] *
313 std::pow(10,
field.digits - digit.index - 1);
315 return upwards ? value + stepSize : value - stepSize;
320 auto project = mContext.GetProject();
325 auto barString =
BarString.Translation();
328 if (barString == mBarString && beatString == mBeatString)
331 mBarString = barString;
332 mBeatString = beatString;
342 double mTempo { 0.0 };
344 int mUpperTimeSignature { 0 };
345 int mLowerTimeSignature { 0 };
351 std::array<double, 3> mFieldLengths {};
362 : mFracPart { fracPart }
363 , mTimeFormat { timeFormat }
367 std::unique_ptr<NumericConverterFormatter>
370 if (!IsAcceptableInContext(context))
373 return std::make_unique<BeatsFormatter>(context, mFracPart, mTimeFormat);
389 timeFormat ?
"beatsTime" :
"beatsDuration",
393 "beats",
XO(
"bar:beat"),
394 std::make_unique<BeatsNumericConverterFormatterFactory>(0, timeFormat)),
398 "beats16",
XO(
"bar:beat:tick"),
399 std::make_unique<BeatsNumericConverterFormatterFactory>(16, timeFormat)));
417 return std::make_unique<BeatsFormatter>(context, fracPart, timeFormat);
constexpr auto NumericConverterFormatterGroup
constexpr auto NumericConverterFormatterItem
const NumericConverterType & NumericConverterType_DURATION()
const NumericConverterType & NumericConverterType_TIME()
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
A context in which formatter operates.
bool HasProject() const
Returns true if the reference to the project is valid at this moment.
A move-only handle representing a connection to a Publisher.
A listener notified of changes in preferences.
static ProjectTimeSignature & Get(AudacityProject &project)
Generates classes whose instances register items at construction.
constexpr size_t npos(-1)
static NumericField WithDigits(size_t digits, bool zeropad=true)
static NumericField ForRange(size_t range, bool zeropad=true, size_t minDigits=0)