public static void Modify( string filename, AudioUtilityInfo sourceExpected, AudioUtilityRequest request, string outputMimeType, AudioUtilityInfo outputExpected, Action <AudioUtilityInfo, AudioUtilityInfo> additionalTests = null) { var source = PathHelper.GetTestAudioFile(filename); var destExtension = MediaTypes.GetExtension(outputMimeType); var outputFilename = Path.GetFileNameWithoutExtension(filename) + "_modified." + destExtension; foreach (var util in new[] { TestHelper.GetAudioUtility() }) { var dir = PathHelper.GetTempDir(); var output = new FileInfo(Path.Combine(dir.FullName, outputFilename)); util.Modify(source, MediaTypes.GetMediaType(source.Extension), output, outputMimeType, request); var sourceInfo = util.Info(source); TestHelper.CheckAudioUtilityInfo(sourceExpected, sourceInfo); var outputInfo = util.Info(output); var outputInfoText = GetDurationInfo(outputInfo); additionalTests?.Invoke(sourceExpected, sourceInfo); PathHelper.DeleteTempDir(dir); } }
public void SegmentsWavpackCorrectly3Wavunpack() { var expected = new AudioUtilityInfo { Duration = TimeSpan.FromSeconds(55), SampleRate = 22050, ChannelCount = 1, MediaType = MediaTypes.MediaTypeWav, BitsPerSecond = 353000, }; var request = new AudioUtilityRequest { MixDownToMono = false, OffsetStart = TimeSpan.FromSeconds(0), OffsetEnd = TimeSpan.FromSeconds(55), //SampleRate = 11025 }; var source = TestHelper.GetAudioFile("f969b39d-2705-42fc-992c-252a776f1af3_090705-0600.wv"); var output = PathHelper.GetTempFile(MediaTypes.ExtWav); TestHelper.GetAudioUtilityWavunpack().Modify(source, MediaTypes.GetMediaType(source.Extension), output, MediaTypes.GetMediaType(output.Extension), request); var actual = TestHelper.GetAudioUtility().Info(output); File.Delete(output.FullName); TestHelper.CheckAudioUtilityInfo(expected, actual); }
public void SegmentsWavpackCorrectly7Master() { var expected = new AudioUtilityInfo { Duration = TimeSpan.FromMinutes(10) + TimeSpan.FromSeconds(0), SampleRate = 17460, ChannelCount = 1, MediaType = MediaTypes.MediaTypeWav, BitsPerSecond = 279000, }; var request = new AudioUtilityRequest { MixDownToMono = true, TargetSampleRate = 17460, }; var util = TestHelper.GetAudioUtility(); var source = TestHelper.GetAudioFile("f969b39d-2705-42fc-992c-252a776f1af3_090705-0600.wv"); var output = PathHelper.GetTempFile(MediaTypes.ExtWav); util.Modify(source, MediaTypes.GetMediaType(source.Extension), output, MediaTypes.GetMediaType(output.Extension), request); var actual = util.Info(output); File.Delete(output.FullName); TestHelper.CheckAudioUtilityInfo(expected, actual); }
public void SegmentsMp3Correctly2Sox() { var expected = new AudioUtilityInfo { Duration = TimeSpan.FromMinutes(3) + TimeSpan.FromSeconds(20), SampleRate = 22050, ChannelCount = 1, MediaType = MediaTypes.MediaTypeMp3, BitsPerSecond = 32000, }; var request = new AudioUtilityRequest { MixDownToMono = true, OffsetStart = TimeSpan.FromSeconds(15), OffsetEnd = TimeSpan.FromMinutes(3) + TimeSpan.FromSeconds(35), }; var util = TestHelper.GetAudioUtilitySox(); var source = TestHelper.GetAudioFile("Currawongs_curlew_West_Knoll_Bees_20091102-183000.mp3"); var output = PathHelper.GetTempFile(MediaTypes.ExtMp3); util.Modify(source, MediaTypes.GetMediaType(source.Extension), output, MediaTypes.GetMediaType(output.Extension), request); var actual = util.Info(output); File.Delete(output.FullName); TestHelper.CheckAudioUtilityInfo(expected, actual); }
public void SegmentsMp3Correctly6Master() { var expected = new AudioUtilityInfo { Duration = TimeSpan.FromSeconds(134.6), SampleRate = 44100, ChannelCount = 1, MediaType = MediaTypes.MediaTypeMp3, BitsPerSecond = 64000, }; var request = new AudioUtilityRequest { MixDownToMono = false, Channels = 2.AsArray(), OffsetStart = TimeSpan.FromSeconds(27), TargetSampleRate = 44100, }; var util = TestHelper.GetAudioUtility(); var source = TestHelper.GetAudioFile("A French Fiddle Speaks.mp3"); var output = PathHelper.GetTempFile(MediaTypes.ExtMp3); util.Modify(source, MediaTypes.GetMediaType(source.Extension), output, MediaTypes.GetMediaType(output.Extension), request); var actual = util.Info(output); File.Delete(output.FullName); TestHelper.CheckAudioUtilityInfo(expected, actual); }
public void SegmentsWavCorrectly5Master() { var expected = new AudioUtilityInfo { Duration = TimeSpan.FromSeconds(90), SampleRate = 17460, ChannelCount = 1, MediaType = MediaTypes.MediaTypeWav, BitsPerSecond = 279000, }; var request = new AudioUtilityRequest { MixDownToMono = true, OffsetStart = TimeSpan.FromSeconds(27), OffsetEnd = TimeSpan.FromSeconds(117), TargetSampleRate = 17460, }; var util = TestHelper.GetAudioUtility(); var source = TestHelper.GetAudioFile("FemaleKoala MaleKoala.wav"); var output = PathHelper.GetTempFile(MediaTypes.ExtWav); util.Modify(source, MediaTypes.GetMediaType(source.Extension), output, MediaTypes.GetMediaType(output.Extension), request); var actual = util.Info(output); File.Delete(output.FullName); TestHelper.CheckAudioUtilityInfo(expected, actual); }
public void SegmentsWavCorrectly6Shntool() { var expected = new AudioUtilityInfo { Duration = TimeSpan.FromSeconds(93), SampleRate = 17460, ChannelCount = 1, MediaType = MediaTypes.MediaTypeWav, BitsPerSecond = 279000, }; var request = new AudioUtilityRequest { //MixDownToMono = false, //Channel = 2, OffsetStart = TimeSpan.FromSeconds(27), //TargetSampleRate = 17460, }; var util = TestHelper.GetAudioUtilityShntool(); var source = TestHelper.GetAudioFile("geckos.wav"); var output = PathHelper.GetTempFile(MediaTypes.ExtWav); util.Modify(source, MediaTypes.GetMediaType(source.Extension), output, MediaTypes.GetMediaType(output.Extension), request); var actual = util.Info(output); File.Delete(output.FullName); TestHelper.CheckAudioUtilityInfo(expected, actual); }
public void SoxResamplingShouldBeDeterministic() { var expected = new AudioUtilityInfo { Duration = TimeSpan.FromSeconds(60), SampleRate = 22050, ChannelCount = 1, MediaType = MediaTypes.MediaTypeWav, BitsPerSecond = 352800, }; var request = new AudioUtilityRequest { MixDownToMono = true, TargetSampleRate = 22050, }; var util = TestHelper.GetAudioUtility(); var source = TestHelper.GetAudioFile("CaneToad_Gympie_44100.wav"); var repeats = new double[5][]; for (int r = 0; r < repeats.Length; r++) { var output = PathHelper.GetTempFile(MediaTypes.ExtWav); util.Modify(source, MediaTypes.GetMediaType(source.Extension), output, MediaTypes.GetMediaType(output.Extension), request); var actual = util.Info(output); TestHelper.CheckAudioUtilityInfo(expected, actual); var reader = new WavReader(output); TestHelper.WavReaderAssertions(reader, actual); repeats[r] = reader.Samples; File.Delete(output.FullName); } for (int i = 1; i < repeats.Length; i++) { Assert.AreEqual(repeats[0].Length, repeats[1].Length); var totalDifference = 0.0; for (int j = 0; j < repeats[0].Length; j++) { var delta = Math.Abs(repeats[i][j] - repeats[0][j]); totalDifference += delta; } CollectionAssert.AreEqual(repeats[0], repeats[i], $"Repeat {i} was not identical to repeat 0. Total delta: {totalDifference}"); } }
public static FileSegment ToSegment(this AudioUtilityInfo info, FileSegment.FileDateBehavior dateBehavior = FileSegment.FileDateBehavior.Try) { var source = new FileSegment( info.SourceFile, info.SampleRate.Value, info.Duration.Value, dateBehavior); return(source); }
protected override void CheckRequestValid(FileInfo source, string sourceMimeType, FileInfo output, string outputMediaType, AudioUtilityRequest request) { if (AppConfigHelper.IsMacOsX && (sourceMimeType == MediaTypes.MediaTypeMp3 || outputMediaType == MediaTypes.MediaTypeMp3)) { throw new AudioFormatNotSupportedException(Mp3NotSupportedOnOSX); } AudioUtilityInfo info = null; if (request?.BitDepth != null) { const string message = "Haven't added support for changing bit depth in" + nameof(SoxAudioUtility); throw new BitDepthOperationNotImplemented(message); } // check that if output is mp3, the bit rate and sample rate are set valid amounts. if (request != null && outputMediaType == MediaTypes.MediaTypeMp3) { if (request.TargetSampleRate.HasValue) { // sample rate is set - check it this.CheckMp3SampleRate(request.TargetSampleRate.Value); } else { // sample rate is not set, get it from the source file info = this.Info(source); if (!info.SampleRate.HasValue) { throw new ArgumentException("Sample rate for output mp3 may not be correct, as sample rate is not set, and cannot be determined from source file."); } this.CheckMp3SampleRate(info.SampleRate.Value); } } if (request != null && request.Channels.NotNull()) { if (request.Channels.Length == 0) { throw new ChannelSelectionOperationNotImplemented("Sox utility cannot choose 0 channels"); } int max = request.Channels.Max(); int min = request.Channels.Min(); info ??= this.Info(source); if (max > info.ChannelCount || min < 1) { var msg = $"Requested channel number was out of range. Requested channel {max} but there are only {info.ChannelCount} channels in {source}."; throw new ChannelNotAvailableException(nameof(request.Channels), request.Channels.ToCommaSeparatedList(), msg); } } }
private static void DoFrequencyAnalysis( AudioUtilityInfo expected, int[][] expectedFrequencies) { var reader = new WavReader(expected.SourceFile); TestHelper.WavReaderAssertions(reader, expected); for (var channel = 0; channel < expectedFrequencies.Length; channel++) { var samples = reader.GetChannel(channel); TestHelper.AssertFrequencyInSignal(reader, samples, expectedFrequencies[channel]); } }
public static void WavReaderAssertions(WavReader reader, AudioUtilityInfo info) { BaseTest.Assert.AreEqual(info.ChannelCount.Value, reader.Channels); if (info.BitsPerSample.HasValue) { BaseTest.Assert.AreEqual(info.BitsPerSample.Value, reader.BitsPerSample); } BaseTest.Assert.AreEqual(info.SampleRate.Value, reader.SampleRate); BaseTest.Assert.AreEqual(info.Duration.Value.TotalMilliseconds, reader.Time.TotalMilliseconds, 150); BaseTest.Assert.AreEqual( (int)(info.Duration.Value.TotalSeconds * info.SampleRate.Value), reader.BlockCount, 100); }
public void ConvertsMp3ToMp3Corectly() { var expected = new AudioUtilityInfo { ChannelCount = 1, SampleRate = 22050, Duration = TimeSpan.FromSeconds(240.031), BitsPerSecond = 96000, MediaType = MediaTypes.MediaTypeMp3, }; Modify( "Currawongs_curlew_West_Knoll_Bees_20091102-183000.mp3", expected, new AudioUtilityRequest { }, MediaTypes.MediaTypeMp3, expected); }
private static string GetDurationInfo(AudioUtilityInfo info) { var durationText = string.Join( ", ", info.RawData.Where( l => l.Key.ToLowerInvariant().Contains("duration") || l.Key.ToLowerInvariant().Contains("length"))); if (info.Duration.HasValue) { durationText += ", Duration: " + info.Duration; durationText = durationText.Trim(' ', ','); } //using (var cr = new ConsoleRedirector()) //{ // LoggedConsole.WriteLine(durationText); //} return(durationText); }
public void ConvertsMp3ToWavCorrectly() { var sourceInfo = TestHelper.AudioDetails["Currawongs_curlew_West_Knoll_Bees_20091102-183000.mp3"]; var expected = new AudioUtilityInfo { ChannelCount = 1, SampleRate = 22050, Duration = TimeSpan.FromSeconds(240.031), BitsPerSecond = 96000, MediaType = MediaTypes.MediaTypeWav, }; Modify( "Currawongs_curlew_West_Knoll_Bees_20091102-183000.mp3", sourceInfo, new AudioUtilityRequest { }, MediaTypes.MediaTypeWav, expected); }
public static void CheckAudioUtilityInfo(AudioUtilityInfo expected, AudioUtilityInfo actual, int epsilonDurationMilliseconds = 150) { if (expected.BitsPerSample.HasValue && actual.BitsPerSample.HasValue) { Assert.AreEqual(expected.BitsPerSample.Value, actual.BitsPerSample.Value); } if (expected.BitsPerSample.HasValue && !actual.BitsPerSample.HasValue) { Assert.Fail($"BitsPerSample failed. Expected: {expected.BitsPerSample}, actual: {actual.BitsPerSample}"); } if (expected.BitsPerSecond.HasValue && actual.BitsPerSecond.HasValue) { // Sox only reports three decimal places and rounds other things var actualBps = (int)((double)actual.BitsPerSecond.Value).RoundToSignificantDigits(3); var expectedBps = (int)((double)expected.BitsPerSecond.Value).RoundToSignificantDigits(3); Assert.AreEqual(expectedBps, actualBps, 0); } if (expected.BitsPerSecond.HasValue && !actual.BitsPerSecond.HasValue) { Assert.Fail($"BitsPerSecond failed. Expected: {expected.BitsPerSecond}, actual: {actual.BitsPerSecond}"); } Assert.IsTrue(!string.IsNullOrWhiteSpace(expected.MediaType)); Assert.IsTrue(expected.ChannelCount.HasValue); Assert.IsTrue(expected.Duration.HasValue); Assert.IsTrue(expected.SampleRate.HasValue); Assert.IsTrue(!string.IsNullOrWhiteSpace(actual.MediaType)); Assert.IsTrue(actual.ChannelCount.HasValue); Assert.IsTrue(actual.Duration.HasValue); Assert.IsTrue(actual.SampleRate.HasValue); Assert.AreEqual(expected.MediaType, actual.MediaType); Assert.AreEqual(expected.ChannelCount.Value, actual.ChannelCount.Value); Assert.AreEqual(expected.Duration.Value.TotalMilliseconds, actual.Duration.Value.TotalMilliseconds, TimeSpan.FromMilliseconds(epsilonDurationMilliseconds).TotalMilliseconds); Assert.AreEqual(expected.SampleRate.Value, actual.SampleRate.Value); }
public void SegmentsRawPcmCorrectlyMaster(object startWrapped, object endWrapped, bool mixDown, int expectedChannels, int expectedBitRate) { double?start = (double?)startWrapped; double?end = (double?)endWrapped; var duration = TimeSpan.FromSeconds((end ?? 60.0) - (start ?? 0.0)); var expected = new AudioUtilityInfo { Duration = duration, SampleRate = 44100, ChannelCount = expectedChannels, MediaType = MediaTypes.MediaTypeWav, BitsPerSecond = expectedBitRate, }; var request = new AudioUtilityRequest { MixDownToMono = mixDown, OffsetStart = start?.Seconds(), OffsetEnd = end?.Seconds(), BitDepth = 16, TargetSampleRate = 44100, Channels = new[] { 1, 2, 3, 4 }, }; TestHelper .GetAudioUtility() .Modify( this.source, MediaTypes.GetMediaType(this.source.Extension), this.output, MediaTypes.GetMediaType(this.output.Extension), request); var actual = TestHelper.GetAudioUtility().Info(this.output); TestHelper.CheckAudioUtilityInfo(expected, actual); }
public void SegmentsMp3Correctly3Master() { /* * * mp3splt accuracy varies with the quality of the input file! * */ var expected = new AudioUtilityInfo { Duration = TimeSpan.FromSeconds(48), SampleRate = 11025, ChannelCount = 1, MediaType = MediaTypes.MediaTypeMp3, BitsPerSecond = 16000, }; var request = new AudioUtilityRequest { MixDownToMono = true, OffsetStart = TimeSpan.Zero, OffsetEnd = TimeSpan.FromSeconds(48), TargetSampleRate = 11025, }; var util = TestHelper.GetAudioUtility(); var source = TestHelper.GetAudioFile("Currawongs_curlew_West_Knoll_Bees_20091102-183000.mp3"); var output = PathHelper.GetTempFile(MediaTypes.ExtMp3); util.Modify(source, MediaTypes.GetMediaType(source.Extension), output, MediaTypes.GetMediaType(output.Extension), request); var actual = util.Info(output); File.Delete(output.FullName); TestHelper.CheckAudioUtilityInfo(expected, actual, 380); }
/// <summary> /// The get info. /// </summary> /// <param name="source"> /// The source. /// </param> /// <param name="process"> /// The process. /// </param> /// <returns> /// The Acoustics.Tools.AudioUtilityInfo. /// </returns> protected override AudioUtilityInfo GetInfo(FileInfo source, ProcessRunner process) { IEnumerable <string> errorlines = process.ErrorOutput.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); // if no lines, or any line contains "no handler for file extension", return empty if (errorlines.Any(l => l.Contains("no handler for file extension"))) { return(new AudioUtilityInfo()); } IEnumerable <string> lines = process.StandardOutput.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); var result = new AudioUtilityInfo(); result.SourceFile = source; foreach (var line in lines) { var firstColon = line.IndexOf(':'); if (firstColon > -1) { var key = line.Substring(0, firstColon).Trim(); var value = line.Substring(firstColon + 1).Trim(); if (key == "Duration") { var values = value.Split('=', '~'); // duration result.RawData.Add(key, values[0].Trim()); // sample count result.RawData.Add("Samples", values[1].Replace("samples", string.Empty).Trim()); // approx. CDDA sectors result.RawData.Add("CDDA sectors", values[2].Replace("CDDA sectors", string.Empty).Trim()); } else { result.RawData.Add(key, value); } } } // parse info info class const string keyDuration = "Duration"; const string keySamples = "Samples"; const string keyBitRate = "Bit Rate"; const string keySampleRate = "Sample Rate"; const string keyChannels = "Channels"; const string keyPrecision = "Precision"; if (result.RawData.ContainsKey(keySampleRate)) { result.SampleRate = this.ParseIntStringWithException(result.RawData[keySampleRate], "sox.samplerate"); } if (result.RawData.ContainsKey(keyDuration)) { var sampleCount = result.RawData[keySamples]; // most precise and efficient calculation, duration from sample count if (sampleCount.IsNotEmpty() && result.SampleRate.HasValue) { var samples = this.ParseLongStringWithException(sampleCount, "Samples"); var duration = Math.Round((decimal)samples.Value / result.SampleRate.Value, 6, MidpointRounding.AwayFromZero); result.Duration = TimeSpan.FromSeconds((double)duration); } else { // less precise and problematic for very long recordings (because of timespan parsing) // but still faster than another invocation var stringDuration = result.RawData[keyDuration]; var formats = new[] { @"h\:mm\:ss\.ff", @"hh\:mm\:ss\.ff", @"h:mm:ss.ff", @"hh:mm:ss.ff", }; if (TimeSpan.TryParseExact(stringDuration.Trim(), formats, CultureInfo.InvariantCulture, out var duration)) { result.Duration = duration; } else { // last resort: ask sox specifically for the duration with another invocation // AT: this case used to always happen even if it was not necessary var extra = this.Duration(source); if (extra.HasValue) { result.Duration = extra.Value; } } } } if (result.RawData.ContainsKey(keyChannels)) { result.ChannelCount = this.ParseIntStringWithException(result.RawData[keyChannels], "sox.channels"); } if (result.RawData.ContainsKey(keyPrecision)) { result.BitsPerSample = this.ParseIntStringWithException(result.RawData[keyPrecision].Replace("-bit", string.Empty).Trim(), "sox.precision"); if (result.BitsPerSample < 1) { result.BitsPerSample = null; } } result.MediaType = GetMediaType(result.RawData, source.Extension); if (result.RawData.ContainsKey(keyBitRate)) { var stringValue = result.RawData[keyBitRate]; int magnitude = 1; if (stringValue.Contains("k")) { stringValue = stringValue.Replace("k", string.Empty); magnitude = 1000; } if (stringValue.Contains("M")) { stringValue = stringValue.Replace("M", string.Empty); magnitude = 1_000_000; } if (stringValue.Contains("G") || stringValue.Contains("T")) { throw new NotSupportedException( $"The file {source.FullName} reported a bit rate of {stringValue} which is not supported"); } var value = double.Parse(stringValue, CultureInfo.InvariantCulture); value = value * magnitude; result.BitsPerSecond = Convert.ToInt32(value); // Further investigation reveals the 'error' I was chasing down was in fact the difference // between the FormatBitRate (averaged inc. header) and the StreamBitRate. // For long files this difference approaches 0, for short files the difference is significant //if (result.MediaType == MediaTypes.MediaTypeWav) //{ // // deal with inaccuracy - calculate it a second way // var estimatedBitRate = result.SampleRate * result.ChannelCount * result.BitsPerSample; // int roundedBitRate = (int)((double)estimatedBitRate).RoundToSignficantDigits(3); // if (roundedBitRate != result.BitsPerSecond.Value) // { // throw new InvalidOperationException( // $"SoxAudioUtlity could not accurately predict BitPerSecond. Parsed BitsPerSecond: {result.BitsPerSecond}, predicted: {estimatedBitRate}"); // } // // // use estimated bit rate // result.BitsPerSecond = estimatedBitRate; //} } return(result); }
private AudioUtilityInfo Combine(AudioUtilityInfo info, AudioUtilityInfo extra) { var result = new AudioUtilityInfo(); if (info == null && extra == null) { return(result); } if (info == null) { return(extra); } if (extra == null) { return(info); } // source file if (info.SourceFile != null && extra.SourceFile != null && info.SourceFile != extra.SourceFile) { throw new InvalidOperationException(string.Format("Source files must be the same: {0} != {1}.", info.SourceFile, extra.SourceFile)); } if (info.SourceFile != null) { result.SourceFile = info.SourceFile; } else if (extra.SourceFile != null) { result.SourceFile = extra.SourceFile; } // bits per sample if (info.BitsPerSample.HasValue) { result.BitsPerSample = info.BitsPerSample; } else if (extra.BitsPerSample.HasValue) { result.BitsPerSample = extra.BitsPerSample; } // bits per second if (info.BitsPerSecond.HasValue) { result.BitsPerSecond = info.BitsPerSecond; } else if (extra.BitsPerSecond.HasValue) { result.BitsPerSecond = extra.BitsPerSecond; } // channel count if (info.ChannelCount.HasValue) { result.ChannelCount = info.ChannelCount; } else if (extra.ChannelCount.HasValue) { result.ChannelCount = extra.ChannelCount; } // duration if (info.Duration.HasValue) { result.Duration = info.Duration; } else if (extra.Duration.HasValue) { result.Duration = extra.Duration; } // media type if (!string.IsNullOrWhiteSpace(info.MediaType)) { result.MediaType = info.MediaType; } else if (!string.IsNullOrWhiteSpace(extra.MediaType)) { result.MediaType = extra.MediaType; } // sample rate if (info.SampleRate.HasValue) { result.SampleRate = info.SampleRate; } else if (extra.SampleRate.HasValue) { result.SampleRate = extra.SampleRate; } // combine raw data if (info.RawData != null) { foreach (var item in info.RawData) { result.RawData.Add(item.Key, item.Value); } } if (extra.RawData != null) { foreach (var item in extra.RawData) { result.RawData.Add(item.Key, item.Value); } } return(result); }
/// <summary> /// The get info. /// </summary> /// <param name="source"> /// The source. /// </param> /// <param name="process"> /// The process. /// </param> /// <returns> /// The Acoustics.Tools.AudioUtilityInfo. /// </returns> protected override AudioUtilityInfo GetInfo(FileInfo source, ProcessRunner process) { var result = new AudioUtilityInfo(); var std = process.StandardOutput; const string Length = "Length:"; const string Channels = "Channels:"; const string BitsPerSample = "Bits/sample:"; const string SamplePerSecond = "Samples/sec:"; const string FileName = "File name:"; const string BitsPerSecond = "Average bytes/sec:"; const string WavDataSize = "Data size:"; long wavDataSizeBytes = 0; foreach (var line in std.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)) { if (line.StartsWith(Length)) { result.Duration = Parse(line.Replace(Length, string.Empty).Trim()); } if (line.StartsWith(Channels)) { result.ChannelCount = this.ParseIntStringWithException(line.Replace(Channels, string.Empty).Trim(), "shntool.channels"); } if (line.StartsWith(BitsPerSample)) { result.BitsPerSample = this.ParseIntStringWithException(line.Replace(BitsPerSample, string.Empty).Trim(), "shntool.bitspersample"); } if (line.StartsWith(SamplePerSecond)) { result.SampleRate = this.ParseIntStringWithException(line.Replace(SamplePerSecond, string.Empty).Trim(), "shntool.samplespersecond"); } if (line.StartsWith(FileName)) { result.SourceFile = new FileInfo(line.Replace(FileName, string.Empty).Trim()); } if (line.StartsWith(BitsPerSecond)) { // convert bytes to bits result.BitsPerSecond = this.ParseIntStringWithException(line.Replace(BitsPerSecond, string.Empty).Trim(), "shntool.BitsPerSecond") * 8; } if (line.StartsWith(WavDataSize)) { wavDataSizeBytes = this.ParseLongStringWithException( line.Replace(WavDataSize, string.Empty).Replace("bytes", string.Empty).Trim(), "shntool.DataSize").Value; } result.MediaType = MediaTypes.MediaTypeWav; if (line.Contains(":")) { result.RawData.Add( line.Substring(0, line.IndexOf(":", StringComparison.Ordinal)).Trim(), line.Substring(line.IndexOf(":", StringComparison.Ordinal) + 1).Trim()); } } // shntool is bloody annoying - for CD quality files it estimates the duration to print out. // See http://linux.die.net/man/1/shntool, https://github.com/flacon/shntool/blob/4c6fc2e58c830080f6f9112935325ad281b784ff/src/core_wave.c#L268 // and https://github.com/flacon/shntool/blob/4c6fc2e58c830080f6f9112935325ad281b784ff/src/core_mode.c#L781 // for explanations. // For now, we just have to sanity check it's reported duration. // exact duration = dataSize * 8 / bitrate // bitrate = samplesPerSecond * channels * bitsPerSample var bitrate = result.SampleRate.Value * result.ChannelCount.Value * result.BitsPerSample.Value; var exactDuration = TimeSpan.FromSeconds((double)(wavDataSizeBytes * 8) / bitrate); if (exactDuration - result.Duration.Value > TimeSpan.FromMilliseconds(100)) { this.Log.Warn("Shntool reported a bad duration because it parsed a file as CD quality. Actual duration has been returned"); result.Duration = exactDuration; } return(result); }
/// <summary> /// The get info. /// </summary> /// <param name="source"> /// The source. /// </param> /// <param name="process"> /// The process. /// </param> /// <returns> /// The Acoustics.Tools.AudioUtilityInfo. /// </returns> protected override AudioUtilityInfo GetInfo(FileInfo source, ProcessRunner process) { var lines = process.StandardOutput.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); var result = new AudioUtilityInfo(); result.SourceFile = source; foreach (var line in lines) { var firstColon = line.IndexOf(':'); if (firstColon > -1) { var key = line.Substring(0, firstColon).Trim(); var value = line.Substring(firstColon + 1).Trim(); if (key == "source") { var values = value.Split(' '); // precision result.RawData.Add("precision", values[0].Trim()); // sample rate result.RawData.Add("sample rate", values[3].Trim()); } else { result.RawData.Add(key, value); } } } // parse info into class const string KeyDuration = "duration"; const string KeyBitRate = "ave bitrate"; const string KeySampleRate = "sample rate"; const string KeyChannels = "channels"; const string KeyPrecision = "precision"; if (result.RawData.ContainsKey(KeyDuration)) { var stringDuration = result.RawData[KeyDuration]; var formats = new[] { @"h\:mm\:ss\.ff", @"hh\:mm\:ss\.ff", @"h:mm:ss.ff", @"hh:mm:ss.ff", }; TimeSpan tsresult; if (TimeSpan.TryParseExact(stringDuration.Trim(), formats, CultureInfo.InvariantCulture, out tsresult)) { result.Duration = tsresult; } } if (result.RawData.ContainsKey(KeyBitRate)) { var stringValue = result.RawData[KeyBitRate]; var hadK = false; if (stringValue.Contains("kbps")) { stringValue = stringValue.Replace("kbps", string.Empty); hadK = true; } var value = double.Parse(stringValue); if (hadK) { value = value * 1000; } result.BitsPerSecond = Convert.ToInt32(value); } if (result.RawData.ContainsKey(KeySampleRate)) { result.SampleRate = this.ParseIntStringWithException(result.RawData[KeySampleRate], "wavPack.SampleRate"); } if (result.RawData.ContainsKey(KeyChannels)) { // multi channel will attempt to print the channel names "4 (FL,FR,FC,LFE)" string channels = result.RawData[KeyChannels]; channels = Regex.Replace(channels, "\\(.*\\).*", string.Empty).Trim(); result.ChannelCount = this.ParseIntStringWithException(channels, "wavPack.Channels"); } if (result.RawData.ContainsKey(KeyPrecision)) { result.BitsPerSample = this.ParseIntStringWithException(result.RawData[KeyPrecision].Replace("-bit", string.Empty).Trim(), "wavPack.Precision"); } result.MediaType = MediaTypes.MediaTypeWavpack; return(result); }
/// <summary> /// The get info. /// </summary> /// <param name="source"> /// The source. /// </param> /// <param name="process"> /// The process. /// </param> /// <returns> /// The Acoustics.Tools.AudioUtilityInfo. /// </returns> protected override AudioUtilityInfo GetInfo(FileInfo source, ProcessRunner process) { var result = new AudioUtilityInfo(); result.SourceFile = source; // parse output ////var err = process.ErrorOutput; var std = process.StandardOutput; string currentBlockName = string.Empty; foreach (var line in std.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)) { if (line.StartsWith("[/") && line.EndsWith("]")) { // end of a block } else if (line.StartsWith("[") && line.EndsWith("]")) { // start of a block currentBlockName = line.Trim('[', ']'); } else { // key=value var key = currentBlockName + " " + line.Substring(0, line.IndexOf('=')); var value = line.Substring(line.IndexOf('=') + 1); result.RawData.Add(key.Trim(), value.Trim()); } } // parse info info class var keyDuration = "FORMAT duration"; var keyBitRate = "FORMAT bit_rate"; var keySampleRate = "STREAM sample_rate"; var keyChannels = "STREAM channels"; var keyBitsPerSample = "STREAM bits_per_sample"; var keyBitsPerRawSample = "STREAM bits_per_raw_sample"; if (result.RawData.ContainsKey(keyDuration)) { var stringDuration = result.RawData[keyDuration]; double?samples = this.ParseDoubleStringWithException(stringDuration.Trim(), keyDuration); result.Duration = TimeSpan.FromSeconds(samples.Value); } result.BitsPerSecond = GetBitRate(result.RawData); if (result.RawData.ContainsKey(keyBitRate)) { result.BitsPerSecond = this.ParseIntStringWithException(result.RawData[keyBitRate], "ffmpeg.bitrate", new string[] { NotApplicable }); } if (result.RawData.ContainsKey(keySampleRate)) { result.SampleRate = this.ParseIntStringWithException(result.RawData[keySampleRate], "ffmpeg.samplerate", new string[] { NotApplicable }); } if (result.RawData.ContainsKey(keyChannels)) { result.ChannelCount = this.ParseIntStringWithException(result.RawData[keyChannels], "ffmpeg.channels", new string[] { NotApplicable }); } if (result.RawData.ContainsKey(keyBitsPerSample)) { result.BitsPerSample = this.ParseIntStringWithException( result.RawData[keyBitsPerSample], "ffmpeg.bitspersample", new[] { NotApplicable }); if (result.BitsPerSample < 1) { result.BitsPerSample = null; } if (result.BitsPerSample == null) { result.BitsPerSample = this.ParseIntStringWithException( result.RawData[keyBitsPerRawSample], "ffmpeg.bitsperrawsample", new[] { NotApplicable }); } if (result.BitsPerSample < 1) { result.BitsPerSample = null; } } result.MediaType = this.GetMediaType(result.RawData, source.Extension); //FfmpegFormatToMediaType(result.RawData, source.Extension); return(result); }