载入中...
搜索中...
未找到
AudioWaveform.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <limits>
6
7namespace eve::audio_editing {
8
10 int maxPixels, std::size_t maxSamples) const {
12 result.asset = request.asset;
13 result.sourceRevision = request.sourceRevision;
14 result.sampleRate = pcm.sampleRate;
15 result.channels = pcm.channels;
16 if (request.asset.empty() || request.pixelWidth <= 0 || request.pixelWidth > maxPixels || maxPixels <= 0 ||
17 pcm.sampleRate <= 0 || pcm.channels <= 0 || pcm.channels > 32 || pcm.samples.empty() ||
18 pcm.samples.size() > maxSamples || pcm.samples.size() % static_cast<std::size_t>(pcm.channels) != 0) {
19 result.status = EditorStatus::Rejected;
21 eve::DiagnosticCode::InvalidArgument, RuleId("editor.audio.invalid-waveform-input"),
22 DiagnosticSeverity::Error,
23 "Waveform requires bounded, aligned PCM and a valid asset/sample format"));
24 return result;
25 }
26 for (float sample : pcm.samples)
27 if (!std::isfinite(sample)) {
28 result.status = EditorStatus::Rejected;
30 eve::DiagnosticCode::InvalidArgument, RuleId("editor.audio.nonfinite-pcm"),
31 DiagnosticSeverity::Error, "Decoded PCM contains a non-finite sample"));
32 return result;
33 }
34 const std::size_t frameCount = pcm.samples.size() / static_cast<std::size_t>(pcm.channels);
35 result.duration = static_cast<double>(frameCount) / pcm.sampleRate;
36 const int buckets = std::min<int>(request.pixelWidth, static_cast<int>(frameCount));
37 result.envelopes.assign(static_cast<std::size_t>(pcm.channels), {});
38 for (auto& channel : result.envelopes) channel.reserve(static_cast<std::size_t>(buckets));
39 for (int bucket = 0; bucket < buckets; ++bucket) {
40 const std::size_t begin = static_cast<std::size_t>(bucket) * frameCount / buckets;
41 const std::size_t end = std::max(begin + 1, static_cast<std::size_t>(bucket + 1) * frameCount / buckets);
42 for (int channel = 0; channel < pcm.channels; ++channel) {
43 float minimum = std::numeric_limits<float>::infinity();
44 float maximum = -std::numeric_limits<float>::infinity();
45 double squares = 0.0;
46 for (std::size_t frame = begin; frame < end; ++frame) {
47 const float sample = pcm.samples[frame * static_cast<std::size_t>(pcm.channels) + channel];
48 minimum = std::min(minimum, sample);
49 maximum = std::max(maximum, sample);
50 squares += static_cast<double>(sample) * sample;
51 }
52 result.envelopes[static_cast<std::size_t>(channel)].push_back(
53 {static_cast<double>(begin) / pcm.sampleRate, static_cast<double>(end) / pcm.sampleRate, minimum,
54 maximum, static_cast<float>(std::sqrt(squares / static_cast<double>(end - begin)))});
55 }
56 }
57 if (request.loopEnd > 0.0 || request.loopStart > 0.0) {
58 if (!std::isfinite(request.loopStart) || !std::isfinite(request.loopEnd) || request.loopStart < 0.0 ||
59 request.loopEnd <= request.loopStart || request.loopEnd > result.duration) {
60 result.status = EditorStatus::Rejected;
63 RuleId("editor.audio.invalid-loop-range"), DiagnosticSeverity::Error,
64 "Loop range must be ordered and within decoded clip duration"));
65 return result;
66 }
67 const auto bucketAt = [&](double seconds) {
68 return std::min(buckets - 1, static_cast<int>(seconds / result.duration * buckets));
69 };
70 result.loopStartBucket = bucketAt(request.loopStart);
71 result.loopEndBucket = bucketAt(request.loopEnd);
72 const std::size_t startFrame =
73 std::min(frameCount - 1, static_cast<std::size_t>(request.loopStart * pcm.sampleRate));
74 const std::size_t endFrame =
75 std::min(frameCount - 1, static_cast<std::size_t>(request.loopEnd * pcm.sampleRate));
76 double maximumDelta = 0.0;
77 for (int channel = 0; channel < pcm.channels; ++channel) {
78 const float start = pcm.samples[startFrame * static_cast<std::size_t>(pcm.channels) + channel];
79 const float end = pcm.samples[endFrame * static_cast<std::size_t>(pcm.channels) + channel];
80 maximumDelta = std::max(maximumDelta, std::abs(static_cast<double>(end - start)));
81 }
82 result.loopSeamDelta = maximumDelta;
83 if (maximumDelta > 0.1)
86 RuleId("editor.audio.loop-seam-discontinuity"), DiagnosticSeverity::Warning,
87 "Loop endpoints have a large amplitude discontinuity and may click"));
88 }
89 result.status = EditorStatus::Applied;
90 return result;
91}
92
93} // namespace eve::audio_editing
Duration start
float maximum[3]
float minimum[3]
const GltfImportRequest & request
float begin
AudioWaveformResult generate(const AudioWaveformRequest &request, const EditorAudioPcm &pcm, int maxPixels=16384, std::size_t maxSamples=100000000) const
Build bounded per-channel envelopes from already-decoded PCM.
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
Immutable waveform request tied to an audio document revision.
Revision-tagged waveform and loop-preview diagnostics.
std::vector< std::vector< AudioWaveformBucket > > envelopes
std::vector< EditorDiagnostic > diagnostics
Decoded interleaved PCM supplied by an audio importer or codec plugin.
uint32_t bucket