├── .gitignore ├── licence.txt └── src ├── .nuget ├── NuGet.Config ├── NuGet.exe └── NuGet.targets ├── Settings.StyleCop ├── SoundFingerprinting.DuplicatesDetector.sln └── SoundFingerprinting.DuplicatesDetector ├── App.xaml ├── App.xaml.cs ├── Application.ico ├── DuplicatesDetectorFacade.cs ├── DuplicatesDetectorService.cs ├── Images └── icon.png ├── Infrastructure ├── CSVWriter.cs ├── ServiceContainer.cs ├── ServiceInjector.cs └── TrackHelper.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── MainWindowResourceDictionary.xaml ├── Model ├── Item.cs └── ResultItem.cs ├── Permutations └── perms.csv ├── Properties └── AssemblyInfo.cs ├── RelayCommand.cs ├── Services ├── FolderBrowserDialogService.cs ├── GenericViewWindowService.cs ├── IFolderBrowserDialogService.cs ├── IGenericViewWindow.cs ├── IMessageBoxService.cs ├── IOpenFileDialogService.cs ├── ISaveFileDialogService.cs ├── IWindowService.cs ├── MessageBoxService.cs ├── OpenFileDialogService.cs ├── SaveFileDialogService.cs └── WindowService.cs ├── Settings.StyleCop ├── SoundFingerprinting.DuplicatesDetector.csproj ├── Themes ├── Brushes.xaml ├── Converters.xaml ├── Datagrid.xaml ├── ItemContStyle.xaml ├── ProgressBar.xaml ├── RoundedButton.xaml ├── SetIdConverter.cs └── TextBlock.xaml ├── View ├── GenericView.xaml ├── GenericView.xaml.cs ├── PathListView.xaml ├── PathListView.xaml.cs ├── ReportView.xaml └── ReportView.xaml.cs ├── ViewModel ├── BooleanToVisibilityConverter.cs ├── GenericViewModel.cs ├── Helper.cs ├── MainWindowViewModel.cs ├── PathListViewModel.cs ├── ReportViewModel.cs └── ViewModelBase.cs └── app.config /.gitignore: -------------------------------------------------------------------------------- 1 | release/ 2 | debug/ 3 | msbuild.log 4 | *.suo 5 | *.user 6 | bin 7 | Bin 8 | obj 9 | _ReSharper* 10 | *.csproj.user 11 | *.resharper.user 12 | *.suo 13 | *.cache 14 | *.nupkg 15 | TestResult.xml 16 | src/TestResults/ 17 | .vagrant 18 | packages*/ 19 | *.exe 20 | !NuGet.exe 21 | .vs/ -------------------------------------------------------------------------------- /licence.txt: -------------------------------------------------------------------------------- 1 | This file is part of Soundfingerprinting project - https://github.com/AddictedCS/soundfingerprinting 2 | 3 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 4 | 5 | You should have received a copy of the MIT along with Soundfingerprinting If not, see . -------------------------------------------------------------------------------- /src/.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AddictedCS/soundfingerprinting.duplicatesdetector/3cb38f9e2d722003c71a3adc5fdbd23e4948ae5c/src/.nuget/NuGet.exe -------------------------------------------------------------------------------- /src/.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | $([System.IO.Path]::Combine($(ProjectDir), "packages.config")) 32 | 33 | 34 | 35 | 36 | $(SolutionDir).nuget 37 | packages.config 38 | 39 | 40 | 41 | 42 | $(NuGetToolsPath)\NuGet.exe 43 | @(PackageSource) 44 | 45 | "$(NuGetExePath)" 46 | mono --runtime=v4.0.30319 $(NuGetExePath) 47 | 48 | $(TargetDir.Trim('\\')) 49 | 50 | -RequireConsent 51 | -NonInteractive 52 | 53 | "$(SolutionDir) " 54 | "$(SolutionDir)" 55 | 56 | 57 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) 58 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 59 | 60 | 61 | 62 | RestorePackages; 63 | $(BuildDependsOn); 64 | 65 | 66 | 67 | 68 | $(BuildDependsOn); 69 | BuildPackage; 70 | 71 | 72 | 73 | 74 | 75 | 76 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | 98 | 100 | 101 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /src/Settings.StyleCop: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | False 8 | 9 | 10 | 11 | 12 | False 13 | 14 | 15 | 16 | 17 | False 18 | 19 | 20 | 21 | 22 | False 23 | 24 | 25 | 26 | 27 | False 28 | 29 | 30 | 31 | 32 | False 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | False 43 | 44 | 45 | 46 | 47 | False 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoundFingerprinting.DuplicatesDetector", "SoundFingerprinting.DuplicatesDetector\SoundFingerprinting.DuplicatesDetector.csproj", "{572367D4-0F0C-4DC9-BE1C-4D037530D785}" 5 | EndProject 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{117D487C-9775-4689-B639-5B80E73DA38A}" 7 | ProjectSection(SolutionItems) = preProject 8 | .nuget\NuGet.Config = .nuget\NuGet.Config 9 | .nuget\NuGet.exe = .nuget\NuGet.exe 10 | .nuget\NuGet.targets = .nuget\NuGet.targets 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {572367D4-0F0C-4DC9-BE1C-4D037530D785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {572367D4-0F0C-4DC9-BE1C-4D037530D785}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {572367D4-0F0C-4DC9-BE1C-4D037530D785}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {572367D4-0F0C-4DC9-BE1C-4D037530D785}.Release|Any CPU.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | EndGlobal 28 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/App.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/App.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector 2 | { 3 | using System.Windows; 4 | 5 | using SoundFingerprinting.DuplicatesDetector.Infrastructure; 6 | using SoundFingerprinting.DuplicatesDetector.Services; 7 | using SoundFingerprinting.DuplicatesDetector.ViewModel; 8 | 9 | public partial class App 10 | { 11 | protected override void OnStartup(StartupEventArgs e) 12 | { 13 | base.OnStartup(e); 14 | MainWindow window = new MainWindow(); 15 | ServiceInjector.InjectServices(); 16 | ViewModelBase mainViewModel = new MainWindowViewModel(); 17 | window.DataContext = mainViewModel; 18 | window.Show(); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Application.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AddictedCS/soundfingerprinting.duplicatesdetector/3cb38f9e2d722003c71a3adc5fdbd23e4948ae5c/src/SoundFingerprinting.DuplicatesDetector/Application.ico -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/DuplicatesDetectorFacade.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector 2 | { 3 | using System; 4 | using System.Collections.Concurrent; 5 | using System.Collections.Generic; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | using SoundFingerprinting.Audio; 10 | using SoundFingerprinting.DAO.Data; 11 | using SoundFingerprinting.Data; 12 | using SoundFingerprinting.DuplicatesDetector.Infrastructure; 13 | using SoundFingerprinting.DuplicatesDetector.Model; 14 | using SoundFingerprinting.DuplicatesDetector.ViewModel; 15 | 16 | /// 17 | /// Facade which prepares the data for analysis of the tracks (does all the "dirty job") 18 | /// 19 | internal class DuplicatesDetectorFacade : IDisposable 20 | { 21 | /// 22 | /// Maximum track length (track's bigger than this value will be discarded) 23 | /// 24 | private const int MaxTrackLength = 60 * 10; /*10 min - maximal track length*/ 25 | 26 | /// 27 | /// Number of seconds to process from each song 28 | /// 29 | private const int SecondsToProcess = 20; 30 | 31 | /// 32 | /// Starting processing point 33 | /// 34 | private const int StartProcessingAtSecond = 20; 35 | 36 | /// 37 | /// Buffer size of the application reading songs 38 | /// 39 | /// 40 | /// Represented in MB. 41 | /// Max 100MB will be reserved for the samples read from songs 42 | /// 43 | private const int BufferSize = 100; 44 | 45 | /// 46 | /// Minimum track length (track's less than this value will be discarded) 47 | /// 48 | private const int MinTrackLength = SecondsToProcess + StartProcessingAtSecond + 1; 49 | 50 | /// 51 | /// Down sampling rate 52 | /// 53 | /// 54 | /// If you want to change this, contact ciumac.sergiu@gmail.com 55 | /// 56 | private const int SampleRate = 5512; 57 | 58 | private readonly DuplicatesDetectorService duplicatesDetectorService; 59 | 60 | private readonly TrackHelper trackHelper; 61 | 62 | private CancellationTokenSource cts; 63 | 64 | public DuplicatesDetectorFacade(DuplicatesDetectorService duplicatesDetectorService, TrackHelper trackHelper) 65 | { 66 | cts = new CancellationTokenSource(); 67 | this.duplicatesDetectorService = duplicatesDetectorService; 68 | this.trackHelper = trackHelper; 69 | } 70 | 71 | ~DuplicatesDetectorFacade() 72 | { 73 | Dispose(false); 74 | } 75 | 76 | /// 77 | /// Process the tracks asynchronously (get their path location, fingerprint content, hash fingerprint into storage) 78 | /// 79 | /// Paths to be processed 80 | /// File filters used 81 | /// Callback invoked once processing ends 82 | /// Callback invoked once 1 track is processed 83 | public void ProcessTracksAsync( 84 | IEnumerable paths, 85 | string[] fileFilters, 86 | Action, Exception> callback, 87 | Action trackProcessed) 88 | { 89 | var files = new List(); 90 | foreach (var path in paths) 91 | { 92 | if (path.IsFolder) 93 | { 94 | files.AddRange(Helper.GetMusicFiles(path.Path, fileFilters)); // get music file names 95 | } 96 | else 97 | { 98 | files.Add(path.Path); 99 | } 100 | } 101 | 102 | Task.Factory.StartNew( 103 | () => 104 | { 105 | try 106 | { 107 | var tracks = ProcessFiles(files, trackProcessed); 108 | callback.Invoke(tracks, null); 109 | } 110 | catch (AggregateException) /*here we are sure all consumers are done processing*/ 111 | { 112 | callback.Invoke(null, null); 113 | duplicatesDetectorService.ClearStorage(); /*its safe to clear the storage, no more thread is executing*/ 114 | } 115 | catch (Exception ex) 116 | { 117 | callback.Invoke(null, ex); 118 | } 119 | }, 120 | cts.Token); 121 | } 122 | 123 | /// 124 | /// Find all duplicate files from the storage 125 | /// 126 | /// Callback invoked at each processed track 127 | /// Set of tracks that are duplicate 128 | public HashSet[] FindAllDuplicates(Action callback) 129 | { 130 | return duplicatesDetectorService.FindDuplicates(callback); 131 | } 132 | 133 | /// 134 | /// Abort processing the files (at any stage) 135 | /// 136 | public void AbortProcessing() 137 | { 138 | cts.Cancel(); 139 | cts = new CancellationTokenSource(); 140 | } 141 | 142 | public void Dispose() 143 | { 144 | Dispose(true); 145 | GC.SuppressFinalize(this); 146 | } 147 | 148 | protected virtual void Dispose(bool isDisposing) 149 | { 150 | if (isDisposing) 151 | { 152 | cts.Dispose(); 153 | } 154 | } 155 | 156 | /// 157 | /// Process files (get fingerprint signatures, hash them into storage) 158 | /// 159 | /// List of files to be hashed 160 | /// Callback invoked once 1 track is processed 161 | /// List of processed tracks 162 | private List ProcessFiles(IEnumerable files, Action processed) 163 | { 164 | /*preprocessing stage ended, now make sure to do the actual job*/ 165 | 166 | int numProcs = Environment.ProcessorCount; 167 | 168 | // 1024 (Kb) * BufferSize / SampleRate * SecondsRead * 4 (1 float = 4 bytes) / 1024 (Kb) 169 | const int Buffersize = 170 | (int)((1024.0 * BufferSize) / ((double)SampleRate * SecondsToProcess / 1000 * 4 / 1024)); 171 | 172 | // ~317 songs are allowed for 15 seconds snippet at 5512 Hz sample rate 173 | var buffer = new BlockingCollection>(Buffersize); 174 | var processedtracks = new List(); 175 | var consumers = new List(); 176 | var producers = new List(); 177 | CancellationToken token = cts.Token; 178 | var bag = new ConcurrentBag(files); 179 | 180 | for (var i = 0; i < numProcs; i++) 181 | { 182 | /*producers*/ 183 | producers.Add(Task.Factory.StartNew( 184 | () => 185 | { 186 | while (!bag.IsEmpty) 187 | { 188 | if (token.IsCancellationRequested) 189 | { 190 | return; 191 | } 192 | 193 | string file; 194 | if (!bag.TryTake(out file)) 195 | { 196 | return; 197 | } 198 | 199 | TrackInfo track; 200 | AudioSamples samples; 201 | try 202 | { 203 | track = trackHelper.GetTrack(MinTrackLength, MaxTrackLength, file); 204 | samples = trackHelper.GetTrackSamples(track, SampleRate, SecondsToProcess, StartProcessingAtSecond); 205 | } 206 | catch 207 | { 208 | continue; 209 | /*Continue processing even if getting samples failed*/ 210 | /*the failing might be caused by a bunch of File I/O factors, that cannot be considered critical*/ 211 | } 212 | 213 | try 214 | { 215 | buffer.TryAdd(new Tuple(track, samples), 1, token); /*producer*/ 216 | } 217 | catch (OperationCanceledException) 218 | { 219 | /*it is safe to break here, operation was canceled*/ 220 | break; 221 | } 222 | } 223 | }, 224 | token)); 225 | } 226 | 227 | /*When all producers ended with their operations, call the CompleteAdding() to tell Consumers no more items are available*/ 228 | Task.Factory.ContinueWhenAll(producers.ToArray(), p => buffer.CompleteAdding()); 229 | 230 | for (int i = 0; i < numProcs * 4; i++) 231 | { 232 | /*consumer*/ 233 | consumers.Add(Task.Factory.StartNew(() => 234 | { 235 | foreach (Tuple tuple in buffer.GetConsumingEnumerable()) /*If OCE is thrown it will be caught in the caller's AggregateException*/ 236 | { 237 | if (tuple != null) 238 | { 239 | /*Long running procedure*/ 240 | duplicatesDetectorService.CreateInsertFingerprints(tuple.Item2, tuple.Item1); 241 | 242 | processedtracks.Add(tuple.Item1); 243 | if (processed != null) 244 | { 245 | processed.Invoke(tuple.Item1); 246 | } 247 | } 248 | } 249 | }, 250 | token)); 251 | } 252 | 253 | Task.WaitAll(consumers.ToArray()); /*wait for all consumers to end*/ 254 | return processedtracks; 255 | } 256 | } 257 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/DuplicatesDetectorService.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | using SoundFingerprinting.Audio; 8 | using SoundFingerprinting.Builder; 9 | using SoundFingerprinting.Configuration; 10 | using SoundFingerprinting.DAO.Data; 11 | using SoundFingerprinting.Data; 12 | using SoundFingerprinting.Strides; 13 | 14 | internal class DuplicatesDetectorService 15 | { 16 | private const double FalsePositivesThreshold = 0.4; 17 | 18 | private readonly IStride createStride = new IncrementalRandomStride(512, 1024); 19 | 20 | private readonly IAdvancedModelService modelService; 21 | 22 | private readonly IAudioService audioService; 23 | 24 | private readonly IFingerprintCommandBuilder fingerprintCommandBuilder; 25 | 26 | private readonly IQueryFingerprintService queryFingerprintService; 27 | 28 | 29 | public DuplicatesDetectorService(IAdvancedModelService modelService, IAudioService audioService, IFingerprintCommandBuilder fingerprintCommandBuilder, IQueryFingerprintService queryFingerprintService) 30 | { 31 | this.modelService = modelService; 32 | this.audioService = audioService; 33 | this.fingerprintCommandBuilder = fingerprintCommandBuilder; 34 | this.queryFingerprintService = queryFingerprintService; 35 | } 36 | 37 | /// 38 | /// Create fingerprints out of down sampled samples 39 | /// 40 | /// Down sampled to 5512 samples 41 | /// Track 42 | public void CreateInsertFingerprints(AudioSamples samples, TrackInfo track) 43 | { 44 | if (track == null) 45 | { 46 | return; /*track is not eligible*/ 47 | } 48 | 49 | /*Create fingerprints that will be used as initial fingerprints to be queried*/ 50 | var hashes = fingerprintCommandBuilder.BuildFingerprintCommand() 51 | .From(samples) 52 | .WithFingerprintConfig(config => 53 | { 54 | config.Stride = createStride; 55 | return config; 56 | }) 57 | .UsingServices(audioService) 58 | .Hash() 59 | .Result; 60 | 61 | modelService.Insert(track, hashes); 62 | } 63 | 64 | /// 65 | /// Find duplicates between existing tracks in the database 66 | /// 67 | /// Callback invoked at each processed track 68 | /// Sets of duplicates 69 | public HashSet[] FindDuplicates(Action callback) 70 | { 71 | var tracks = modelService.ReadAllTracks().ToList(); 72 | var duplicates = new List>(); 73 | int total = tracks.Count, current = 0; 74 | var queryConfiguration = new DefaultQueryConfiguration { MaxTracksToReturn = int.MaxValue, ThresholdVotes = 4 }; 75 | foreach (var track in tracks) 76 | { 77 | var trackDuplicates = new HashSet(); 78 | 79 | var hashedFingerprints = modelService.ReadHashedFingerprintsByTrack(track.TrackReference); 80 | var max = hashedFingerprints.Max(_ => _.StartsAt); 81 | var min = hashedFingerprints.Min(_ => _.StartsAt); 82 | var hashes = new Hashes(hashedFingerprints, GetLength(min, max, queryConfiguration.FingerprintConfiguration.FingerprintLengthInSeconds)); 83 | var result = queryFingerprintService.Query(hashes, queryConfiguration, modelService); 84 | 85 | if (result.ContainsMatches) 86 | { 87 | foreach (var resultEntry in result.ResultEntries) 88 | { 89 | if (resultEntry.Confidence < FalsePositivesThreshold || track.Equals(resultEntry.Track)) 90 | { 91 | continue; 92 | } 93 | 94 | trackDuplicates.Add(resultEntry.Track); 95 | } 96 | 97 | if (trackDuplicates.Any()) 98 | { 99 | HashSet duplicatePair = new HashSet(trackDuplicates) { track }; 100 | duplicates.Add(duplicatePair); 101 | } 102 | } 103 | 104 | callback?.Invoke(track, total, ++current); 105 | } 106 | 107 | for (int i = 0; i < duplicates.Count - 1; i++) 108 | { 109 | HashSet set = duplicates[i]; 110 | for (int j = i + 1; j < duplicates.Count; j++) 111 | { 112 | IEnumerable result = set.Intersect(duplicates[j]); 113 | if (result.Any()) 114 | { 115 | foreach (var track in duplicates[j]) 116 | { 117 | // collapse all duplicates in one set 118 | set.Add(track); 119 | } 120 | 121 | duplicates.RemoveAt(j); /*Remove the duplicate set*/ 122 | j--; 123 | } 124 | } 125 | } 126 | 127 | return duplicates.ToArray(); 128 | } 129 | 130 | public void ClearStorage() 131 | { 132 | var tracks = modelService.ReadAllTracks(); 133 | foreach (var track in tracks) 134 | { 135 | modelService.DeleteTrack(track.Id); 136 | } 137 | } 138 | 139 | private static double GetLength(double min, double max, double fingerprintLengthInSeconds) 140 | { 141 | return max - min + fingerprintLengthInSeconds; 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AddictedCS/soundfingerprinting.duplicatesdetector/3cb38f9e2d722003c71a3adc5fdbd23e4948ae5c/src/SoundFingerprinting.DuplicatesDetector/Images/icon.png -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Infrastructure/CSVWriter.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Infrastructure 2 | { 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Security.Permissions; 6 | using System.Text; 7 | 8 | /// 9 | /// Class for writing any object values in comma separated file 10 | /// 11 | [DebuggerDisplay("Path={pathToFile}")] 12 | public class CSVWriter 13 | { 14 | /// 15 | /// Separator used while writing to CVS 16 | /// 17 | private const char Separator = ','; 18 | 19 | /// 20 | /// Carriage return line feed 21 | /// 22 | private const string Crlf = "\r\n"; 23 | 24 | /// 25 | /// Path to file 26 | /// 27 | private readonly string pathToFile; 28 | 29 | /// 30 | /// Writer 31 | /// 32 | private StreamWriter writer; 33 | 34 | public CSVWriter(string pathToFile) 35 | { 36 | this.pathToFile = pathToFile; 37 | } 38 | 39 | /// 40 | /// Write the data into CSV 41 | /// 42 | /// Data to be written 43 | [FileIOPermission(SecurityAction.Demand)] 44 | public void Write(object[][] data) 45 | { 46 | if (data == null) 47 | { 48 | return; 49 | } 50 | 51 | using (writer = new StreamWriter(pathToFile)) 52 | { 53 | int cols = data[0].Length; 54 | StringBuilder builder = new StringBuilder(); 55 | for (int i = 0, n = data.Length; i < n; i++) 56 | { 57 | for (int j = 0; j < cols; j++) 58 | { 59 | builder.Append(data[i][j]); 60 | if (j != cols - 1) 61 | { 62 | builder.Append(Separator); 63 | } 64 | } 65 | 66 | builder.Append(Crlf); 67 | } 68 | 69 | writer.Write(builder.ToString()); 70 | writer.Close(); 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Infrastructure/ServiceContainer.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Infrastructure 2 | { 3 | using Ninject; 4 | 5 | /// 6 | /// Class which will hold services injected by dependency injection on Application startup 7 | /// Ninject lib is used for cross class injection 8 | /// 9 | /// 10 | /// Follows the Service Locator pattern. 11 | /// More details can be found here: 12 | /// http://martinfowler.com/articles/injection.html 13 | /// 14 | public static class ServiceContainer 15 | { 16 | /// 17 | /// Actual service container 18 | /// 19 | public static readonly IKernel Kernel = new StandardKernel(); 20 | } 21 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Infrastructure/ServiceInjector.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Infrastructure 2 | { 3 | using SoundFingerprinting.Audio; 4 | using SoundFingerprinting.Audio.Bass; 5 | using SoundFingerprinting.Builder; 6 | using SoundFingerprinting.DuplicatesDetector.Services; 7 | using SoundFingerprinting.InMemory; 8 | 9 | public static class ServiceInjector 10 | { 11 | public static void InjectServices() 12 | { 13 | ServiceContainer.Kernel.Bind().To(); 14 | ServiceContainer.Kernel.Bind().To(); 15 | ServiceContainer.Kernel.Bind().To(); 16 | ServiceContainer.Kernel.Bind().To(); 17 | ServiceContainer.Kernel.Bind().To(); 18 | ServiceContainer.Kernel.Bind().To(); 19 | 20 | ServiceContainer.Kernel.Bind().ToConstant(FingerprintCommandBuilder.Instance).InSingletonScope(); 21 | ServiceContainer.Kernel.Bind().ToConstant(QueryFingerprintService.Instance).InSingletonScope(); 22 | ServiceContainer.Kernel.Bind().ToSelf().InSingletonScope(); 23 | ServiceContainer.Kernel.Bind().ToSelf().InSingletonScope(); 24 | ServiceContainer.Kernel.Bind().To().InSingletonScope(); 25 | ServiceContainer.Kernel.Bind().To().InSingletonScope(); 26 | ServiceContainer.Kernel.Bind().To().InSingletonScope(); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Infrastructure/TrackHelper.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Infrastructure 2 | { 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | using SoundFingerprinting.Audio; 7 | using SoundFingerprinting.DAO.Data; 8 | using SoundFingerprinting.Data; 9 | 10 | public class TrackHelper 11 | { 12 | private readonly IAudioService audioService; 13 | 14 | public TrackHelper(IAudioService audioService) 15 | { 16 | this.audioService = audioService; 17 | } 18 | 19 | public AudioSamples GetTrackSamples(TrackInfo track, int sampleRate, int secondsToRead, int startAtSecond) 20 | { 21 | string filePath = track.MetaFields["FilePath"]; 22 | if (track == null || filePath == null) 23 | { 24 | return null; 25 | } 26 | 27 | return audioService.ReadMonoSamplesFromFile(filePath, sampleRate, secondsToRead, startAtSecond); 28 | } 29 | 30 | public TrackInfo GetTrack(int mintracklen, int maxtracklen, string filename) 31 | { 32 | string artist, title, isrc; 33 | /*The song does not contain any tags*/ 34 | artist = "Unknown Artist"; 35 | title = "Unknown Title"; 36 | isrc = Path.GetFileNameWithoutExtension(filename); 37 | var meta = new Dictionary(); 38 | meta["FilePath"] = Path.GetFullPath(filename); 39 | 40 | double duration = audioService.GetLengthInSeconds(Path.GetFullPath(filename)); 41 | /*check the duration of a music file*/ 42 | if (duration < mintracklen || duration > maxtracklen) 43 | { 44 | return null; 45 | } 46 | 47 | return new TrackInfo(isrc, title, artist, meta, MediaType.Audio); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector 2 | { 3 | using System.Windows; 4 | 5 | public partial class MainWindow : Window 6 | { 7 | public MainWindow() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/MainWindowResourceDictionary.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Model/Item.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Model 2 | { 3 | using System; 4 | using System.ComponentModel; 5 | 6 | /// 7 | /// Class for folder representation in the UI element 8 | /// 9 | [Serializable] 10 | public class Item : INotifyPropertyChanged 11 | { 12 | /// 13 | /// Number of music files within the folder 14 | /// 15 | private int count; 16 | 17 | /// 18 | /// Path to folder 19 | /// 20 | private string path; 21 | 22 | #region INotifyPropertyChanged Members 23 | 24 | public event PropertyChangedEventHandler PropertyChanged; 25 | 26 | #endregion 27 | 28 | public bool IsFolder { get; set; } 29 | 30 | public string Path 31 | { 32 | get 33 | { 34 | return path; 35 | } 36 | 37 | set 38 | { 39 | if (path != value) 40 | { 41 | path = value; 42 | OnPropertyChanged("Path"); 43 | } 44 | } 45 | } 46 | 47 | public int Count 48 | { 49 | get 50 | { 51 | return count; 52 | } 53 | 54 | set 55 | { 56 | if (count != value) 57 | { 58 | count = value; 59 | OnPropertyChanged("Count"); 60 | } 61 | } 62 | } 63 | 64 | private void OnPropertyChanged(string property) 65 | { 66 | PropertyChangedEventHandler temp = PropertyChanged; 67 | if (temp != null) 68 | { 69 | PropertyChanged(this, new PropertyChangedEventArgs(property)); 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Model/ResultItem.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Model 2 | { 3 | using SoundFingerprinting.DAO.Data; 4 | 5 | public class ResultItem 6 | { 7 | private readonly TrackData track; 8 | 9 | public ResultItem(int setId, TrackData track) 10 | { 11 | SetId = setId; 12 | this.track = track; 13 | } 14 | 15 | public int SetId { get; private set; } 16 | 17 | public string FileName 18 | { 19 | get { return System.IO.Path.GetFileName(track.MetaFields["FilePath"]); } 20 | } 21 | 22 | public string Path 23 | { 24 | get { return track.MetaFields["FilePath"]; } 25 | } 26 | 27 | public double TrackLength 28 | { 29 | get { return track.Length; } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Windows; 4 | 5 | [assembly: AssemblyTitle("DuplicateTracks")] 6 | [assembly: AssemblyDescription("Duplicate audio files detector. Tool for finding duplicate audio file by their perceptual identity.")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("Ciumac Sergiu")] 9 | [assembly: AssemblyProduct("DuplicateTracks")] 10 | [assembly: AssemblyCopyright("Copyright © Ciumac Sergiu 2017")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | [assembly: ComVisible(false)] 14 | [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] 15 | [assembly: AssemblyVersion("2.0.0")] 16 | [assembly: AssemblyFileVersion("2.0.0")] -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/RelayCommand.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector 2 | { 3 | using System; 4 | using System.Windows.Input; 5 | 6 | public class RelayCommand : ICommand 7 | { 8 | /// 9 | /// Method which verifies whether the command can execute 10 | /// 11 | private readonly Predicate canExecute; 12 | 13 | /// 14 | /// Method to execute 15 | /// 16 | private readonly Action execute; 17 | 18 | public RelayCommand(Action execute) 19 | : this(execute, null) 20 | { 21 | } 22 | 23 | public RelayCommand(Action execute, Predicate canExecute) 24 | { 25 | this.execute = execute; 26 | this.canExecute = canExecute; 27 | } 28 | 29 | /// 30 | /// Fires when the CanExecute status of this command changes. 31 | /// 32 | public event EventHandler CanExecuteChanged 33 | { 34 | add 35 | { 36 | CommandManager.RequerySuggested += value; 37 | } 38 | 39 | remove 40 | { 41 | CommandManager.RequerySuggested -= value; 42 | } 43 | } 44 | 45 | /// 46 | /// Check if the method can be executed 47 | /// 48 | /// Parameter 49 | /// True/False 50 | public bool CanExecute(object parameter) 51 | { 52 | return canExecute == null || canExecute.Invoke(parameter); 53 | } 54 | 55 | /// 56 | /// Execute the method 57 | /// 58 | /// Parameter for execution 59 | public void Execute(object parameter) 60 | { 61 | execute(parameter); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Services/FolderBrowserDialogService.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Services 2 | { 3 | using System.Windows.Forms; 4 | 5 | public class FolderBrowserDialogService : IFolderBrowserDialogService 6 | { 7 | #region IFolderBrowserDialogService Members 8 | 9 | public string SelectedPath { get; private set; } 10 | 11 | public DialogResult Show() 12 | { 13 | using (FolderBrowserDialog dlg = new FolderBrowserDialog()) 14 | { 15 | DialogResult result = dlg.ShowDialog(); 16 | SelectedPath = dlg.SelectedPath; 17 | return result; 18 | } 19 | } 20 | 21 | #endregion 22 | } 23 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Services/GenericViewWindowService.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Services 2 | { 3 | using System; 4 | using System.ComponentModel; 5 | 6 | using SoundFingerprinting.DuplicatesDetector.View; 7 | 8 | /// 9 | /// Generic view window service 10 | /// 11 | public class GenericViewWindowService : IGenericViewWindow 12 | { 13 | /// 14 | /// Lock object 15 | /// 16 | private static readonly object LockObject = new object(); 17 | 18 | /// 19 | /// Actual view (uninitialized) 20 | /// 21 | private GenericView view; 22 | 23 | #region IGenericViewWindow Members 24 | 25 | public event EventHandler Closed; 26 | 27 | public event CancelEventHandler Closing; 28 | 29 | public bool? DialogResult 30 | { 31 | get { return GetView().DialogResult; } 32 | set { GetView().DialogResult = value; } 33 | } 34 | 35 | public object DataContext 36 | { 37 | get { return GetView().DataContext; } 38 | set { GetView().DataContext = value; } 39 | } 40 | 41 | public void Show() 42 | { 43 | GetView().Show(); 44 | } 45 | 46 | public void Close() 47 | { 48 | GetView().Close(); 49 | view = null; 50 | } 51 | 52 | #endregion 53 | 54 | /// 55 | /// Lazy initialization of the view 56 | /// 57 | /// Actual view 58 | private GenericView GetView() 59 | { 60 | lock (LockObject) 61 | { 62 | if (view != null) 63 | { 64 | return view; 65 | } 66 | 67 | view = new GenericView(); 68 | view.Closed += (sender, e) => 69 | { 70 | if (Closed != null) 71 | { 72 | Closed(sender, e); 73 | } 74 | 75 | view = null; 76 | }; 77 | view.Closing += (sender, e) => 78 | { 79 | if (Closing != null) 80 | { 81 | Closing(sender, e); 82 | } 83 | }; 84 | return view; 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Services/IFolderBrowserDialogService.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Services 2 | { 3 | using System.Windows.Forms; 4 | 5 | /// 6 | /// Contract for FolderBrowserDialog service 7 | /// 8 | public interface IFolderBrowserDialogService 9 | { 10 | /// 11 | /// Gets selected path 12 | /// 13 | string SelectedPath { get; } 14 | 15 | /// 16 | /// Show FolderBrowserDialog 17 | /// 18 | /// Dialog results 19 | DialogResult Show(); 20 | } 21 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Services/IGenericViewWindow.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Services 2 | { 3 | using System; 4 | using System.ComponentModel; 5 | 6 | /// 7 | /// Interface to be implemented by views 8 | /// 9 | /// 10 | /// The binding between the views and view-models will be performed by a 11 | /// mediator IWindowService which will take care of the abstraction 12 | /// 13 | public interface IGenericViewWindow 14 | { 15 | event EventHandler Closed; 16 | 17 | event CancelEventHandler Closing; 18 | 19 | bool? DialogResult { get; set; } 20 | 21 | object DataContext { get; set; } 22 | 23 | void Show(); 24 | 25 | void Close(); 26 | } 27 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Services/IMessageBoxService.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Services 2 | { 3 | using System.Windows; 4 | 5 | /// 6 | /// Service Contract to be implemented by the types which would like to provide MessageBox.Show services 7 | /// 8 | public interface IMessageBoxService 9 | { 10 | /// 11 | /// Show the MessageBox to the client 12 | /// 13 | /// Message to be shown 14 | /// Title of the MessageBox 15 | /// Buttons 16 | /// Image to be shown 17 | /// MessageBox results 18 | MessageBoxResult Show(string message, string title, MessageBoxButton buttons, MessageBoxImage image); 19 | } 20 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Services/IOpenFileDialogService.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Services 2 | { 3 | using System.Windows.Forms; 4 | 5 | public interface IOpenFileDialogService 6 | { 7 | string[] SelectedPaths { get; } 8 | 9 | DialogResult Show(string title, string filename, string filter, bool multiselect); 10 | } 11 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Services/ISaveFileDialogService.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Services 2 | { 3 | using System.Windows.Forms; 4 | 5 | internal interface ISaveFileDialogService 6 | { 7 | string Filename { get; } 8 | 9 | DialogResult SaveFile(string title, string filename, string extension); 10 | } 11 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Services/IWindowService.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Services 2 | { 3 | using System; 4 | 5 | /// 6 | /// Window service works as mediator between the View and View/Model 7 | /// 8 | public interface IWindowService 9 | { 10 | void ShowDialog(IGenericViewWindow view, TViewModel viewModel, Action onDialogClose); 11 | 12 | void ShowDialog(IGenericViewWindow view, TDialogViewModel viewModel); 13 | } 14 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Services/MessageBoxService.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Services 2 | { 3 | using System.Windows; 4 | 5 | /// 6 | /// MessageBox service 7 | /// 8 | public class MessageBoxService : IMessageBoxService 9 | { 10 | #region IMessageBoxService Members 11 | 12 | /// 13 | /// Show actual MessageBox 14 | /// 15 | /// Message to be shown 16 | /// Title of the MessageBox 17 | /// Buttons in the MessageBox 18 | /// Image on the MessageBox 19 | /// MessageBox results 20 | public MessageBoxResult Show(string message, string title, MessageBoxButton buttons, MessageBoxImage image) 21 | { 22 | return MessageBox.Show(message, title, buttons, image); 23 | } 24 | 25 | #endregion 26 | } 27 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Services/OpenFileDialogService.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Services 2 | { 3 | using System.Windows.Forms; 4 | 5 | /// 6 | /// Open file dialog service 7 | /// 8 | public class OpenFileDialogService : IOpenFileDialogService 9 | { 10 | #region IOpenFileDialogService Members 11 | 12 | public string[] SelectedPaths { get; private set; } 13 | 14 | /// 15 | /// Show open file dialog 16 | /// 17 | /// Title of the dialog 18 | /// Default filename 19 | /// Filter of the file dialog 20 | /// Multi-select enabled 21 | /// Dialog result 22 | public DialogResult Show(string title, string filename, string filter, bool multiselect) 23 | { 24 | using (OpenFileDialog ofd = new OpenFileDialog { Title = title, FileName = filename, Filter = filter, Multiselect = multiselect }) 25 | { 26 | DialogResult result = ofd.ShowDialog(); 27 | SelectedPaths = ofd.FileNames; 28 | return result; 29 | } 30 | } 31 | 32 | #endregion 33 | } 34 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Services/SaveFileDialogService.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Services 2 | { 3 | using System.Windows.Forms; 4 | 5 | internal class SaveFileDialogService : ISaveFileDialogService 6 | { 7 | #region ISaveFileDialogService Members 8 | 9 | public string Filename { get; private set; } 10 | 11 | public DialogResult SaveFile(string title, string filename, string extension) 12 | { 13 | using (SaveFileDialog sfd = new SaveFileDialog { Title = title, FileName = filename, Filter = extension }) 14 | { 15 | DialogResult result = sfd.ShowDialog(); 16 | Filename = sfd.FileName; 17 | return result; 18 | } 19 | } 20 | 21 | #endregion 22 | } 23 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Services/WindowService.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Services 2 | { 3 | using System; 4 | 5 | public class WindowService : IWindowService 6 | { 7 | #region IWindowService Members 8 | 9 | public void ShowDialog(IGenericViewWindow view, TViewModel viewModel, Action onDialogClose) 10 | { 11 | view.DataContext = viewModel; 12 | if (onDialogClose != null) 13 | { 14 | view.Closing += (o, args) => onDialogClose(o, args); 15 | } 16 | 17 | view.Show(); 18 | } 19 | 20 | public void ShowDialog(IGenericViewWindow view, TViewModel viewModel) 21 | { 22 | ShowDialog(view, viewModel, null); 23 | } 24 | 25 | #endregion 26 | } 27 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Settings.StyleCop: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/SoundFingerprinting.DuplicatesDetector.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 3.0.1927.0 7 | {572367D4-0F0C-4DC9-BE1C-4D037530D785} 8 | WinExe 9 | Properties 10 | Soundfingerprinting.DuplicatesDetector 11 | SoundFingerprinting.DuplicatesDetector 12 | SoundFingerprinting.DuplicatesDetector 13 | v4.6.1 14 | false 15 | Application.ico 16 | 512 17 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 18 | 4 19 | 20 | 21 | 3.5 22 | 23 | publish\ 24 | true 25 | Disk 26 | false 27 | Foreground 28 | 7 29 | Days 30 | false 31 | false 32 | true 33 | 0 34 | 1.0.0.%2a 35 | false 36 | false 37 | true 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ..\ 48 | true 49 | 50 | 51 | true 52 | full 53 | false 54 | bin\Debug\ 55 | TRACE;DEBUG 56 | prompt 57 | 4 58 | AllRules.ruleset 59 | x86 60 | false 61 | 62 | 63 | pdbonly 64 | true 65 | bin\Release\ 66 | TRACE 67 | prompt 68 | 4 69 | AllRules.ruleset 70 | x86 71 | false 72 | 73 | 74 | 75 | 76 | 3.5 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | MSBuild:Compile 89 | Designer 90 | MSBuild:Compile 91 | Designer 92 | 93 | 94 | App.xaml 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | MainWindow.xaml 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | GenericView.xaml 131 | 132 | 133 | PathListView.xaml 134 | 135 | 136 | ReportView.xaml 137 | 138 | 139 | Designer 140 | MSBuild:Compile 141 | 142 | 143 | MSBuild:Compile 144 | Designer 145 | MSBuild:Compile 146 | Designer 147 | 148 | 149 | Designer 150 | MSBuild:Compile 151 | 152 | 153 | MSBuild:Compile 154 | Designer 155 | MSBuild:Compile 156 | Designer 157 | 158 | 159 | MSBuild:Compile 160 | Designer 161 | MSBuild:Compile 162 | Designer 163 | 164 | 165 | 166 | 167 | false 168 | 169 | 170 | Designer 171 | MSBuild:Compile 172 | 173 | 174 | Designer 175 | MSBuild:Compile 176 | 177 | 178 | Designer 179 | MSBuild:Compile 180 | 181 | 182 | Designer 183 | MSBuild:Compile 184 | 185 | 186 | Designer 187 | MSBuild:Compile 188 | 189 | 190 | Designer 191 | MSBuild:Compile 192 | 193 | 194 | Designer 195 | MSBuild:Compile 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | False 204 | .NET Framework 3.5 SP1 Client Profile 205 | false 206 | 207 | 208 | False 209 | .NET Framework 3.5 SP1 210 | true 211 | 212 | 213 | False 214 | Windows Installer 3.1 215 | true 216 | 217 | 218 | 219 | 220 | 3.3.4 221 | 222 | 223 | 7.3.3 224 | 225 | 226 | 7.0.0 227 | 228 | 229 | 3.5.50211.1 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 245 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Themes/Brushes.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Themes/Converters.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Themes/Datagrid.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 18 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Themes/ItemContStyle.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 13 | 14 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Themes/ProgressBar.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 144 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Themes/RoundedButton.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 102 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Themes/SetIdConverter.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.Themes 2 | { 3 | using System; 4 | using System.Globalization; 5 | using System.Windows.Data; 6 | using System.Windows.Media; 7 | 8 | /// 9 | /// Converter which alternates the color on duplicate sets 10 | /// 11 | [ValueConversion(typeof(object), typeof(int))] 12 | public class SetIdConverter : IValueConverter 13 | { 14 | #region IValueConverter Members 15 | 16 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | if (value is int) 19 | { 20 | int setId = (int)value; 21 | return setId % 2 == 0 ? new SolidColorBrush(Color.FromArgb(100, 0, 100, 150)) : new SolidColorBrush(Colors.Transparent); 22 | } 23 | 24 | return new SolidColorBrush(Colors.Transparent); 25 | } 26 | 27 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 28 | { 29 | throw new NotImplementedException(); 30 | } 31 | 32 | #endregion 33 | } 34 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/Themes/TextBlock.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/View/GenericView.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/View/GenericView.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace SoundFingerprinting.DuplicatesDetector.View 2 | { 3 | public partial class GenericView 4 | { 5 | public GenericView() 6 | { 7 | InitializeComponent(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/SoundFingerprinting.DuplicatesDetector/View/PathListView.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 |