166 : mContext { context }
168 , mFormat { untranslatedFormat.Translation() }
169 , mUntranslatedFormat { untranslatedFormat }
173 if (IsTimeRelatedFormat())
175 auto project = mContext.GetProject();
180 mProjectRateChangedSubscription =
182 .Subscribe([
this](
const auto&) { UpdateFormat(); });
199 mFieldConfigs.clear();
201 mScalingFactor = 1.0;
206 int numWholeFields = 0;
207 int numFracFields = 0;
213 for (i = 0; i < mFormat.length(); i++)
215 bool handleDelim =
false;
216 bool handleNum =
false;
218 if (mFormat[i] ==
'|')
220 wxString remainder = mFormat.Right(mFormat.length() - i - 1);
222 remainder.Replace(
wxT(
","),
wxT(
"."));
224 mScalingFactorIsSamples = remainder ==
wxT(
"#");
226 if (mScalingFactorIsSamples)
228 mScalingFactor = mSampleRate;
230 else if (remainder ==
wxT(
"N"))
239 remainder.ToCDouble(&mScalingFactor);
240 i = mFormat.length() - 1;
241 if (!delimStr.empty())
247 (mFormat[i] >=
'0' && mFormat[i] <=
'9') ||
248 mFormat[i] ==
wxT(
'*') || mFormat[i] ==
wxT(
'#'))
250 numStr += mFormat[i];
251 if (!delimStr.empty())
256 delimStr += mFormat[i];
261 if (i == mFormat.length() - 1)
265 if (!delimStr.empty())
271 bool zeropad =
false;
274 if (numStr.Right(1) ==
wxT(
"#"))
275 range =
static_cast<long int>(mSampleRate);
276 else if (numStr.Right(1) !=
wxT(
"*"))
278 numStr.ToLong(&range);
280 if (numStr.GetChar(0) ==
'0' && numStr.length() > 1)
288 int base = fracMult * range;
289 mFieldConfigs.push_back({ inFrac, base, range });
297 for (j = 0; j < mFields.size(); j++)
298 mFieldConfigs[j].base *= range;
299 mFieldConfigs.push_back({ inFrac, 1, range });
308 bool goToFrac =
false;
312 wxChar delim = delimStr[delimStr.length() - 1];
313 if (delim ==
'<' || delim ==
'>')
316 if (delimStr.length() > 1)
317 delimStr = delimStr.BeforeLast(delim);
323 if (numFracFields == 0)
328 if (handleNum && numFracFields > 1)
329 mFields[mFields.size() - 2].label = delimStr;
331 mFields[mFields.size() - 1].label = delimStr;
335 if (numWholeFields == 0)
339 delimStr.Replace(
wxT(
"<"),
wxT(
","));
340 delimStr.Replace(
wxT(
">"),
wxT(
"."));
341 mFields[numWholeFields - 1].label = delimStr;
353 pos += mPrefix.length();
355 for (i = 0; i < mFields.size(); i++)
357 mFields[i].pos = pos;
359 for (
size_t j = 0; j < mFields[i].digits; j++)
361 mDigits.push_back(
DigitInfo { i, j, pos });
365 pos += mFields[i].label.length();
376 const auto newSampleRate = mContext.GetSampleRate();
378 const bool sampleRateChanged = newSampleRate != mSampleRate;
380 mSampleRate = newSampleRate;
382 if (mFields.empty() || (sampleRateChanged && mScalingFactorIsSamples))
387 double rawValue,
bool nearest)
const override
391 if (IsTimeRelatedFormat() && mContext.HasSampleRate())
392 rawValue = floor(rawValue * mSampleRate + (nearest ? 0.5f : 0.0f)) /
394 double theValue = rawValue * mScalingFactor
403 for (
size_t i = 0; i < mFields.size(); i++)
405 if (mFieldConfigs[i].frac)
411 t_int =
sampleCount(theValue + (nearest ? 0.5f : 0.0f));
414 wxASSERT(mFieldConfigs.back().frac);
415 theValue += (nearest ? 0.5f : 0.0f) / mFieldConfigs.back().base;
432 if (mNtscDrop && theValue >= 0)
434 frames = (int)(theValue * 30. / 1.001 + (nearest ? 0.5f : 0.0f));
435 tenMins = frames / 17982;
436 frames -= tenMins * 17982;
442 addMins = frames / 1798;
443 frames -= addMins * 1798;
459 t_int = mins * 60 + secs;
460 t_frac = frames / 30.;
463 for (
size_t i = 0; i < mFields.size(); i++)
465 long long value = -1;
467 if (mFieldConfigs[i].frac)
476 value = t_frac * mFieldConfigs[i].base;
487 if (mFieldConfigs[i].range > 0)
488 value = value % mFieldConfigs[i].range;
496 for (
int ii = 0; ii < mFields[i].digits; ++ii)
500 field = wxString::Format(mFields[i].formatStr, (
int)value);
512 const wxString& valueString)
const override
518 mFields.size() > 0 &&
519 valueString.Mid(mFields[0].pos, 1) == wxChar(
'-'))
522 for (i = 0; i < mFields.size(); i++)
524 const auto pos = mFields[i].pos;
525 const auto digits = mFields[i].digits;
527 if (pos >= valueString.size() || pos + digits > valueString.size())
532 const auto fieldStringValue =
533 valueString.Mid(mFields[i].pos, mFields[i].digits);
535 if (!fieldStringValue.ToLong(&val))
538 if (mFieldConfigs[i].frac)
539 t += (val / (double)mFieldConfigs[i].base);
541 t += (val * (double)mFieldConfigs[i].base);
548 int t_int = (int)(t + .000000001);
549 double t_frac = (t - t_int);
550 int tenMins = t_int / 600;
551 double frames = tenMins * 17982;
552 t_int -= tenMins * 600;
553 int mins = t_int / 60;
560 frames += addMins * 1798;
563 frames += t_int * 30 + t_frac * 30.;
567 frames += 28 + (t_int - 1) * 30 + t_frac * 30.;
569 frames += t_frac * 30. - 2.;
571 t = frames * 1.001 / 30.;
577 double SingleStep(
double value,
int digitIndex,
bool upwards)
const override
579 const auto dir = upwards ? 1 : -1;
580 for (
size_t i = 0; i < mFields.size(); i++)
583 (mDigits[digitIndex].pos >= mFields[i].pos) &&
584 (mDigits[digitIndex].pos < mFields[i].pos + mFields[i].digits))
589 value *= mScalingFactor;
591 const double mult = pow(
592 10., mFields[i].digits -
593 (mDigits[digitIndex].pos - mFields[i].pos) - 1);
595 if (mFieldConfigs[i].frac)
597 value += ((mult / (double)mFieldConfigs[i].base) * dir);
601 value += ((mult * (double)mFieldConfigs[i].base) * dir);
606 if ((value - (
int)value) * 30 < 2)
608 if ((((
int)value) % 60 == 0) && (((int)value) % 600 != 0))
610 value = (int)value + (dir > 0 ? 2. : -1.) / 30.;
620 value /= mScalingFactor;
625 auto result = ValueToString(value,
false);
627 return *StringToValue(result.valueString);
639 auto newFormat = mUntranslatedFormat.Translation();
641 if (mFormat == newFormat)
656 double mSampleRate { 1.0 };
660 bool mScalingFactorIsSamples {
false };
681 , fraction { fraction }
718 XO(
"01000,01000 seconds")
724 {
XO(
"seconds + milliseconds") },
730 {
XO(
"01000,01000>01000 seconds"),
741 XO(
"0100 h 060 m 060 s")
747 {
XO(
"dd:hh:mm:ss") },
753 XO(
"0100 days 024 h 060 m 060 s")
765 {
XO(
"0100 h 060 m 060>0100 s"),
778 {
XO(
"0100 h 060 m 060>01000 s"),
791 XO(
"0100 h 060 m 060 s+># samples")
804 XO(
"01000,01000,01000 samples|#")
810 {
XO(
"hh:mm:ss + film frames (24 fps)") },
818 XO(
"0100 h 060 m 060 s+>24 frames")
824 {
XO(
"film frames (24 fps)") },
829 XO(
"01000,01000 frames|24")
836 {
XO(
"hh:mm:ss + NTSC drop frames") },
843 XO(
"0100 h 060 m 060 s+>30 frames|N")
850 {
XO(
"hh:mm:ss + NTSC non-drop frames") },
858 XO(
"0100 h 060 m 060 s+>030 frames| .999000999")
864 {
XO(
"NTSC frames") },
870 XO(
"01000,01000 frames|29.97002997")
876 {
XO(
"hh:mm:ss + PAL frames (25 fps)") },
883 XO(
"0100 h 060 m 060 s+>25 frames")
889 {
XO(
"PAL frames (25 fps)") },
894 XO(
"01000,01000 frames|25")
900 {
XO(
"hh:mm:ss + CDDA frames (75 fps)") },
907 XO(
"0100 h 060 m 060 s+>75 frames")
913 {
XO(
"CDDA frames (75 fps)") },
918 XO(
"01000,01000 frames|75")
939 XO(
"010,01000>0100 Hz")
952 XO(
"01000>01000 kHz|0.001")
974 XO(
"100>01000 octaves|1.442695041"),
976 XO(
"thousandths of octaves")
983 {
XO(
"semitones + cents") },
990 XO(
"1000 semitones >0100 cents|17.312340491"),
992 XO(
"hundredths of cents")
1003 XO(
"10>01000 decades|0.434294482"),
1005 XO(
"thousandths of decades")
1020 : mType {
std::move(type) }
1026 mDependsOnSampleRate = mFormat.Debug().find(L
'#') !=
wxString::npos;
1029 std::unique_ptr<NumericConverterFormatter>
1032 if (!IsAcceptableInContext(context))
1035 return std::make_unique<ParsedNumericConverterFormatter>(
1036 mType, mFormat, context);
1053 const auto functionIdentifier = formatString.name.Internal();
1055 functionIdentifier, formatString.name,
1056 formatString.formatStrings.fraction,
1057 std::make_unique<ParsedNumericConverterFormatterFactory>(
1058 type, formatString.formatStrings.formatStr));
1087std::unique_ptr<NumericConverterFormatter>
1092 return std::make_unique<ParsedNumericConverterFormatter>(type,
format, context);
constexpr auto NumericConverterItems
constexpr auto NumericConverterFormatterGroup
constexpr auto NumericConverterFormatterItem
const NumericConverterType & NumericConverterType_BANDWIDTH()
const NumericConverterType & NumericConverterType_DURATION()
const NumericConverterType & NumericConverterType_FREQUENCY()
const NumericConverterType & NumericConverterType_TIME()
an object holding per-project preferred sample rate
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
A context in which formatter operates.
bool HasSampleRate() const
Returns true if it is possible to get a sample rate from this context.
An explicitly nonlocalized string, not meant for the user to see.
A move-only handle representing a connection to a Publisher.
A listener notified of changes in preferences.
Holds project sample rate.
static ProjectRate & Get(AudacityProject &project)
Generates classes whose instances register items at construction.
Holds a msgid for the translation catalog; may also bind format arguments.
Positions or offsets within audio files need a wide type.
long long as_long_long() const
constexpr size_t npos(-1)
const char * end(const char *str) noexcept
const char * begin(const char *str) noexcept
fastfloat_really_inline void round(adjusted_mantissa &am, callback cb) noexcept
static NumericField ForRange(size_t range, bool zeropad=true, size_t minDigits=0)