Audacity 3.2.0
CompareAudioCommand.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity - A Digital Audio Editor
4 Copyright 1999-2018 Audacity Team
5 License: wxwidgets
6
7 Dan Horgan
8 James Crook
9
10******************************************************************//*******************************************************************/
20
21
22#include "CompareAudioCommand.h"
23
24#include "CommandDispatch.h"
25#include "MenuRegistry.h"
26#include "../CommonCommandFlags.h"
27#include "LoadCommands.h"
28#include "ViewInfo.h"
29#include "WaveTrack.h"
30
31
32#include <float.h>
33
34#include "SettingsVisitor.h"
35#include "ShuttleGui.h"
36#include "AudacityMessageBox.h"
37#include "../widgets/valnum.h"
38#include "CommandContext.h"
39
41{ XO("Compare Audio") };
42
44
46 R.AddCommand( std::make_unique<CompareAudioCommand>() );
47// std::unique_ptr<CommandOutputTargets> &&target
48// return std::make_shared<CompareAudioCommand>(*this, std::move(target));
49
50}
51
52template<bool Const>
54 S.Define( errorThreshold, wxT("Threshold"), 0.0f, 0.0f, 0.01f, 1.0f );
55 return true;
56}
58 { return VisitSettings<false>(S); }
59
61 { return VisitSettings<true>(S); }
62
64{
65 S.AddSpace(0, 5);
66
67 S.StartMultiColumn(2, wxALIGN_CENTER);
68 {
69 S.TieTextBox(XXO("Threshold:"),errorThreshold);
70 }
71 S.EndMultiColumn();
72}
73
74// Update member variables with project selection data (and validate)
76{
77 // Get the selected time interval
78 auto &selectedRegion = ViewInfo::Get( proj ).selectedRegion;
79 mT0 = selectedRegion.t0();
80 mT1 = selectedRegion.t1();
81 if (mT0 >= mT1)
82 {
83 context.Error(wxT("There is no selection!"));
84 return false;
85 }
86
87 // Get the selected tracks and check that there are at least two to
88 // compare
89 auto trackRange = TrackList::Get(proj).Selected<const WaveTrack>();
90 mTrack0 = *trackRange.first;
91 if (!mTrack0) {
92 context.Error(wxT("No tracks selected! Select two tracks to compare."));
93 return false;
94 }
95 mTrack1 = * ++ trackRange.first;
96 if (!mTrack1) {
97 context.Error(wxT("Only one track selected! Select two tracks to compare."));
98 return false;
99 }
100 if (mTrack0->NChannels() != mTrack1->NChannels()) {
101 context.Error(wxT("Selected tracks must have the same number of channels!"));
102 return false;
103 }
104 if (* ++ trackRange.first)
105 context.Status(wxT("More than two tracks selected - only the first two will be compared."));
106 return true;
107}
108
109double CompareAudioCommand::CompareSample(double value1, double value2)
110{
111 return fabs(value1 - value2);
112}
113
114inline int min(int a, int b)
115{
116 return (a < b) ? a : b;
117}
118
120{
121 if (!GetSelection(context, context.project))
122 {
123 return false;
124 }
125
126 wxString msg = wxT("Comparing tracks '");
127 msg += mTrack0->GetName() + wxT("' and '")
128 + mTrack1->GetName() + wxT("'.");
129 context.Status(msg);
130
131 long errorCount = 0;
132 // Initialize buffers for track data to be analyzed
134
135 Floats buff0{ buffSize };
136 Floats buff1{ buffSize };
137
138 // Compare tracks block by block
139 auto s0 = mTrack0->TimeToLongSamples(mT0);
140 auto s1 = mTrack0->TimeToLongSamples(mT1);
141 const auto channels0 = mTrack0->Channels();
142 auto iter = mTrack1->Channels().begin();
143 for (const auto pChannel0 : channels0) {
144 const auto pChannel1 = *iter++;
145 auto position = s0;
146 auto length = s1 - s0;
147 while (position < s1) {
148 // Get a block of data into the buffers
149 auto block = limitSampleBufferSize(
150 pChannel0->GetBestBlockSize(position), s1 - position
151 );
152 pChannel0->GetFloats(buff0.get(), position, block);
153 pChannel1->GetFloats(buff1.get(), position, block);
154
155 for (decltype(block) buffPos = 0; buffPos < block; ++buffPos)
156 if (CompareSample(buff0[buffPos], buff1[buffPos]) > errorThreshold)
157 ++errorCount;
158
159 position += block;
160 context.Progress(
161 (position - s0).as_double() /
162 length.as_double()
163 );
164 }
165 }
166
167 // Output the results
168 double errorSeconds = mTrack0->LongSamplesToTime(errorCount);
169 context.Status(wxString::Format(wxT("%li"), errorCount));
170 context.Status(wxString::Format(wxT("%.4f"), errorSeconds));
171 context.Status(wxString::Format(wxT("Finished comparison: %li samples (%.3f seconds) exceeded the error threshold of %f."), errorCount, errorSeconds, errorThreshold));
172 return true;
173}
174
175namespace {
176using namespace MenuRegistry;
177
178// Register menu items
179
181 // Note that the PLUGIN_SYMBOL must have a space between words,
182 // whereas the short-form used here must not.
183 // (So if you did write "Compare Audio" for the PLUGIN_SYMBOL name, then
184 // you would have to use "CompareAudio" here.)
185 Command( wxT("CompareAudio"), XXO("Compare Audio..."),
187 wxT("Optional/Extra/Part2/Scriptables2")
188};
189}
wxT("CloseDown"))
const ReservedCommandFlag & AudioIONotBusyFlag()
void RegisterCompareAudio(Registrar &R)
int min(int a, int b)
Contains declaration of CompareAudioCommand and CompareAudioCommandType classes.
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
#define S(N)
Definition: ToChars.cpp:64
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
virtual void Progress(double d) const
virtual void Error(const wxString &message) const
virtual void Status(const wxString &message, bool bFlush=false) const
AudacityProject & project
double CompareSample(double value1, double value2)
void PopulateOrExchange(ShuttleGui &S) override
const WaveTrack * mTrack1
bool Apply(const CommandContext &context) override
bool GetSelection(const CommandContext &context, AudacityProject &proj)
static const ComponentInterfaceSymbol Symbol
const WaveTrack * mTrack0
bool VisitSettings(SettingsVisitorBase< Const > &S)
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
Base class for registration callback. Audacity will call providers RegisterNameOfThing() functions wi...
Definition: Registrar.h:33
virtual void AddCommand(std::unique_ptr< AudacityCommand > &&WXUNUSED(command))
Definition: Registrar.h:46
Generates classes whose instances register items at construction.
Definition: Registry.h:388
Visitor of effect or command parameters. This is a base class with lots of virtual functions that do ...
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
const wxString & GetName() const
Name is always the same for all channels of a group.
Definition: Track.cpp:64
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:314
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:967
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:216
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
auto Channels()
Definition: WaveTrack.h:263
size_t NChannels() const override
A constant property.
Definition: WaveTrack.cpp:530
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:2279
double LongSamplesToTime(sampleCount pos) const
sampleCount TimeToLongSamples(double t0) const
AUDACITY_DLL_API void OnAudacityCommand(const CommandContext &ctx)
constexpr auto Command
Definition: MenuRegistry.h:456
BuiltinCommandsModule::Registration< CompareAudioCommand > reg