39 return filename.substr(filename.find_last_of(
"/\\") + 1);
48std::optional<ProjectSyncInfo>
51 if (in.
tags.has_value() && in.
tags->isOneShot)
55 std::optional<double> bpm;
56 std::optional<TimeSignature> timeSignature;
57 std::optional<TempoObtainedFrom> usedMethod;
59 if (in.
tags.has_value() && in.
tags->bpm.has_value() && *in.
tags->bpm > 30.)
74 timeSignature = meter->timeSignature;
83 auto recommendedStretch = 1.0;
90 auto excessDurationInQuarternotes = 0.;
92 const auto roundedNumQuarters =
std::round(numQuarters);
93 const auto delta = numQuarters - roundedNumQuarters;
95 if (0 < delta && delta < 1. / 8)
96 excessDurationInQuarternotes = delta;
103 excessDurationInQuarternotes,
113 const std::regex bpmRegex {
114 R
"((?:.*(?:_|-|\s|\.|/|\\))?(\d+)(?:_|-|\s|\.)?bpm(?:(?:_|-|\s|\.).*)?)",
118 if (std::regex_match(filename, matches, bpmRegex))
121 const auto value = std::stoi(matches[1]);
122 return 30 <= value && value <= 300 ? std::optional<double> { value } :
125 catch (
const std::invalid_argument& e)
134 const std::function<
void(
double)>& progressCallback,
137 if (
audio.GetSampleRate() <= 0)
139 const auto duration = 1. *
audio.GetNumSamples() /
audio.GetSampleRate();
146 decimatedAudio, tolerance, progressCallback, debugOutput);
150 const std::vector<std::shared_ptr<AnalyzedAudioClip>>& clips,
153 const auto isBeatsAndMeasures =
project.ViewIsBeatsAndMeasures();
155 if (!projectWasEmpty && !isBeatsAndMeasures)
158 const auto projectTempo =
159 !projectWasEmpty ? std::make_optional(
project.GetTempo()) : std::nullopt;
162 clips.begin(), clips.end(),
163 [](
const std::shared_ptr<AnalyzedAudioClip>& clip) {
164 return clip->GetSyncInfo().has_value();
171 if (!
project.ViewIsBeatsAndMeasures())
174 clips.begin(), clips.end(),
175 [&](
const std::shared_ptr<AnalyzedAudioClip>& clip) {
181 if (!projectWasEmpty && isBeatsAndMeasures)
184 const auto [loopIndices, oneshotIndices] = [&] {
185 std::vector<size_t> loopIndices;
186 std::vector<size_t> oneshotIndices;
187 for (
size_t i = 0; i < clips.size(); ++i)
188 if (clips[i]->GetSyncInfo().has_value())
189 loopIndices.push_back(i);
191 oneshotIndices.push_back(i);
192 return std::make_pair(loopIndices, oneshotIndices);
197 std::unordered_map<TempoObtainedFrom, size_t> indexMap;
198 std::for_each(loopIndices.begin(), loopIndices.end(), [&](
size_t i) {
199 const auto usedMethod = clips[i]->GetSyncInfo()->usedMethod;
200 if (!indexMap.count(usedMethod))
201 indexMap[usedMethod] = i;
210 const auto& chosenSyncInfo = *clips[chosenIndex]->GetSyncInfo();
211 const auto isSingleFileImport = clips.size() == 1;
212 if (!
project.ShouldBeReconfigured(
213 chosenSyncInfo.rawAudioTempo, isSingleFileImport))
217 chosenSyncInfo.rawAudioTempo, chosenSyncInfo.timeSignature);
221 std::for_each(oneshotIndices.begin(), oneshotIndices.end(), [&](
size_t i) {
222 clips[i]->SetRawAudioTempo(chosenSyncInfo.rawAudioTempo);
Our MIR operations do not need the full 44.1 or 48kHz resolution typical of audio files....
double GetDuration() const
std::optional< MusicalMeter > GetMusicalMeterFromSignal(const MirAudioReader &audio, FalsePositiveTolerance tolerance, const std::function< void(double)> &progressCallback, QuantizationFitDebugOutput *debugOutput)
std::optional< double > GetBpmFromFilename(const std::string &filename)
std::optional< MusicalMeter > GetMeterUsingTatumQuantizationFit(const MirAudioReader &audio, FalsePositiveTolerance tolerance, const std::function< void(double)> &progressCallback, QuantizationFitDebugOutput *debugOutput)
Get the BPM of the given audio file, using the Tatum Quantization Fit method.
std::optional< ProjectSyncInfo > GetProjectSyncInfo(const ProjectSyncInfoInput &in)
void SynchronizeProject(const std::vector< std::shared_ptr< AnalyzedAudioClip > > &clips, ProjectInterface &project, bool projectWasEmpty)
fastfloat_really_inline void round(adjusted_mantissa &am, callback cb) noexcept
"finally" as in The C++ Programming Language, 4th ed., p. 358 Useful for defining ad-hoc RAII actions...