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 "CommandManager.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 == NULL)
92 {
93 context.Error(wxT("No tracks selected! Select two tracks to compare."));
94 return false;
95 }
96 mTrack1 = * ++ trackRange.first;
97 if (mTrack1 == NULL)
98 {
99 context.Error(wxT("Only one track selected! Select two tracks to compare."));
100 return false;
101 }
102 if ( * ++ trackRange.first )
103 {
104 context.Status(wxT("More than two tracks selected - only the first two will be compared."));
105 }
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 auto position = s0;
142 auto length = s1 - s0;
143 while (position < s1)
144 {
145 // Get a block of data into the buffers
146 auto block = limitSampleBufferSize(
147 mTrack0->GetBestBlockSize(position), s1 - position
148 );
149 mTrack0->GetFloats(buff0.get(), position, block);
150 mTrack1->GetFloats(buff1.get(), position, block);
151
152 for (decltype(block) buffPos = 0; buffPos < block; ++buffPos)
153 {
154 if (CompareSample(buff0[buffPos], buff1[buffPos]) > errorThreshold)
155 {
156 ++errorCount;
157 }
158 }
159
160 position += block;
161 context.Progress(
162 (position - s0).as_double() /
163 length.as_double()
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 MenuTable;
177
178// Register menu items
179
181 wxT("Optional/Extra/Part2/Scriptables2"),
182 // Note that the PLUGIN_SYMBOL must have a space between words,
183 // whereas the short-form used here must not.
184 // (So if you did write "Compare Audio" for the PLUGIN_SYMBOL name, then
185 // you would have to use "CompareAudio" here.)
186 Command( wxT("CompareAudio"), XXO("Compare Audio..."),
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
bool GetFloats(float *buffer, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
Retrieve samples from a track in floating-point format, regardless of the storage format.
Definition: SampleTrack.h:82
double LongSamplesToTime(sampleCount pos) const
Convert correctly between a number of samples and an (absolute) time in seconds.
Definition: SampleTrack.cpp:48
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: SampleTrack.cpp:43
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:625
wxString GetName() const
Definition: Track.h:467
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:487
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1457
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:219
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
A Track that contains audio waveform data.
Definition: WaveTrack.h:51
size_t GetMaxBlockSize() const override
This returns a nonnegative number of samples meant to size a memory buffer.
Definition: WaveTrack.cpp:1611
size_t GetBestBlockSize(sampleCount t) const override
This returns a nonnegative number of samples meant to size a memory buffer.
Definition: WaveTrack.cpp:1593
AUDACITY_DLL_API void OnAudacityCommand(const CommandContext &ctx)
std::unique_ptr< CommandItem > Command(const CommandID &name, const TranslatableString &label_in, void(Handler::*pmf)(const CommandContext &), CommandFlag flags, const CommandManager::Options &options={}, CommandHandlerFinder finder=FinderScope::DefaultFinder())
BuiltinCommandsModule::Registration< CompareAudioCommand > reg