diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fa3399d39..2680d6417 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,6 +35,39 @@ jobs: - name: Get Path to Tests run: echo "TEST_PATH=$(dotnet msbuild SIL.Core.Tests/ --getProperty:OutputPath -p:TargetFramework=${{ matrix.framework }} -p:Configuration=Release)" >> $GITHUB_ENV + # Several steps to set up FFmpeg and Scream and start Audio Service so that audio tests can run + - name: Install FFmpeg + uses: FedericoCarboni/setup-ffmpeg@v3 + id: setup-ffmpeg + with: + ffmpeg-version: release + - run: echo ffmpeg path ${{ steps.setup-ffmpeg.outputs.ffmpeg-path }} + + - name: Install Scream on Windows + shell: powershell + run: | + Invoke-WebRequest https://github.com/duncanthrax/scream/releases/download/4.0/Scream4.0.zip -OutFile Scream4.0.zip + Expand-Archive -Path Scream4.0.zip -DestinationPath Scream + openssl req -batch -verbose -x509 -newkey rsa -keyout ScreamCertificate.pvk -out ScreamCertificate.cer -nodes -extensions v3_req + openssl pkcs12 -export -nodes -in ScreamCertificate.cer -inkey ScreamCertificate.pvk -out ScreamCertificate.pfx -passout pass: + + - name: Setup MSVC Dev Cmd + uses: ilammy/msvc-dev-cmd@v1 + + - name: Sign and Install Scream Driver on Windows + if: matrix.os == 'windows-latest' + shell: powershell + run: | + signtool sign /v /fd SHA256 /f ScreamCertificate.pfx Scream\Install\driver\x64\Scream.cat + Import-Certificate -FilePath ScreamCertificate.cer -CertStoreLocation Cert:\LocalMachine\root + Import-Certificate -FilePath ScreamCertificate.cer -CertStoreLocation Cert:\LocalMachine\TrustedPublisher + Scream\Install\helpers\devcon-x64.exe install Scream\Install\driver\x64\Scream.inf *Scream + timeout-minutes: 5 + + - name: Start Windows Audio Service + run: net start audiosrv + shell: powershell + # there are cases where this will fail and we want to know about it # so we don't use continue-on-error, but we still want to publish the results - name: Test project diff --git a/CHANGELOG.md b/CHANGELOG.md index 50855f1f1..d48d36e7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - [SIL.Archiving] Added public property isValid to IMDIPackage. - [SIL.Archiving] Added public event InitializationFailed to IMDIArchivingDlgViewModel. - [SIL.Archiving] Added the following properties to ArchivingDlgViewModel as an alternative way to customize the initial summary displayed: GetOverriddenPreArchivingMessages, InitialFileGroupDisplayMessageType, OverrideGetFileGroupDisplayMessage +- [SIL.Media] Added FFmpegRunner.FfmpegMinimumVersion property. ### Changed @@ -78,6 +79,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - [SIL.Archiving] Changed the name of the third parameter in ArchivingDlgViewModel.AddFileGroup from progressMessage to addingToArchiveProgressMessage. - [SIL.Windows.Forms.Archiving] Changed Cancel Button to say Close instead in IMDIArchivingDlg. - [SIL.Core.Desktop] Renamed GetFromRegistryProgramThatOpensFileType to GetDefaultProgramForFileType. +- [SIL.Media] Made FFmpegRunner able to use version of FFmpeg found on the path. +- [SIL.Media] Upgraded irrKlang to v. 1.6. ### Fixed - [SIL.Archiving] Fixed typo in RampArchivingDlgViewModel for Ethnomusicology performance collection. diff --git a/Palaso.sln.DotSettings b/Palaso.sln.DotSettings index 6bfe95d19..e644a8af1 100644 --- a/Palaso.sln.DotSettings +++ b/Palaso.sln.DotSettings @@ -23,6 +23,7 @@ True True True + True True True True @@ -34,6 +35,7 @@ True True True + True True True True @@ -118,6 +120,7 @@ True True True + True True True True diff --git a/SIL.Media.Tests/AudioFactoryTests.cs b/SIL.Media.Tests/AudioFactoryTests.cs index b3ba3ee9a..fb2665c82 100644 --- a/SIL.Media.Tests/AudioFactoryTests.cs +++ b/SIL.Media.Tests/AudioFactoryTests.cs @@ -6,7 +6,6 @@ namespace SIL.Media.Tests { // These will not work if a speaker is not available. [TestFixture] - [Category("SkipOnTeamCity")] [Category("AudioTests")] public class AudioFactoryTests { diff --git a/SIL.Media.Tests/AudioPlayerTests.cs b/SIL.Media.Tests/AudioPlayerTests.cs index 87a40a40c..13e33ec96 100644 --- a/SIL.Media.Tests/AudioPlayerTests.cs +++ b/SIL.Media.Tests/AudioPlayerTests.cs @@ -10,13 +10,12 @@ namespace SIL.Media.Tests { /// - /// All these tests are skipped on TeamCity (even if you remove this category) because SIL.Media.Tests compiles to an exe, - /// and the project that builds libpalaso on TeamCity (build/Palaso.proj, task Test) invokes RunNUnitTC which - /// selects the test assemblies using Include="$(RootDir)/output/$(Configuration)/*.Tests.dll" which excludes exes. - /// I have not tried to verify that all of these tests would actually have problems on TeamCity, but it seemed - /// helpful to document in the usual way that they are not, in fact, run there. + /// Tests for the AudioPlayer class. /// - [Category("SkipOnTeamCity")] + /// This fixture used to be skipped during the CI build, perhaps because of the test + /// that actually tries to do playback (since that would require an audio output device). + /// However, that test is now ignored and the only non-ignored test works fine without an + /// actual playback device. [TestFixture] public class AudioPlayerTests { @@ -33,7 +32,7 @@ public void LoadFile_ThenDispose_FileCanBeDeleted() } /// - /// This test shows what caused hearthis to abandon the naudio; previous to a change to this class in 2/2012, it is believed that all was ok. + /// This test shows what caused HearThis to abandon NAudio; previous to a change to this class in 2/2012, it is believed that all was ok. /// [Test, Ignore("Known to Fail (hangs forever")] public void PlayFile_ThenDispose_FileCanBeDeleted() diff --git a/SIL.Media.Tests/AudioRecorderTests.cs b/SIL.Media.Tests/AudioRecorderTests.cs index b6cf4f997..a783c0e94 100644 --- a/SIL.Media.Tests/AudioRecorderTests.cs +++ b/SIL.Media.Tests/AudioRecorderTests.cs @@ -11,7 +11,6 @@ namespace SIL.Media.Tests // Some of these tests require a speaker. Others require a microphone. // None of them will work if neither a speaker nor a microphone is available. [TestFixture] - [Category("SkipOnTeamCity")] [Category("AudioTests")] public class AudioRecorderTests { diff --git a/SIL.Media.Tests/AudioSessionTests.cs b/SIL.Media.Tests/AudioSessionTests.cs index ea557bbff..450301b43 100644 --- a/SIL.Media.Tests/AudioSessionTests.cs +++ b/SIL.Media.Tests/AudioSessionTests.cs @@ -15,7 +15,6 @@ namespace SIL.Media.Tests // Some of these tests require a speaker. Others require a microphone. // None of them will work if neither a speaker nor a microphone is available. [TestFixture] - [NUnit.Framework.Category("SkipOnTeamCity")] [NUnit.Framework.Category("AudioTests")] public class AudioSessionTests { @@ -265,10 +264,7 @@ public RecordingSession(int millisecondsToRecordBeforeStopping) _recorder.StopRecordingAndSaveAsWav(); } - public ISimpleAudioSession Recorder - { - get { return _recorder; } - } + public ISimpleAudioSession Recorder => _recorder; public void Dispose() { @@ -451,8 +447,8 @@ public void Record_LongRecording() { using (var folder = new TemporaryFolder("Record_LongRecording")) { - string fpath = Path.Combine(folder.Path, "long.wav"); - using (var x = AudioFactory.CreateAudioSession(fpath)) + string fPath = Path.Combine(folder.Path, "long.wav"); + using (var x = AudioFactory.CreateAudioSession(fPath)) { SystemSounds.Beep.Play(); Assert.DoesNotThrow(() => x.StartRecording()); diff --git a/SIL.Media.Tests/FFmpegRunnerTests.cs b/SIL.Media.Tests/FFmpegRunnerTests.cs index 1430daf64..dcae847aa 100644 --- a/SIL.Media.Tests/FFmpegRunnerTests.cs +++ b/SIL.Media.Tests/FFmpegRunnerTests.cs @@ -1,4 +1,6 @@ +using System; using System.IO; +using FFMpegCore; using NUnit.Framework; using SIL.IO; using SIL.Media.Tests.Properties; @@ -6,14 +8,7 @@ namespace SIL.Media.Tests { - /// - /// All these tests are skipped on TeamCity (even if you remove this category) because SIL.Media.Tests compiles to an exe, - /// and the project that builds libpalaso on TeamCity (build/Palaso.proj, task Test) invokes RunNUnitTC which - /// selects the test assemblies using Include="$(RootDir)/output/$(Configuration)/*.Tests.dll" which excludes exes. - /// I have not tried to verify that all of these tests would actually have problems on TeamCity, but it seemed - /// helpful to document in the usual way that they are not, in fact, run there. - /// - [Category("SkipOnTeamCity")] + [Category("RequiresFfmpeg")] [TestFixture] public class FFmpegRunnerTests { @@ -21,18 +16,52 @@ public class FFmpegRunnerTests public void CheckRequirements() { if (!FFmpegRunner.HaveNecessaryComponents) - Assert.Ignore("These tests require ffmpeg to be installed."); + { + if (Environment.GetEnvironmentVariable("CI") == null) + Assert.Ignore("These tests require ffmpeg to be installed."); + else + Assert.Fail("On CI build using GHA, FFMpeg should have been installed before running tests."); + } } [Test] - [Category("RequiresFfmpeg")] - public void HaveNecessaryComponents_ReturnsTrue() + public void HaveNecessaryComponents_NoExplicitMinVersion_ReturnsTrue() + { + Assert.IsTrue(FFmpegRunner.HaveNecessaryComponents); + } + + [TestCase(5, 1)] + [TestCase(4, 9)] + public void HaveNecessaryComponents_TwoDigitMinVersion_ReturnsTrue(int major, int minor) { + FFmpegRunner.FfmpegMinimumVersion = new Version(major, minor); Assert.IsTrue(FFmpegRunner.HaveNecessaryComponents); } + [TestCase(5, 1, 1)] + [TestCase(5, 0, 0)] + public void HaveNecessaryComponents_ThreeDigitMinVersion_ReturnsTrue(int major, int minor, int build) + { + FFmpegRunner.FfmpegMinimumVersion = new Version(major, minor, build); + Assert.IsTrue(FFmpegRunner.HaveNecessaryComponents); + } + + [TestCase(5, 1, 1, 0)] + [TestCase(5, 0, 0, 9)] + public void HaveNecessaryComponents_FourDigitMinVersion_ReturnsTrue(int major, int minor, int build, int revision) + { + FFmpegRunner.FfmpegMinimumVersion = new Version(major, minor, build, revision); + Assert.IsTrue(FFmpegRunner.HaveNecessaryComponents); + } + + [Test] + public void HaveNecessaryComponents_ReallyHighVersionThatDoesNotExist_ReturnsFalse() + { + FFmpegRunner.FfmpegMinimumVersion = new Version(int.MaxValue, int.MaxValue); + Assert.IsFalse(FFmpegRunner.HaveNecessaryComponents); + } + [Test] - [Category("RequiresFfmpeg")] public void ExtractMp3Audio_CreatesFile() { using (var file = TempFile.FromResource(Resources.tiny, ".wmv")) @@ -44,7 +73,6 @@ public void ExtractMp3Audio_CreatesFile() } [Test] - [Category("RequiresFfmpeg")] public void ExtractOggAudio_CreatesFile() { using (var file = TempFile.FromResource(Resources.tiny, ".wmv")) @@ -56,7 +84,6 @@ public void ExtractOggAudio_CreatesFile() } [Test] - [Category("RequiresFfmpeg")] public void ChangeNumberOfAudioChannels_CreatesFile() { using (var file = TempFile.FromResource(Resources._2Channel, ".wav")) @@ -68,7 +95,6 @@ public void ChangeNumberOfAudioChannels_CreatesFile() } [Test] - [Category("RequiresFfmpeg")] public void MakeLowQualityCompressedAudio_CreatesFile() { using (var file = TempFile.FromResource(Resources.tiny, ".wmv")) @@ -79,12 +105,29 @@ public void MakeLowQualityCompressedAudio_CreatesFile() var outputPath = originalAudioPath.Replace("mp3", "low.mp3"); FFmpegRunner.MakeLowQualityCompressedAudio(originalAudioPath, outputPath, new ConsoleProgress()); Assert.IsTrue(File.Exists(outputPath)); - System.Diagnostics.Process.Start(outputPath); + + var mediaInfoOrig = FFProbe.Analyse(file.Path); + var mediaInfo = FFProbe.Analyse(outputPath); + + // Validate resolution and bit rate + Assert.That(mediaInfo.PrimaryVideoStream, Is.Null); + Assert.That(mediaInfo.PrimaryAudioStream, Is.Not.Null); + Assert.That(mediaInfo.AudioStreams.Count, Is.EqualTo(1)); + Assert.That(mediaInfo.PrimaryAudioStream.Channels, Is.EqualTo(1)); + Assert.That(mediaInfo.Format.BitRate, Is.LessThan(mediaInfoOrig.Format.BitRate)); + Assert.That(mediaInfo.PrimaryAudioStream.SampleRateHz, Is.EqualTo(8000)); + try + { + RobustFile.Delete(outputPath); + } + catch (Exception e) + { + Console.WriteLine(e); + } } } [Test] - [Category("RequiresFfmpeg")] public void MakeLowQualitySmallVideo_CreatesFile() { using (var file = TempFile.FromResource(Resources.tiny, ".wmv")) @@ -92,7 +135,28 @@ public void MakeLowQualitySmallVideo_CreatesFile() var outputPath = file.Path.Replace("wmv", "low.wmv"); FFmpegRunner.MakeLowQualitySmallVideo(file.Path, outputPath, 0, new ConsoleProgress()); Assert.IsTrue(File.Exists(outputPath)); - System.Diagnostics.Process.Start(outputPath); + + var mediaInfoOrig = FFProbe.Analyse(file.Path); + var mediaInfo = FFProbe.Analyse(outputPath); + + // Validate resolution and bit rate + Assert.That(mediaInfo.PrimaryVideoStream, Is.Not.Null); + Assert.That(mediaInfo.VideoStreams.Count, Is.EqualTo(1)); + Assert.That(mediaInfo.PrimaryAudioStream, Is.Not.Null); + Assert.That(mediaInfo.AudioStreams.Count, Is.EqualTo(1)); + Assert.That(mediaInfo.PrimaryVideoStream.Width, Is.EqualTo(160)); + Assert.That(mediaInfo.PrimaryVideoStream.Height, Is.EqualTo(120)); + Assert.That(mediaInfo.Format.BitRate, Is.LessThan(mediaInfoOrig.Format.BitRate)); + try + { + // When running the by-hand test, the default media player might leave this + // locked, so this cleanup will fail. + RobustFile.Delete(outputPath); + } + catch (Exception e) + { + Console.WriteLine(e); + } } } } diff --git a/SIL.Media.Tests/MediaInfoTests.cs b/SIL.Media.Tests/MediaInfoTests.cs index a9f8a608b..0b4710e38 100644 --- a/SIL.Media.Tests/MediaInfoTests.cs +++ b/SIL.Media.Tests/MediaInfoTests.cs @@ -1,17 +1,11 @@ +using System; using NUnit.Framework; using SIL.IO; using SIL.Media.Tests.Properties; namespace SIL.Media.Tests { - /// - /// All these tests are skipped on TeamCity (even if you remove this category) because SIL.Media.Tests compiles to an exe, - /// and the project that builds libpalaso on TeamCity (build/Palaso.proj, task Test) invokes RunNUnitTC which - /// selects the test assemblies using Include="$(RootDir)/output/$(Configuration)/*.Tests.dll" which excludes exes. - /// I have not tried to verify that all of these tests would actually have problems on TeamCity, but it seemed - /// helpful to document in the usual way that they are not, in fact, run there. - /// - [Category("SkipOnTeamCity")] + [Category("RequiresFFprobe")] [TestFixture] public class MediaInfoTests { @@ -19,18 +13,21 @@ public class MediaInfoTests public void CheckRequirements() { if (!MediaInfo.HaveNecessaryComponents) - Assert.Ignore(MediaInfo.MissingComponentMessage); + { + if (Environment.GetEnvironmentVariable("CI") == null) + Assert.Ignore(MediaInfo.MissingComponentMessage); + else + Assert.Fail("On CI build using GHA, FFMpeg should have been installed before running tests."); + } } [Test] - [Category("RequiresFFprobe")] public void HaveNecessaryComponents_ReturnsTrue() { Assert.IsTrue(MediaInfo.HaveNecessaryComponents); } [Test] - [Category("RequiresFFprobe")] public void VideoInfo_Duration_Correct() { using (var file = TempFile.FromResource(Resources.tiny, ".wmv")) @@ -43,7 +40,6 @@ public void VideoInfo_Duration_Correct() } [Test] - [Category("RequiresFFprobe")] public void VideoInfo_Encoding_Correct() { using (var file = TempFile.FromResource(Resources.tiny, ".wmv")) @@ -54,7 +50,6 @@ public void VideoInfo_Encoding_Correct() } [Test] - [Category("RequiresFFprobe")] public void VideoInfo_Resolution_Correct() { using (var file = TempFile.FromResource(Resources.tiny, ".wmv")) @@ -65,7 +60,6 @@ public void VideoInfo_Resolution_Correct() } [Test] - [Category("RequiresFFprobe")] public void VideoInfo_FramesPerSecond_Correct() { using (var file = TempFile.FromResource(Resources.tiny, ".wmv")) @@ -77,7 +71,6 @@ public void VideoInfo_FramesPerSecond_Correct() } [Test] - [Category("RequiresFFprobe")] public void AudioInfo_Duration_Correct() { using (var file = TempFile.FromResource(Resources.finished, ".wav")) @@ -89,7 +82,6 @@ public void AudioInfo_Duration_Correct() [Test] - [Category("RequiresFFprobe")] public void AudioInfo_SampleFrequency_Correct() { using (var file = TempFile.FromResource(Resources.finished, ".wav")) @@ -100,7 +92,6 @@ public void AudioInfo_SampleFrequency_Correct() } [Test] - [Category("RequiresFFprobe")] public void AudioInfo_Channels_Correct() { using (var file = TempFile.FromResource(Resources.finished, ".wav")) @@ -111,7 +102,6 @@ public void AudioInfo_Channels_Correct() } [Test] - [Category("RequiresFFprobe")] public void AudioInfo_BitDepth_Correct() { using (var file = TempFile.FromResource(Resources.finished, ".wav")) @@ -122,7 +112,6 @@ public void AudioInfo_BitDepth_Correct() } [Test] - [Category("RequiresFFprobe")] public void AudioInfo_H4N24BitStereoBitDepth_Correct() { using (var file = TempFile.FromResource(Resources._24bitH4NSample, ".wav")) @@ -133,14 +122,12 @@ public void AudioInfo_H4N24BitStereoBitDepth_Correct() } [Test] - [Category("RequiresFFprobe")] public void GetMediaInfo_AudioFile_VideoInfoAndImageInfoAreNull() { using (var file = TempFile.FromResource(Resources.finished,".wav")) { var info =MediaInfo.GetInfo(file.Path); Assert.IsNull(info.Video); - //Assert.IsNull(info.Image); } } diff --git a/SIL.Media/FFmpegRunner.cs b/SIL.Media/FFmpegRunner.cs index f29b25c38..5db9de7a7 100644 --- a/SIL.Media/FFmpegRunner.cs +++ b/SIL.Media/FFmpegRunner.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Text.RegularExpressions; using JetBrains.Annotations; using SIL.CommandLineProcessing; using SIL.IO; @@ -27,6 +28,16 @@ public class FFmpegRunner /// If your app knows where FFmpeg lives, you can tell us before making any calls. /// public static string FFmpegLocation; + /// + /// If your app has a known minimum version of FFMpeg that it will work with, you can set + /// this to prevent this library from attempting to use a version that is not going to meet + /// your requirements. This will be ignored if you set FFmpegLocation, if the ffmpeg + /// installation is based on a Linux package dependency or if FFmpeg is colocated with the + /// applications, since it seems safe to assume that you are not specifying or installing a + /// version that does not satisfy your needs. + /// + public static Version FfmpegMinimumVersion; + private static bool? _ffmpegOnPath; /// /// Find the path to FFmpeg, and remember it (some apps (like SayMore) call FFmpeg a lot) @@ -67,7 +78,11 @@ private static string LocateFFmpeg() var fromChoco = MediaInfo.GetFFmpegFolderFromChocoInstall(kFFmpegExe); if (fromChoco != null) - return Path.Combine(fromChoco, kFFmpegExe); + { + var pathToFFmpeg = Path.Combine(fromChoco, kFFmpegExe); + if (MeetsMinimumVersionRequirement(pathToFFmpeg)) + return pathToFFmpeg; + } var progFileDirs = new List { Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), @@ -93,10 +108,45 @@ private static string LocateFFmpeg() foreach (var path in progFileDirs) { var exePath = (Path.Combine(path, "FFmpeg for Audacity", kFFmpegExe)); - if (File.Exists(exePath)) + if (File.Exists(exePath) && MeetsMinimumVersionRequirement(exePath)) return exePath; } - return null; + + string ffmpeg = null; + + // Locate may be called multiple times, we don't want to run this command every time. + if (_ffmpegOnPath == null) + { + ffmpeg = Path.GetFileNameWithoutExtension(kFFmpegExe); + // Try to just run ffmpeg from the path, if it works then we can use that directly. + _ffmpegOnPath = MeetsMinimumVersionRequirement(ffmpeg); + if (!_ffmpegOnPath.Value) + ffmpeg = null; + } + return ffmpeg; + } + + private static bool MeetsMinimumVersionRequirement(string ffmpeg) + { + try + { + var version = new Regex(@"ffmpeg version (?\d+\.\d+(\.\d+)?)"); + var results = CommandLineRunner.Run(ffmpeg, "-version", ".", 5, new NullProgress()); + var match = version.Match(results.StandardOutput); + if (!match.Success) + return false; + if (FfmpegMinimumVersion == null) + return true; + var actualVersion = Version.Parse(match.Groups["version"].Value); + actualVersion = new Version(actualVersion.Major, actualVersion.Minor, + actualVersion.Build >= 0 ? actualVersion.Build : 0, + actualVersion.Revision >= 0 ? actualVersion.Revision : 0); + return actualVersion >= FfmpegMinimumVersion; + } + catch + { + return false; + } } private static string GetPathToBundledFFmpeg() @@ -168,7 +218,7 @@ public static ExecutionResult ExtractMp3Audio(string inputPath, string outputPat var arguments = $"-i \"{inputPath}\" -vn {mp3LameCodecArg} -ac {channels} \"{outputPath}\""; var result = RunFFmpeg(arguments, progress); - //hide a meaningless error produced by some versions of liblame + // Hide a meaningless error produced by some versions of liblame if (result.StandardError.Contains("lame: output buffer too small") && File.Exists(outputPath)) { @@ -203,7 +253,7 @@ public static ExecutionResult ExtractOggAudio(string inputPath, string outputPat var arguments = $"-i \"{inputPath}\" -vn -acodec vorbis -ac {channels} \"{outputPath}\""; var result = RunFFmpeg(arguments, progress); - //hide a meaningless error produced by some versions of liblame + // Hide a meaningless error produced by some versions of liblame if (result.StandardError.Contains("lame: output buffer too small") && File.Exists(outputPath)) { @@ -294,7 +344,7 @@ private static ExecutionResult ExtractAudio(string inputPath, string outputPath, var result = RunFFmpeg(arguments, progress); - //hide a meaningless error produced by some versions of liblame + // Hide a meaningless error produced by some versions of liblame if (result.StandardError.Contains("lame: output buffer too small") && File.Exists(outputPath)) { @@ -328,7 +378,7 @@ public static ExecutionResult ChangeNumberOfAudioChannels(string inputPath, var result = RunFFmpeg(arguments, progress); - //hide a meaningless error produced by some versions of liblame + // Hide a meaningless error produced by some versions of liblame if (result.StandardError.Contains("lame: output buffer too small") && File.Exists(outputPath)) { var doctoredResult = new ExecutionResult @@ -361,7 +411,7 @@ public static ExecutionResult MakeLowQualityCompressedAudio(string inputPath, st var result = RunFFmpeg(arguments, progress); - //hide a meaningless error produced by some versions of liblame + // Hide a meaningless error produced by some versions of liblame if (result.StandardError.Contains("lame: output buffer too small") && File.Exists(outputPath)) { @@ -404,7 +454,7 @@ public static ExecutionResult MakeLowQualitySmallVideo(string inputPath, string var result = RunFFmpeg(arguments, progress); - //hide a meaningless error produced by some versions of liblame + // Hide a meaningless error produced by some versions of liblame if (result.StandardError.Contains("lame: output buffer too small") && File.Exists(outputPath)) { diff --git a/SIL.Media/Naudio/AudioPlayer.cs b/SIL.Media/Naudio/AudioPlayer.cs index 46cdf41c1..c6d325b75 100644 --- a/SIL.Media/Naudio/AudioPlayer.cs +++ b/SIL.Media/Naudio/AudioPlayer.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Timers; +using JetBrains.Annotations; using NAudio.Wave; namespace SIL.Media.Naudio @@ -26,6 +27,7 @@ public AudioPlayer() }; } + [PublicAPI] public void LoadStream(WaveStream stream) { InternalLoad(stream as TrimWaveStream ?? new TrimWaveStream(stream)); @@ -82,22 +84,19 @@ public void Stop() public TimeSpan StartPosition { - get { return _inStream.StartPosition; } - set { _inStream.StartPosition = value; } + get => _inStream.StartPosition; + set => _inStream.StartPosition = value; } public TimeSpan EndPosition { - get { return _inStream.EndPosition; } - set { _inStream.EndPosition = value; } + get => _inStream.EndPosition; + set => _inStream.EndPosition = value; } public TimeSpan CurrentPosition { get; set; } - public PlaybackState PlaybackState - { - get { return _waveOut == null ? PlaybackState.Stopped : _waveOut.PlaybackState; } - } + public PlaybackState PlaybackState => _waveOut?.PlaybackState ?? PlaybackState.Stopped; public void Dispose() { diff --git a/SIL.Media/WindowsAudioSession.cs b/SIL.Media/WindowsAudioSession.cs index b764f0238..50b390940 100644 --- a/SIL.Media/WindowsAudioSession.cs +++ b/SIL.Media/WindowsAudioSession.cs @@ -17,7 +17,7 @@ namespace SIL.Media internal class WindowsAudioSession : ISimpleAudioSession, ISimpleAudioWithEvents { private readonly IrrKlang.IAudioRecorder _recorder; - private readonly ISoundEngine _engine = new ISoundEngine(); + private readonly ISoundEngine _engine = new ISoundEngine(SoundOutputDriver.AutoDetect, SoundEngineOptionFlag.PrintDebugInfoIntoDebugger); private bool _thinkWeAreRecording; private DateTime _startRecordingTime; private DateTime _stopRecordingTime; diff --git a/SIL.Media/lib/win-x64/ikpFlac.dll b/SIL.Media/lib/win-x64/ikpFlac.dll index abfed4f1c..2271a3fae 100644 Binary files a/SIL.Media/lib/win-x64/ikpFlac.dll and b/SIL.Media/lib/win-x64/ikpFlac.dll differ diff --git a/SIL.Media/lib/win-x64/ikpMP3.dll b/SIL.Media/lib/win-x64/ikpMP3.dll index 2f46856ec..b9134000c 100644 Binary files a/SIL.Media/lib/win-x64/ikpMP3.dll and b/SIL.Media/lib/win-x64/ikpMP3.dll differ diff --git a/SIL.Media/lib/win-x64/irrKlang.NET4.dll b/SIL.Media/lib/win-x64/irrKlang.NET4.dll index e32b982d3..ef53ee589 100644 Binary files a/SIL.Media/lib/win-x64/irrKlang.NET4.dll and b/SIL.Media/lib/win-x64/irrKlang.NET4.dll differ diff --git a/SIL.Media/lib/win-x86/ikpFlac.dll b/SIL.Media/lib/win-x86/ikpFlac.dll index 335e8b06d..b3cdcc550 100644 Binary files a/SIL.Media/lib/win-x86/ikpFlac.dll and b/SIL.Media/lib/win-x86/ikpFlac.dll differ diff --git a/SIL.Media/lib/win-x86/ikpMP3.dll b/SIL.Media/lib/win-x86/ikpMP3.dll index 974b8929b..b2e3ec342 100644 Binary files a/SIL.Media/lib/win-x86/ikpMP3.dll and b/SIL.Media/lib/win-x86/ikpMP3.dll differ diff --git a/SIL.Media/lib/win-x86/irrKlang.NET4.dll b/SIL.Media/lib/win-x86/irrKlang.NET4.dll index ca182caef..97131be4d 100644 Binary files a/SIL.Media/lib/win-x86/irrKlang.NET4.dll and b/SIL.Media/lib/win-x86/irrKlang.NET4.dll differ