public MainWindow() { InitializeComponent(); try { this.logger = Gnosis.Utilities.Log4NetLogger.GetDefaultLogger(typeof(MainWindow)); } catch (Exception loggerEx) { throw new ApplicationException("Could not initialize logger", loggerEx); } try { logger.Info("Initializing Alexandria"); mediaFactory = new MediaFactory(logger); securityContext = new SecurityContext(mediaFactory); tagTypeFactory = new TagTypeFactory(); mediaRepository = new SQLiteMediaRepository(logger, mediaFactory); mediaRepository.Initialize(); linkRepository = new SQLiteLinkRepository(logger); linkRepository.Initialize(); tagRepository = new SQLiteTagRepository(logger, tagTypeFactory); tagRepository.Initialize(); metadataRepository = new SQLiteMetadataRepository(logger, securityContext, mediaFactory); metadataRepository.Initialize(); marqueeRepository = new SQLiteMarqueeRepository(logger); audioStreamFactory = new AudioStreamFactory(); videoPlayer = new Gnosis.Video.Vlc.VideoPlayerControl(); videoPlayer.Initialize(logger, () => GetVideoHost()); catalogController = new CatalogController(logger, securityContext, mediaFactory, mediaRepository, linkRepository, tagRepository, metadataRepository, audioStreamFactory); spiderFactory = new SpiderFactory(logger, securityContext, mediaFactory, linkRepository, tagRepository, mediaRepository, metadataRepository, audioStreamFactory); metadataController = new MediaItemController(logger, securityContext, mediaFactory, linkRepository, tagRepository, metadataRepository); taskController = new TaskController(logger, mediaFactory, videoPlayer, spiderFactory, metadataController, marqueeRepository, metadataRepository); tagController = new TagController(logger, tagRepository); commandController = new CommandController(logger); taskResultView.Initialize(logger, securityContext, mediaFactory, metadataController, taskController, tagController, videoPlayer); //taskManagerView.Initialize(logger, taskController, taskResultView); searchView.Initialize(logger, taskController, taskResultView); commandView.Initialize(logger, commandController, taskController, taskResultView); ScreenSaver.Disable(); } catch (Exception ex) { logger.Error("MainWindow.ctor", ex); } }
private void AddConcatenatedFiles(List <FileInfo> fileInfos) { if (fileInfos.Select(fi => AudioStreamFactory.IsSupportedFile(fi.FullName)).Count() == fileInfos.Count) { AudioTrack audioTrack = new AudioTrack(fileInfos.ToArray()); trackList.Add(audioTrack); } }
private void AddFile(string fileName) { if (AudioStreamFactory.IsSupportedFile(fileName)) { AudioTrack audioTrack = new AudioTrack(new FileInfo(fileName)); trackListBox.Items.Add(audioTrack); } }
private void AddFile(FileInfo fileInfo) { try { AudioStreamFactory.IsSupportedFileOrThrow(fileInfo.FullName); AudioTrack audioTrack = new AudioTrack(fileInfo); trackList.Add(audioTrack); } catch (Exception e) { MessageBox.Show(this, e.Message, "Cannot open file: " + fileInfo.Name, MessageBoxButton.OK, MessageBoxImage.Error); } }
private void SetAudioTrack(AudioTrack audioTrack) { UnsetAudioTrack(); this.audioTrack = audioTrack; audioStream = AudioStreamFactory.FromAudioTrackForGUI(audioTrack); audioStream.WaveformChanged += OnAudioStreamWaveformChanged; audioTrack.LengthChanged += OnAudioTrackLengthChanged; audioTrack.VolumeChanged += OnAudioTrackVolumeChanged; audioTrack.BalanceChanged += OnAudioTrackBalanceChanged; }
public MainWindow() { InitializeComponent(); // Use PFFFT as FFT implementation FFTFactory.Factory = new Aurio.PFFFT.FFTFactory(); // Use Soxr as resampler implementation ResamplerFactory.Factory = new Aurio.Soxr.ResamplerFactory(); // Use FFmpeg for file reading/decoding AudioStreamFactory.AddFactory(new FFmpegAudioStreamFactory()); }
public void Generate() { IAudioStream audioStream = inputTrack.File ? AudioStreamFactory.FromFileInfoIeee32(inputTrack.FileInfo) : inputTrack.Stream; audioStream = new MonoStream(audioStream); audioStream = new ResamplingStream(audioStream, ResamplingQuality.Medium, profile.SampleRate); STFT stft = new STFT(audioStream, profile.FrameSize, profile.FrameStep, WindowType.Hann, STFT.OutputFormat.Decibel, this.bufferSize); index = 0; indices = stft.WindowCount; frameBuffer = new float[profile.FrameSize / 2]; List <SubFingerprint> subFingerprints = new List <SubFingerprint>(); while (stft.HasNext()) { // Get FFT spectrum stft.ReadFrame(frameBuffer); // Sum FFT bins into target frequency bands profile.MapFrequencies(frameBuffer, bands); CalculateSubFingerprint(bandsPrev, bands, subFingerprints); CommonUtil.Swap <float[]>(ref bands, ref bandsPrev); index++; // Output subfingerprints every once in a while if (index % this.eventInterval == 0 && SubFingerprintsGenerated != null) { SubFingerprintsGenerated(this, new SubFingerprintsGeneratedEventArgs(inputTrack, subFingerprints, index, indices)); subFingerprints.Clear(); } } // Output remaining subfingerprints if (SubFingerprintsGenerated != null) { SubFingerprintsGenerated(this, new SubFingerprintsGeneratedEventArgs(inputTrack, subFingerprints, index, indices)); } if (Completed != null) { Completed(this, EventArgs.Empty); } audioStream.Close(); }
public IAudioStream CreateAudioStream() { IAudioStream stream = null; if (MultiFile) { stream = new ConcatenationStream(FileInfos.Select(fi => AudioStreamFactory.FromFileInfoIeee32(fi)).ToArray()); } else { stream = AudioStreamFactory.FromFileInfoIeee32(FileInfo); } return(new TimeWarpStream(stream, timeWarps)); }
public IAudioStream OpenFile(FileInfo fileInfo) { if (FFmpegSourceStream.WaveProxySuggested(fileInfo)) { Console.WriteLine("File format with known seek problems, creating proxy file..."); return(AudioStreamFactory.FromFileInfo(FFmpegSourceStream.CreateWaveProxy(fileInfo))); } else { try { FFmpegSourceStream stream = new FFmpegSourceStream(fileInfo); // Make a seek to test if it works or if it throws an exception stream.Position = 0; return(stream); } catch (FFmpegSourceStream.FileNotSeekableException) { /* * This exception gets thrown if a file is not seekable and therefore cannot * provide all the functionality that is needed for an IAudioStream, although * the problem could be solved by creating a seek index. See FFmpegSourceStream * for further information. * * For now, we create a WAV proxy file, because it is easier (consumes * additional space though). */ Console.WriteLine("File not seekable, creating proxy file..."); return(AudioStreamFactory.FromFileInfo(FFmpegSourceStream.CreateWaveProxy(fileInfo))); } catch (FFmpegSourceStream.FileSeekException) { /* * This exception gets thrown if a file should be seekable but seeking still does * not work correctly. We also create a proxy in this case. */ Console.WriteLine("File test seek failed, creating proxy file..."); return(AudioStreamFactory.FromFileInfo(FFmpegSourceStream.CreateWaveProxy(fileInfo))); } catch (DllNotFoundException e) { throw new DllNotFoundException("Cannot open file through FFmpeg: DLL missing", e); } } }
private static void Main(string[] args) { // Use PFFFT as FFT implementation FFTFactory.Factory = new Aurio.PFFFT.FFTFactory(); // Use Soxr as resampler implementation ResamplerFactory.Factory = new Aurio.Soxr.ResamplerFactory(); // Use FFmpeg for file reading/decoding AudioStreamFactory.AddFactory(new FFmpegAudioStreamFactory()); try { // read config file Dictionary <string, double> mapping = ReadConfig(args[0]); // read input dir DirectoryInfo indir = new DirectoryInfo(args[1]); // read output dir DirectoryInfo outdir = new DirectoryInfo(args[2]); if (!outdir.Exists) { outdir.Create(); } Process(mapping, indir, outdir); } catch (Exception e) { Console.WriteLine("Batch Resampling Tool to change the playback speed of audio files"); Console.WriteLine(); Console.WriteLine("Error: " + e.Message); Console.WriteLine(); var info = "Usage: BatchResampler mappingFile inputDirectory outputDirectory" + Environment.NewLine + Environment.NewLine + "The mappingFile is a text file with at least one line of the pattern 'filenamePattern;resamplingFactor' (without quotes), " + "where the filenamePattern can be a filename or a filename wildcard expression to be matched in the input directory, and the " + "resamplingFactor is a floating point value specifying the input:output resampling ratio with which the files will be written" + "to the output directory. The simplest mappingFile would be '*;1', which means that all files in the input directory will be" + "resampled with a factor of 1.0 (i.e. no resampling at all) and written to the output directory. The nominal sampling rate of a file" + "stays untouched, leading to altered playback speeds." + Environment.NewLine + "A mappingFile with the content '*.wav;0.5' speeds up all wave audio files to 200% playback speed because they are getting resampled " + "to half of their effective sampling rate while the nominal playback sample rate stays the same, cutting their playback duration in half."; Console.WriteLine(info); } }
private void bw_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; Parameters parameters = (Parameters)e.Argument; var streams = parameters.SourceFiles.Select(fi => AudioStreamFactory.FromFileInfo(fi)); // load source stream var sourceStream = new ConcatenationStream(streams.ToArray()); var firstFile = parameters.SourceFiles.First(); string targetFileNamePrefix = firstFile.FullName.Remove(firstFile.FullName.Length - firstFile.Extension.Length); string targetFileNameSuffix = firstFile.Extension; int partCount = 0; Random random = new Random(); CropStream cropStream = new CropStream(sourceStream, 0, 0); while (sourceStream.Position < sourceStream.Length) { partCount++; int length = random.Next(parameters.MinLength, parameters.MaxLength); // length in seconds of the current part to write long byteLength = TimeUtil.TimeSpanToBytes(new TimeSpan(TimeUtil.SECS_TO_TICKS * length), cropStream.Properties); Debug.WriteLine("writing part " + partCount + " (" + length + " secs = " + byteLength + " bytes)"); Debug.WriteLine("before: " + cropStream.Begin + " / " + cropStream.End + " / " + cropStream.Position + " / " + sourceStream.Position); cropStream.Begin = cropStream.End; cropStream.End += sourceStream.Length - cropStream.Begin < byteLength ? sourceStream.Length - cropStream.Begin : byteLength; cropStream.Position = 0; Debug.WriteLine("after : " + cropStream.Begin + " / " + cropStream.End + " / " + cropStream.Position + " / " + sourceStream.Position); AudioStreamFactory.WriteToFile(cropStream, String.Format("{0}.part{1:000}{2}", targetFileNamePrefix, partCount, targetFileNameSuffix)); if (worker.CancellationPending) { e.Cancel = true; Debug.WriteLine("canceled"); return; } worker.ReportProgress((int)((double)sourceStream.Position / sourceStream.Length * 100)); } Debug.WriteLine("finished"); }
static void Process(Dictionary <string, double> mapping, DirectoryInfo indir, DirectoryInfo outdir) { Dictionary <FileInfo, double> fileMapping = new Dictionary <FileInfo, double>(); foreach (string fileNamePattern in mapping.Keys) { double factor = mapping[fileNamePattern]; foreach (FileInfo fileInfo in indir.EnumerateFiles(fileNamePattern)) { fileMapping.Add(fileInfo, factor); } } Parallel.ForEach <FileInfo>(fileMapping.Keys, (fileInfo) => { double factor = fileMapping[fileInfo]; FileInfo outputFileInfo = new FileInfo(Path.Combine(outdir.FullName, fileInfo.Name)); if (outputFileInfo.Exists) { Console.WriteLine(fileInfo.Name + " SKIP (file already existing)"); return; } Console.WriteLine(fileInfo.Name); try { IAudioStream inputStream = AudioStreamFactory.FromFileInfoIeee32(fileInfo); IAudioStream resamplingStream = new ResamplingStream(inputStream, ResamplingQuality.VeryHigh, factor); MixerStream sampleRateResetStream = new MixerStream(resamplingStream.Properties.Channels, inputStream.Properties.SampleRate); sampleRateResetStream.Add(resamplingStream); IAudioStream outputStream = sampleRateResetStream; AudioStreamFactory.WriteToFile(outputStream, outputFileInfo.FullName); } catch (Exception e) { Console.WriteLine("Error processing " + fileInfo.Name + ": " + e.Message); } }); }
public AudioTrack(FileInfo[] fileInfos, bool initialize) : base(fileInfos) { this.TimeWarps = new TimeWarpCollection(); if (initialize) { using (IAudioStream stream = AudioStreamFactory.FromFileInfo(FileInfo)) { sourceProperties = stream.Properties; if (MultiFile) { // For multi-file tracks, we need to get a concatenated stream of all files for the length InitializeLength(); } else { // Single-file tracks can just reuse this stream to get the length InitializeLength(stream); } } } }
private void SetAudioTrack(AudioTrack audioTrack) { UnsetAudioTrack(); // init renderers waveformBitmapRenderers = new WaveformBitmapRenderer[audioTrack.SourceProperties.Channels]; for (int i = 0; i < waveformBitmapRenderers.Length; i++) { waveformBitmapRenderers[i] = new WaveformBitmapRenderer(); } waveformGeometryRenderers = new WaveformGeometryRenderer[audioTrack.SourceProperties.Channels]; for (int i = 0; i < waveformGeometryRenderers.Length; i++) { waveformGeometryRenderers[i] = new WaveformGeometryRenderer(); } this.audioTrack = audioTrack; audioStream = AudioStreamFactory.FromAudioTrackForGUI(audioTrack); audioStream.WaveformChanged += OnAudioStreamWaveformChanged; audioTrack.LengthChanged += OnAudioTrackLengthChanged; audioTrack.VolumeChanged += OnAudioTrackVolumeChanged; audioTrack.BalanceChanged += OnAudioTrackBalanceChanged; }
public void Generate(AudioTrack track) { IAudioStream audioStream = new ResamplingStream( new MonoStream(AudioStreamFactory.FromFileInfoIeee32(track.FileInfo)), ResamplingQuality.Medium, profile.SamplingRate); STFT stft = new STFT(audioStream, profile.WindowSize, profile.HopSize, WindowType.Hann, STFT.OutputFormat.Decibel); int index = 0; int indices = stft.WindowCount; int processedFrames = 0; float[] spectrum = new float[profile.WindowSize / 2]; float[] smoothedSpectrum = new float[spectrum.Length - profile.SpectrumSmoothingLength + 1]; // the smooved frequency spectrum of the current frame var spectrumSmoother = new SimpleMovingAverage(profile.SpectrumSmoothingLength); float[] spectrumTemporalAverage = new float[spectrum.Length]; // a running average of each spectrum bin over time float[] spectrumResidual = new float[spectrum.Length]; // the difference between the current spectrum and the moving average spectrum var peakHistory = new PeakHistory(1 + profile.TargetZoneDistance + profile.TargetZoneLength, spectrum.Length / 2); var peakPairs = new List <PeakPair>(profile.PeaksPerFrame * profile.PeakFanout); // keep a single instance of the list to avoid instantiation overhead var subFingerprints = new List <SubFingerprint>(); while (stft.HasNext()) { // Get the FFT spectrum stft.ReadFrame(spectrum); // Skip frames whose average spectrum volume is below the threshold // This skips silent frames (zero samples) that only contain very low noise from the FFT // and that would screw up the temporal spectrum average below for the following frames. if (spectrum.Average() < spectrumMinThreshold) { index++; continue; } // Smooth the frequency spectrum to remove small peaks if (profile.SpectrumSmoothingLength > 0) { spectrumSmoother.Clear(); for (int i = 0; i < spectrum.Length; i++) { var avg = spectrumSmoother.Add(spectrum[i]); if (i >= profile.SpectrumSmoothingLength) { smoothedSpectrum[i - profile.SpectrumSmoothingLength] = avg; } } } // Update the temporal moving bin average if (processedFrames == 0) { // Init averages on first frame for (int i = 0; i < spectrum.Length; i++) { spectrumTemporalAverage[i] = spectrum[i]; } } else { // Update averages on all subsequent frames for (int i = 0; i < spectrum.Length; i++) { spectrumTemporalAverage[i] = ExponentialMovingAverage.UpdateMovingAverage( spectrumTemporalAverage[i], profile.SpectrumTemporalSmoothingCoefficient, spectrum[i]); } } // Calculate the residual // The residual is the difference of the current spectrum to the temporal average spectrum. The higher // a bin residual is, the steeper the increase in energy in that peak. for (int i = 0; i < spectrum.Length; i++) { spectrumResidual[i] = spectrum[i] - spectrumTemporalAverage[i] - 90f; } // Find local peaks in the residual // The advantage of finding peaks in the residual instead of the spectrum is that spectrum energy is usually // concentrated in the low frequencies, resulting in a clustering of the highest peaks in the lows. Getting // peaks from the residual distributes the peaks more evenly across the spectrum. var peaks = peakHistory.List; // take oldest list, peaks.Clear(); // clear it, and FindLocalMaxima(spectrumResidual, peaks); // refill with new peaks // Pick the largest n peaks int numMaxima = Math.Min(peaks.Count, profile.PeaksPerFrame); if (numMaxima > 0) { peaks.Sort((p1, p2) => p1.Value == p2.Value ? 0 : p1.Value < p2.Value ? 1 : -1); // order peaks by height if (peaks.Count > numMaxima) { peaks.RemoveRange(numMaxima, peaks.Count - numMaxima); // select the n tallest peaks by deleting the rest } peaks.Sort((p1, p2) => p1.Index == p2.Index ? 0 : p1.Index < p2.Index ? -1 : 1); // sort peaks by index (not really necessary) } peakHistory.Add(index, peaks); if (FrameProcessed != null) { // Mark peaks as 0dB for spectrogram display purposes foreach (var peak in peaks) { spectrum[peak.Index] = 0; spectrumResidual[peak.Index] = 0; } FrameProcessed(this, new FrameProcessedEventArgs { AudioTrack = track, Index = index, Indices = indices, Spectrum = spectrum, SpectrumResidual = spectrumResidual }); } processedFrames++; index++; if (processedFrames >= peakHistory.Length) { peakPairs.Clear(); FindPairsWithMaxEnergy(peakHistory, peakPairs); ConvertPairsToSubFingerprints(peakPairs, subFingerprints); } if (subFingerprints.Count > 512) { FireFingerprintHashesGenerated(track, indices, subFingerprints); subFingerprints.Clear(); } } // Flush the remaining peaks of the last frames from the history to get all remaining pairs for (int i = 0; i < profile.TargetZoneLength; i++) { var peaks = peakHistory.List; peaks.Clear(); peakHistory.Add(-1, peaks); peakPairs.Clear(); FindPairsWithMaxEnergy(peakHistory, peakPairs); ConvertPairsToSubFingerprints(peakPairs, subFingerprints); } FireFingerprintHashesGenerated(track, indices, subFingerprints); audioStream.Close(); }
/// <summary> /// This method generates hash codes from an audio stream in a streaming fashion, /// which means that it only maintains a small constant-size state and can process /// streams of arbitrary length. /// /// Here is a scheme of the data processing flow. After the subband splitting /// stage, every subband is processed independently. /// /// +-----------------+ +--------------+ +-----------+ /// audio stream +---> mono conversion +---> downsampling +---> whitening | /// +-----------------+ +--------------+ +---------+-+ /// | /// +-------------------+ +------------------+ | /// | subband splitting <---+ subband analysis <---+ /// +--+---+---+---+----+ +------------------+ /// | | | | /// | v v v /// | ... ... ... /// | /// | +------------------+ +-----------------+ /// +---> RMS downsampling +---> onset detection | /// +------------------+ +----------+------+ /// | /// +-----------------+ | /// hash codes <-------------------+ hash generation <----------+ /// +-----------------+ /// /// The hash codes from the hash generators of each band are then sent though a /// sorter which brings them into sequential temporal order before they are stored /// in the final list. /// </summary> /// <param name="track"></param> public void Generate(AudioTrack track) { IAudioStream audioStream = new ResamplingStream( new MonoStream(AudioStreamFactory.FromFileInfoIeee32(track.FileInfo)), ResamplingQuality.Medium, profile.SamplingRate); var whiteningStream = new WhiteningStream(audioStream, profile.WhiteningNumPoles, profile.WhiteningDecaySecs, profile.WhiteningBlockLength); var subbandAnalyzer = new SubbandAnalyzer(whiteningStream); float[] analyzedFrame = new float[profile.SubBands]; var bandAnalyzers = new BandAnalyzer[profile.SubBands]; for (int i = 0; i < profile.SubBands; i++) { bandAnalyzers[i] = new BandAnalyzer(profile, i); } List <SubFingerprint> hashes = new List <SubFingerprint>(); HashTimeSorter hashSorter = new HashTimeSorter(profile.SubBands); var sw = new Stopwatch(); sw.Start(); int totalFrames = subbandAnalyzer.WindowCount; int currentFrame = 0; while (subbandAnalyzer.HasNext()) { subbandAnalyzer.ReadFrame(analyzedFrame); for (int i = 0; i < profile.SubBands; i++) { bandAnalyzers[i].ProcessSample(analyzedFrame[i], hashSorter.Queues[i]); } if (currentFrame % 4096 == 0) { hashSorter.Fill(hashes, false); if (SubFingerprintsGenerated != null) { SubFingerprintsGenerated(this, new SubFingerprintsGeneratedEventArgs(track, hashes, currentFrame, totalFrames)); hashes.Clear(); } } currentFrame++; } for (int i = 0; i < bandAnalyzers.Length; i++) { bandAnalyzers[i].Flush(hashSorter.Queues[i]); } hashSorter.Fill(hashes, true); if (SubFingerprintsGenerated != null) { SubFingerprintsGenerated(this, new SubFingerprintsGeneratedEventArgs(track, hashes, currentFrame, totalFrames)); hashes.Clear(); } sw.Stop(); audioStream.Close(); Console.WriteLine("time: " + sw.Elapsed); }
public float Run() { IAudioStream audioStream = new ResamplingStream( new MonoStream(AudioStreamFactory.FromFileInfoIeee32(audioTrack.FileInfo)), ResamplingQuality.Medium, 11000); ContinuousFrequencyActivationQuantifier cfaq = new ContinuousFrequencyActivationQuantifier(audioStream); float[] cfaValue = new float[1]; float[] cfaValues = new float[cfaq.WindowCount]; Label[] cfaLabels = new Label[cfaq.WindowCount]; int count = 0; int musicCount = 0; while (cfaq.HasNext()) { cfaq.ReadFrame(cfaValue); cfaValues[count] = cfaValue[0]; if (cfaValue[0] > threshold) { musicCount++; cfaLabels[count] = Label.MUSIC; } Console.WriteLine("cfa {0,3}% {3} {1,5:0.00} {2}", (int)(Math.Round((float)count++ / cfaq.WindowCount * 100)), cfaValue[0], cfaValue[0] > threshold ? "MUSIC" : "", TimeUtil.BytesToTimeSpan(audioStream.Position, audioStream.Properties)); } audioStream.Close(); if (smoothing) { // 3.3 Smoothing /* majority filtering with sliding window ~5 secs * 1 frame = ~2,4 secs, at least 3 frames are needed for majority filtering -> 3 * ~2,4 secs = ~7,2 secs */ // filter out single NO_MUSIC frames for (int i = 2; i < cfaLabels.Length; i++) { if (cfaLabels[i - 2] == Label.MUSIC && cfaLabels[i - 1] == Label.NO_MUSIC && cfaLabels[i] == Label.MUSIC) { cfaLabels[i - 1] = Label.MUSIC; } } // filter out single MUSIC frames for (int i = 2; i < cfaLabels.Length; i++) { if (cfaLabels[i - 2] == Label.NO_MUSIC && cfaLabels[i - 1] == Label.MUSIC && cfaLabels[i] == Label.NO_MUSIC) { cfaLabels[i - 1] = Label.NO_MUSIC; } } // swap ~5 secs NO_MUSIC segments to MUSIC for (int i = 3; i < cfaLabels.Length; i++) { if (cfaLabels[i - 3] == Label.MUSIC && cfaLabels[i - 2] == Label.NO_MUSIC && cfaLabels[i - 1] == Label.NO_MUSIC && cfaLabels[i] == Label.MUSIC) { cfaLabels[i - 1] = Label.MUSIC; cfaLabels[i - 2] = Label.MUSIC; } } // swap ~5 secs NMUSIC segments to NO_MUSIC for (int i = 3; i < cfaLabels.Length; i++) { if (cfaLabels[i - 3] == Label.NO_MUSIC && cfaLabels[i - 2] == Label.MUSIC && cfaLabels[i - 1] == Label.MUSIC && cfaLabels[i] == Label.NO_MUSIC) { cfaLabels[i - 1] = Label.NO_MUSIC; cfaLabels[i - 2] = Label.NO_MUSIC; } } } float musicRatio = (float)musicCount / count; float musicRatioSmoothed = -1f; Console.WriteLine("'" + audioTrack.FileInfo.FullName + "' contains " + ((int)(Math.Round(musicRatio * 100))) + "% music"); if (smoothing) { musicCount = cfaLabels.Count <Label>(l => l == Label.MUSIC); musicRatioSmoothed = (float)musicCount / count; Console.WriteLine("smoothed: " + ((int)(Math.Round(musicRatioSmoothed * 100))) + "% music"); } if (writeLog) { FileInfo logFile = new FileInfo(audioTrack.FileInfo.FullName + ".music"); StreamWriter writer = logFile.CreateText(); writer.WriteLine(musicRatio + "; " + musicRatioSmoothed); writer.WriteLine(threshold); for (int i = 0; i < cfaValues.Length; i++) { writer.WriteLine("{0:0.00000}; {1}; \t{2}", cfaValues[i], cfaValues[i] > threshold ? Label.MUSIC : Label.NO_MUSIC, cfaLabels[i]); } writer.Flush(); writer.Close(); } return(0); }
public void Generate(AudioTrack track) { IAudioStream audioStream = new ResamplingStream( new MonoStream(AudioStreamFactory.FromFileInfoIeee32(track.FileInfo)), ResamplingQuality.Medium, profile.SamplingRate); var chroma = new Chroma(audioStream, profile.WindowSize, profile.HopSize, profile.WindowType, profile.ChromaMinFrequency, profile.ChromaMaxFrequency, false, profile.ChromaMappingMode); float[] chromaFrame; var chromaBuffer = new RingBuffer <float[]>(profile.ChromaFilterCoefficients.Length); var chromaFilterCoefficients = profile.ChromaFilterCoefficients; var filteredChromaFrame = new double[Chroma.Bins]; var classifiers = profile.Classifiers; var maxFilterWidth = classifiers.Max(c => c.Filter.Width); var integralImage = new IntegralImage(maxFilterWidth, Chroma.Bins); int index = 0; int indices = chroma.WindowCount; var subFingerprints = new List <SubFingerprint>(); while (chroma.HasNext()) { // Get chroma frame buffer // When the chroma buffer is full, we can take and reuse the oldest array chromaFrame = chromaBuffer.Count == chromaBuffer.Length ? chromaBuffer[0] : new float[Chroma.Bins]; // Read chroma frame into buffer chroma.ReadFrame(chromaFrame); // ChromaFilter chromaBuffer.Add(chromaFrame); if (chromaBuffer.Count < chromaBuffer.Length) { // Wait for the buffer to fill completely for the filtering to start continue; } Array.Clear(filteredChromaFrame, 0, filteredChromaFrame.Length); for (int i = 0; i < chromaFilterCoefficients.Length; i++) { var frame = chromaBuffer[i]; for (int j = 0; j < frame.Length; j++) { filteredChromaFrame[j] += frame[j] * chromaFilterCoefficients[i]; } } // ChromaNormalizer double euclideanNorm = 0; for (int i = 0; i < filteredChromaFrame.Length; i++) { var value = filteredChromaFrame[i]; euclideanNorm += value * value; } euclideanNorm = Math.Sqrt(euclideanNorm); if (euclideanNorm < profile.ChromaNormalizationThreshold) { Array.Clear(filteredChromaFrame, 0, filteredChromaFrame.Length); } else { for (int i = 0; i < filteredChromaFrame.Length; i++) { filteredChromaFrame[i] /= euclideanNorm; } } // ImageBuilder // ... just add one feature vector after another as rows to the image integralImage.AddColumn(filteredChromaFrame); // FingerprintCalculator if (integralImage.Columns < maxFilterWidth) { // Wait for the image to fill completely before hashes can be generated continue; } // Calculate subfingerprint hash uint hash = 0; for (int i = 0; i < classifiers.Length; i++) { hash = (hash << 2) | grayCodeMapping[classifiers[i].Classify(integralImage, 0)]; } // We have a SubFingerprint@frameTime subFingerprints.Add(new SubFingerprint(index, new SubFingerprintHash(hash), false)); index++; if (index % 512 == 0 && SubFingerprintsGenerated != null) { SubFingerprintsGenerated(this, new SubFingerprintsGeneratedEventArgs(track, subFingerprints, index, indices)); subFingerprints.Clear(); } } if (SubFingerprintsGenerated != null) { SubFingerprintsGenerated(this, new SubFingerprintsGeneratedEventArgs(track, subFingerprints, index, indices)); } if (Completed != null) { Completed(this, EventArgs.Empty); } audioStream.Close(); }
static void Main(string[] args) { if (args.Length == 0) { Console.WriteLine("no file(s) specified"); Console.WriteLine(); Console.WriteLine("Usage: MusicDetector file_1 file_2 ... file_n"); Console.WriteLine(""); Console.WriteLine("This tool expects at least one file, either specified as filename or " + "filename wildcard pattern. It scans all files for music content and writes " + "detection results in separate text files named {file_x}.music. Errors are logged " + "to a seperate error.log text file."); return; } // Use PFFFT as FFT implementation FFTFactory.Factory = new Aurio.PFFFT.FFTFactory(); // Use Soxr as resampler implementation ResamplerFactory.Factory = new Aurio.Soxr.ResamplerFactory(); // Use FFmpeg for file reading/decoding AudioStreamFactory.AddFactory(new FFmpegAudioStreamFactory()); Queue <FileInfo> scanQueue = new Queue <FileInfo>(); foreach (string file in args) { if (file.Contains("*")) { int slashIndex = file.LastIndexOf(@"\"); var di = new DirectoryInfo(slashIndex > -1 ? file.Substring(0, slashIndex) : "."); foreach (var fi in di.GetFiles(file.Substring(slashIndex > -1 ? slashIndex + 1 : 0), SearchOption.TopDirectoryOnly)) { scanQueue.Enqueue(fi); } } else { scanQueue.Enqueue(new FileInfo(file)); } } FileInfo errorLogFileInfo = new FileInfo("error.log"); StreamWriter errorLogWriter = errorLogFileInfo.AppendText(); foreach (var fi in scanQueue) { try { if (fi.Exists && fi.Length > 0) { new CFA(new AudioTrack(fi), CFA.DEFAULT_THRESHOLD, true, true).Run(); } else { throw new FileNotFoundException(String.Format("file '{0}' not existing or empty, skipping", fi.FullName)); } } catch (FileNotFoundException e) { Console.WriteLine(DateTime.Now + " " + e.Message); errorLogWriter.WriteLine(DateTime.Now + " " + e.Message); } catch (Exception e) { Console.WriteLine(DateTime.Now + " " + e.ToString()); Console.WriteLine(); errorLogWriter.WriteLine(DateTime.Now + " " + fi.FullName); errorLogWriter.WriteLine(e.ToString()); } } errorLogWriter.Flush(); errorLogWriter.Close(); }
public MainWindow() { // Use PFFFT as FFT implementation FFTFactory.Factory = new Aurio.PFFFT.FFTFactory(); // Use Soxr as resampler implementation ResamplerFactory.Factory = new Aurio.Soxr.ResamplerFactory(); // Use FFmpeg for file reading/decoding AudioStreamFactory.AddFactory(new FFmpegAudioStreamFactory()); recentProjects = new RecentProjects(); /* * The menu items of the recently opened project are handled here in code behind * because in XAML (ItemsSource/CompositeCollection/CollectionViewSource/CollectionContainer) * there's a few problems I spent too much time with and could not solve. This * programmatic solution works perfectly. * * - When overriding the ItemContainerStyle, which is necessary to automatically * set the Header and Command, the style breaks... this is probably because of * the weird style combination I'm using in this app and wouldn't happen with * the default style (setting ItemContainerStyle replaces the style in the locally * included style file and falls back to proerties of the default style for the app). * - When setting the header text through the style, the header text of all statically * defined MenuItems gets overwritten because they don't have the header directly * set but get the header text from the associated command. So the header text * overrules the command text. */ recentProjects.MenuEntries.CollectionChanged += delegate(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { // Always rebuilt the whole menu... happens very seldomly and shouldn't be a noticable performance impact int separatorIndex = FileMenu.Items.IndexOf(RecentSeparator); // Clear old entries if (FileMenu.Items.Count > (separatorIndex + 1)) { for (int x = FileMenu.Items.Count - 1; x > separatorIndex; x--) { FileMenu.Items.RemoveAt(x); } } Debug.Assert(FileMenu.Items.Count == separatorIndex + 1, "wrong menu delete count"); // Add new entries int count = 0; foreach (RecentProjects.RecentEntry entry in recentProjects.MenuEntries) { // Determine if this item represents a real project to be numbered bool projectEntry = entry.Parameter != null && entry.Parameter != RecentProjects.ClearCommand; FileMenu.Items.Add(new MenuItem { Header = (projectEntry ? (++count) + " " : "") + entry.Title.Replace("_", "__"), IsEnabled = entry.Enabled, Command = Commands.FileOpenRecentProject, CommandParameter = entry.Parameter }); } Debug.Assert(FileMenu.Items.Count == separatorIndex + 1 + recentProjects.MenuEntries.Count, "wrong menu item count"); }; project = new Project(); trackList = new TrackList <AudioTrack>(); InitializeComponent(); recentProjects.Load(); }
/// <summary> /// Creates a Wave format proxy file in the same directory and with the same name as the specified file, /// if no storage directory is specified (i.e. if it is null). If a storage directory is specified, the proxy /// file will be stored in the specified directory with a hashed file name to avoid name collisions and /// file overwrites. The story directory option is convenient for the usage of temporary or working directories. /// </summary> /// <param name="fileInfo">the file for which a proxy file should be created</param> /// <param name="storageDirectory">optional directory where the proxy file will be stored, can be null</param> /// <returns>the FileInfo of the proxy file</returns> public static FileInfo CreateWaveProxy(FileInfo fileInfo, DirectoryInfo storageDirectory) { FileInfo outputFileInfo; if (storageDirectory == null) { // Without a storage directory, store the proxy file beside the original file outputFileInfo = new FileInfo(fileInfo.FullName + ".ffproxy.wav"); } else { // With a storage directory specified, store the proxy file with a hashed name // (to avoid name collision / overwrites) in the target directory (e.g. a temp or working directory) using (var sha256 = SHA256.Create()) { byte[] hash = sha256.ComputeHash(Encoding.Unicode.GetBytes(fileInfo.FullName)); string hashString = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); outputFileInfo = new FileInfo(Path.Combine(storageDirectory.FullName, hashString + ".ffproxy.wav")); } } if (outputFileInfo.Exists) { Console.WriteLine("Proxy already existing, using " + outputFileInfo.Name); return(outputFileInfo); } var reader = new FFmpegReader(fileInfo, FFmpeg.Type.Audio); var writer = new MemoryWriterStream(new AudioProperties( reader.AudioOutputConfig.format.channels, reader.AudioOutputConfig.format.sample_rate, reader.AudioOutputConfig.format.sample_size * 8, reader.AudioOutputConfig.format.sample_size == 4 ? AudioFormat.IEEE : AudioFormat.LPCM)); int output_buffer_size = reader.AudioOutputConfig.frame_size * writer.SampleBlockSize; byte[] output_buffer = new byte[output_buffer_size]; int samplesRead; long timestamp; FFmpeg.Type type; // sequentially read samples from decoder and write it to wav file while ((samplesRead = reader.ReadFrame(out timestamp, output_buffer, output_buffer_size, out type)) > 0) { int bytesRead = samplesRead * writer.SampleBlockSize; writer.Write(output_buffer, 0, bytesRead); } reader.Dispose(); writer.Position = 0; AudioStreamFactory.WriteToFile(writer, outputFileInfo.FullName); writer.Close(); return(outputFileInfo); }