Build an envelope for `target` from the visible (play-range) automation points of each clip in `sources`, in order. The audio side of `target` is left untouched; only its envelope is replaced. Boundary preserves are emitted at every clip's play start and play end. At seams between adjacent clips the preserves coincide in time: - if their values agree (within kSeamValueTolerance), the second emiss
| 2172 | // - if their values disagree, the second emission is appended via raw |
| 2173 | // Envelope::Insert after the first. |
| 2174 | void RebuildJoinedEnvelope( |
| 2175 | WaveClip& target, const WaveTrack::IntervalHolders& sources, int rate) |
| 2176 | { |
| 2177 | const auto& templateEnv = target.GetEnvelope(); |
| 2178 | auto rebuilt = std::make_unique<Envelope>( |
| 2179 | templateEnv.GetExponential(), |
| 2180 | templateEnv.GetMinValue(), |
| 2181 | templateEnv.GetMaxValue(), |
| 2182 | templateEnv.GetDefaultValue()); |
| 2183 | rebuilt->SetOffset(target.GetSequenceStartTime()); |
| 2184 | const double newSeqLen |
| 2185 | =target.GetSequenceEndTime() - target.GetSequenceStartTime(); |
| 2186 | const double sampleDur = 1.0 / rate; |
| 2187 | rebuilt->SetTrackLen(newSeqLen, sampleDur); |
| 2188 | |
| 2189 | const double interiorMargin = sampleDur / 2; |
| 2190 | // Tolerance for deciding whether two boundary values are the same. |
| 2191 | // Matches the constant used inside Envelope.cpp. |
| 2192 | constexpr double kSeamValueTolerance = 0.001; |
| 2193 | const double offset = rebuilt->GetOffset(); |
| 2194 | |
| 2195 | bool hasLast = false; |
| 2196 | double lastT = 0.0; |
| 2197 | double lastV = 0.0; |
| 2198 | |
| 2199 | auto emit = [&](double absT, double value) { |
| 2200 | const double relT = absT - offset; |
| 2201 | if (!hasLast || relT > lastT + interiorMargin) { |
| 2202 | rebuilt->Insert(relT, value); |
| 2203 | hasLast = true; |
| 2204 | lastT = relT; |
| 2205 | lastV = value; |
| 2206 | return; |
| 2207 | } |
| 2208 | // Coincident in time with the previous emission. |
| 2209 | if (fabs(value - lastV) <= kSeamValueTolerance) { |
| 2210 | return; // same point, suppress duplicate |
| 2211 | } |
| 2212 | // Genuine seam discontinuity: append a second point at the same |
| 2213 | // time with the new value. Raw indexed Insert is needed because |
| 2214 | // InsertOrReplace would overwrite the previous point. |
| 2215 | rebuilt->Insert( |
| 2216 | static_cast<int>(rebuilt->GetNumberOfPoints()), |
| 2217 | EnvPoint { relT, value }); |
| 2218 | lastV = value; |
| 2219 | }; |
| 2220 | |
| 2221 | // Returns true if the source envelope has an explicit control point at |
| 2222 | // absT (within interiorMargin). Used to decide whether to keep the |
| 2223 | // outer boundary points -- we skip them when they were synthesized by |
| 2224 | // the trim, but preserve them when the user had placed a real point |
| 2225 | // there before the join. |
| 2226 | auto hasExplicitPointAt = [&](const Envelope& env, double absT) { |
| 2227 | const double srcOffset = env.GetOffset(); |
| 2228 | const auto n = env.GetNumberOfPoints(); |
| 2229 | for (size_t j = 0; j < n; ++j) { |
| 2230 | const double t = srcOffset + env[static_cast<int>(j)].GetT(); |
| 2231 | if (t < absT - interiorMargin) { |
no test coverage detected