Browse Source

chore; status bar; codec info

Coder 7 months ago
parent
commit
b4deb3a117

+ 4 - 2
BMP.sln

@@ -26,7 +26,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03 PoC", "03 PoC", "{56AEFF
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "04 UI", "04 UI", "{EABF36B3-3577-4E7E-843D-388FE770C312}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bmp.Poc.DSD4I2S", "Bmp.Poc.DSD4I2S\Bmp.Poc.DSD4I2S.csproj", "{C0EAD193-71A0-4572-B53B-8C44CA052709}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bmp.Poc.DSD4I2S", "Bmp.Poc.DSD4I2S\Bmp.Poc.DSD4I2S.csproj", "{C0EAD193-71A0-4572-B53B-8C44CA052709}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "00 Common", "00 Common", "{84D56EB3-409B-46EE-B9F7-03BFA719A817}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -67,7 +69,7 @@ Global
 		{B3AD1B0C-F2C6-4425-B172-933CA3E63907} = {C63E0265-E45E-43F0-B46D-9E6C0EA1EFAC}
 		{114D7388-AA14-41C7-9DF9-8759F48FE137} = {EABF36B3-3577-4E7E-843D-388FE770C312}
 		{EFA386DE-D0E8-4551-8B8D-71E32A26AE0B} = {3EAF721A-8FAA-4297-B6FB-7CF9F4E33041}
-		{17A4C7DF-BF0A-4F68-B5A9-5C5F4FB1F63F} = {3EAF721A-8FAA-4297-B6FB-7CF9F4E33041}
+		{17A4C7DF-BF0A-4F68-B5A9-5C5F4FB1F63F} = {84D56EB3-409B-46EE-B9F7-03BFA719A817}
 		{C0EAD193-71A0-4572-B53B-8C44CA052709} = {56AEFFAF-D446-4098-B561-361DB5AD6600}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution

+ 6 - 4
Bmp.Core.Lite/Playback/Inputs/DffAudioStream.cs

@@ -6,7 +6,7 @@ using WaveFormat = NAudio.Wave.WaveFormat;
 
 namespace Bmp.Core.Lite.Playback.Inputs;
 
-public class DffAudioStream : DsdAudioStream
+public class DffAudioStream : DsdAudioStream,IHaveDecoderInfo
 {
     private const string ID_FRM8 = "FRM8";
     private const string ID_DSD = "DSD ";
@@ -22,6 +22,9 @@ public class DffAudioStream : DsdAudioStream
 
     private const int FILE_VERSION = 0x01050000;
 
+    string IHaveDecoderInfo.DecoderName => "DFF";
+    string? IHaveDecoderInfo.FileFormat => "V1.5";
+
     public static bool IsDffFile(Stream stream)
     {
         if (stream.Length > 4)
@@ -46,7 +49,7 @@ public class DffAudioStream : DsdAudioStream
         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
         public string Id;
 
-        private long chunkDataSize;
+        private readonly long chunkDataSize;
         public long DataSize => chunkDataSize.NetworkOrderToHostOrder();
     }
 
@@ -61,8 +64,7 @@ public class DffAudioStream : DsdAudioStream
     private readonly long _dataOffset;
     private readonly long _dataSize;
 
-    private long _position;
-
+ 
     public DffAudioStream(string urlOrPath) : this(InputSourceProviderLite.ReadContentAsSeekableStream(urlOrPath), true)
     {
     }

+ 6 - 2
Bmp.Core.Lite/Playback/Inputs/DsfAudioStream.cs

@@ -3,12 +3,15 @@ using NAudio.Wave;
 
 namespace Bmp.Core.Lite.Playback.Inputs;
 
-public class DsfAudioStream : DsdAudioStream
+public class DsfAudioStream : DsdAudioStream,IHaveDecoderInfo
 {
     public const string SIGNATURE_DSD = "DSD ";
     private const string SIGNATURE_FMT = "fmt ";
     private const string SIGNATURE_DATA = "data";
 
+    string IHaveDecoderInfo.DecoderName => "DSF";
+    string? IHaveDecoderInfo.FileFormat => $"V{_formatVersion}";
+
     private readonly Stream _underlyingStream;
     private readonly bool _disposeStream;
 
@@ -27,6 +30,7 @@ public class DsfAudioStream : DsdAudioStream
     private readonly byte[] _clusterBuf;
     private int _clusterBufSize;
     private int _clusterPtrByChannel;
+    private readonly int _formatVersion;
 
     public static bool IsDsfFile(Stream stream)
     {
@@ -66,7 +70,7 @@ public class DsfAudioStream : DsdAudioStream
         if (SIGNATURE_FMT != fmtSignature) throw new InvalidDataException("invalid FMT signature");
         var fmtSizeOfThisChunk = reader.ReadInt64();
         if (fmtSizeOfThisChunk != 52) throw new NotSupportedException("not supported fmt chunk size");
-        var formatVersion = reader.ReadInt32();
+        _formatVersion = reader.ReadInt32();
         var formatId = reader.ReadInt32();
         if (formatId != 0) throw new NotSupportedException("only DSD raw supported");
 

+ 7 - 0
Bmp.Core.Lite/Playback/Inputs/IHaveDecoderInfo.cs

@@ -0,0 +1,7 @@
+namespace Bmp.Core.Lite.Playback.Inputs;
+
+public interface IHaveDecoderInfo
+{
+    public string DecoderName { get; }
+    public string? FileFormat { get; }
+}

+ 7 - 3
Bmp.Core.Lite/Playback/MiddleWare/VisualizeDataMiddleWare.cs

@@ -12,9 +12,9 @@ public class VisualizeDataMiddleWare : IWaveProvider, IHaveBitPerRawSample
         DataTransferred?.Invoke(this, e);
     }
 
-    private readonly IWaveProvider _provider;
+    private readonly WaveStream _provider;
 
-    public VisualizeDataMiddleWare(IWaveProvider provider) => _provider = provider;
+    public VisualizeDataMiddleWare(WaveStream provider) => _provider = provider;
 
     public int? BitPerRawSample => _provider is IHaveBitPerRawSample bpr ? bpr.BitPerRawSample : null;
     public WaveFormat WaveFormat => _provider.WaveFormat;
@@ -27,8 +27,12 @@ public class VisualizeDataMiddleWare : IWaveProvider, IHaveBitPerRawSample
             OnDataTransferred(new VisualizeDataEventArgs(WaveFormat, new ReadOnlyMemory<byte>(buffer, 0, read)));
             return read;
         }
-        catch (Exception e)
+        catch //(Exception e)
         {
+            if (_provider.Position < _provider.Length)
+            {
+                //TODO: 通知机制 流读取中断
+            }
             return 0;
         }
     }

+ 13 - 17
Bmp.Core.Lite/Playback/Outputs/NAudioASIO/ASIODriverExt.cs

@@ -129,18 +129,12 @@ public class AsioDriverExt
     /// <summary>
     /// Starts playing the buffers.
     /// </summary>
-    public void Start()
-    {
-        _driver.Start();
-    }
+    public void Start() => _driver.Start();
 
     /// <summary>
     /// Stops playing the buffers.
     /// </summary>
-    public void Stop()
-    {
-        _driver.Stop();
-    }
+    public void Stop() => _driver.Stop();
 
     /// <summary>
     /// Shows the control panel.
@@ -173,10 +167,7 @@ public class AsioDriverExt
     /// <returns>
     /// 	<c>true</c> if [is sample rate supported]; otherwise, <c>false</c>.
     /// </returns>
-    public bool IsSampleRateSupported(double sampleRate)
-    {
-        return _driver.CanSampleRate(sampleRate);
-    }
+    public bool IsSampleRateSupported(double sampleRate) => _driver.CanSampleRate(sampleRate);
 
     /// <summary>
     /// Sets the sample rate.
@@ -193,17 +184,22 @@ public class AsioDriverExt
             Init();
         }
 
+        var nowRate = _driver.GetSampleRate();
+        
         // ReSharper disable once CompareOfFloatsByEqualityOperator
-        var flag = _driver.GetSampleRate() == sampleRate;
+        var flag = nowRate == sampleRate;
+        
+        if(!flag) _driver.SetSampleRate(sampleRate); // Error ASE_NoClock ?
+
+        // ReSharper disable once CompareOfFloatsByEqualityOperator
+        flag = nowRate == sampleRate;
+
         // Update Capabilities
         if (flag) BuildCapabilities();
         else throw new NotSupportedException("Sample rate not expected");
     }
 
-    public double GetSampleRate()
-    {
-        return _driver.GetSampleRate();
-    }
+    public double GetSampleRate() => _driver.GetSampleRate();
 
     /// <summary>
     /// Gets or sets the fill buffer callback.

+ 1 - 4
Bmp.Core.Lite/Playback/Outputs/NAudioASIO/AsioOut.cs

@@ -135,10 +135,7 @@ public class AsioOut : IWavePlayer
     /// Gets the names of the installed ASIO Driver.
     /// </summary>
     /// <returns>an array of driver names</returns>
-    public static string[] GetDriverNames()
-    {
-        return AsioDriver.GetAsioDriverNames();
-    }
+    public static string[] GetDriverNames() => AsioDriver.GetAsioDriverNames();
 
     /// <summary>
     /// Determines whether ASIO is supported.

+ 1 - 1
Bmp.Core/Bmp.Core.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
 	<PropertyGroup>
-		<TargetFramework>net8.0-windows</TargetFramework>
+		<TargetFramework>net8.0</TargetFramework>
 		<ImplicitUsings>enable</ImplicitUsings>
 		<Nullable>enable</Nullable>
 		<AllowUnsafeBlocks>True</AllowUnsafeBlocks>

+ 35 - 35
Bmp.Core/FFMpeg/CsCorePorts/FFMpegWrap/FfmpegCalls.cs

@@ -71,21 +71,21 @@ internal class FfmpegCalls
         //    }
         //}
 
-        ffmpeg.av_register_all();
-        ffmpeg.avcodec_register_all();
+        FFMPEG.av_register_all();
+        FFMPEG.avcodec_register_all();
 
-        ffmpeg.avformat_network_init();
+        FFMPEG.avformat_network_init();
     }
 
     internal static unsafe AVOutputFormat[] GetOutputFormats()
     {
         var formats = new List<AVOutputFormat>();
 
-        var format = ffmpeg.av_oformat_next(null);
+        var format = FFMPEG.av_oformat_next(null);
         while (format != null)
         {
             formats.Add(*format);
-            format = ffmpeg.av_oformat_next(format);
+            format = FFMPEG.av_oformat_next(format);
         }
 
         return formats.ToArray();
@@ -95,11 +95,11 @@ internal class FfmpegCalls
     {
         var formats = new List<AVInputFormat>();
 
-        var format = ffmpeg.av_iformat_next(null);
+        var format = FFMPEG.av_iformat_next(null);
         while (format != null)
         {
             formats.Add(*format);
-            format = ffmpeg.av_iformat_next(format);
+            format = FFMPEG.av_iformat_next(format);
         }
 
         return formats.ToArray();
@@ -110,7 +110,7 @@ internal class FfmpegCalls
         var codecs = new List<AvCodecId>();
         uint i = 0;
         AvCodecId codecId;
-        while ((codecId = ffmpeg.av_codec_get_id(codecTag, i++)) != AvCodecId.None)
+        while ((codecId = FFMPEG.av_codec_get_id(codecTag, i++)) != AvCodecId.None)
         {
             codecs.Add(codecId);
         }
@@ -120,7 +120,7 @@ internal class FfmpegCalls
 
     internal static unsafe nint AvMalloc(int bufferSize)
     {
-        var buffer = ffmpeg.av_malloc((ulong)bufferSize);
+        var buffer = FFMPEG.av_malloc((ulong)bufferSize);
         var ptr = new nint(buffer);
         if (ptr == nint.Zero)
             throw new OutOfMemoryException("Could not allocate memory.");
@@ -130,7 +130,7 @@ internal class FfmpegCalls
 
     internal static unsafe void AvFree(nint buffer)
     {
-        ffmpeg.av_free((void*)buffer);
+        FFMPEG.av_free((void*)buffer);
     }
 
     internal static unsafe AVIOContext* AvioAllocContext(AvioBuffer buffer, bool writeable, nint userData,
@@ -138,7 +138,7 @@ internal class FfmpegCalls
     {
         var bufferPtr = (byte*)buffer.Buffer;
 
-        var avioContext = ffmpeg.avio_alloc_context(
+        var avioContext = FFMPEG.avio_alloc_context(
             bufferPtr,
             buffer.BufferSize,
             writeable ? 1 : 0,
@@ -154,7 +154,7 @@ internal class FfmpegCalls
 
     internal static unsafe AVFormatContext* AvformatAllocContext()
     {
-        var formatContext = ffmpeg.avformat_alloc_context();
+        var formatContext = FFMPEG.avformat_alloc_context();
         if (formatContext == null)
         {
             throw new FfmpegException("Could not allocate avformat-context.", "avformat_alloc_context");
@@ -166,30 +166,30 @@ internal class FfmpegCalls
     internal static unsafe void AvformatOpenInput(AVFormatContext** formatContext, AvioContext avioContext)
     {
         (*formatContext)->pb = (AVIOContext*)avioContext.ContextPtr;
-        var result = ffmpeg.avformat_open_input(formatContext, "DUMMY-FILENAME", null, null);
+        var result = FFMPEG.avformat_open_input(formatContext, "DUMMY-FILENAME", null, null);
         FfmpegException.Try(result, "avformat_open_input");
     }
 
     internal static unsafe void AvformatOpenInput(AVFormatContext** formatContext, string url)
     {
-        var result = ffmpeg.avformat_open_input(formatContext, url, null, null);
+        var result = FFMPEG.avformat_open_input(formatContext, url, null, null);
         FfmpegException.Try(result, "avformat_open_input");
     }
 
     internal static unsafe void AvformatCloseInput(AVFormatContext** formatContext)
     {
-        ffmpeg.avformat_close_input(formatContext);
+        FFMPEG.avformat_close_input(formatContext);
     }
 
     internal static unsafe void AvFormatFindStreamInfo(AVFormatContext* formatContext)
     {
-        var result = ffmpeg.avformat_find_stream_info(formatContext, null);
+        var result = FFMPEG.avformat_find_stream_info(formatContext, null);
         FfmpegException.Try(result, "avformat_find_stream_info");
     }
 
     internal static unsafe int AvFindBestStreamInfo(AVFormatContext* formatContext)
     {
-        var result = ffmpeg.av_find_best_stream(
+        var result = FFMPEG.av_find_best_stream(
             formatContext,
             AVMediaType.AVMEDIA_TYPE_AUDIO,
             -1, -1, null, 0);
@@ -200,7 +200,7 @@ internal class FfmpegCalls
 
     internal static unsafe AVCodec* AvCodecFindDecoder(AvCodecId codecId)
     {
-        var decoder = ffmpeg.avcodec_find_decoder(codecId);
+        var decoder = FFMPEG.avcodec_find_decoder(codecId);
         if (decoder == null)
         {
             throw new FfmpegException(
@@ -212,18 +212,18 @@ internal class FfmpegCalls
 
     internal static unsafe void AvCodecOpen(AVCodecContext* codecContext, AVCodec* codec)
     {
-        var result = ffmpeg.avcodec_open2(codecContext, codec, null);
+        var result = FFMPEG.avcodec_open2(codecContext, codec, null);
         FfmpegException.Try(result, "avcodec_open2");
     }
 
     internal static unsafe void AvCodecClose(AVCodecContext* codecContext)
     {
-        ffmpeg.avcodec_close(codecContext);
+        FFMPEG.avcodec_close(codecContext);
     }
 
     internal static unsafe AVFrame* AvFrameAlloc()
     {
-        var frame = ffmpeg.av_frame_alloc();
+        var frame = FFMPEG.av_frame_alloc();
         if (frame == null)
         {
             throw new FfmpegException("Could not allocate frame.", "av_frame_alloc");
@@ -234,29 +234,29 @@ internal class FfmpegCalls
 
     internal static unsafe void AvFrameFree(AVFrame* frame)
     {
-        ffmpeg.av_frame_free(&frame);
+        FFMPEG.av_frame_free(&frame);
     }
 
     internal static unsafe void InitPacket(AVPacket* packet)
     {
-        ffmpeg.av_init_packet(packet);
+        FFMPEG.av_init_packet(packet);
     }
 
     internal static unsafe void FreePacket(AVPacket* packet)
     {
-        ffmpeg.av_packet_unref(packet);
+        FFMPEG.av_packet_unref(packet);
     }
 
     internal static unsafe bool AvReadFrame(AvFormatContext formatContext, AVPacket* packet)
     {
-        var result = ffmpeg.av_read_frame((AVFormatContext*)formatContext.FormatPtr, packet);
+        var result = FFMPEG.av_read_frame((AVFormatContext*)formatContext.FormatPtr, packet);
         return result >= 0;
     }
 
     internal static unsafe bool AvCodecDecodeAudio4(AVCodecContext* codecContext, AVFrame* frame, AVPacket* packet, out int bytesConsumed)
     {
         int gotFrame;
-        var result = ffmpeg.avcodec_decode_audio4(codecContext, frame, &gotFrame, packet);
+        var result = FFMPEG.avcodec_decode_audio4(codecContext, frame, &gotFrame, packet);
         FfmpegException.Try(result, "avcodec_decode_audio4");
         bytesConsumed = result;
         return gotFrame != 0;
@@ -264,7 +264,7 @@ internal class FfmpegCalls
 
     internal static int AvGetBytesPerSample(AVSampleFormat sampleFormat)
     {
-        var dataSize = ffmpeg.av_get_bytes_per_sample(sampleFormat);
+        var dataSize = FFMPEG.av_get_bytes_per_sample(sampleFormat);
         if (dataSize <= 0)
         {
             throw new FfmpegException("Could not calculate data size.");
@@ -274,7 +274,7 @@ internal class FfmpegCalls
 
     internal static unsafe int AvSamplesGetBufferSize(AVFrame* frame)
     {
-        var result = ffmpeg.av_samples_get_buffer_size(null, frame->channels, frame->nb_samples,
+        var result = FFMPEG.av_samples_get_buffer_size(null, frame->channels, frame->nb_samples,
             (AVSampleFormat)frame->format, 1);
         FfmpegException.Try(result, "av_samples_get_buffer_size");
         return result;
@@ -282,7 +282,7 @@ internal class FfmpegCalls
 
     internal static unsafe void AvFormatSeekFile(AvFormatContext formatContext, double time)
     {
-        var result = ffmpeg.avformat_seek_file((AVFormatContext*)formatContext.FormatPtr,
+        var result = FFMPEG.avformat_seek_file((AVFormatContext*)formatContext.FormatPtr,
             formatContext.BestAudioStreamIndex, long.MinValue, (long)time, (long)time, 0);
 
         FfmpegException.Try(result, "avformat_seek_file");
@@ -291,7 +291,7 @@ internal class FfmpegCalls
     internal static unsafe string AvStrError(int errorCode)
     {
         var buffer = stackalloc byte[500];
-        var result = ffmpeg.av_strerror(errorCode, new nint(buffer), 500);
+        var result = FFMPEG.av_strerror(errorCode, new nint(buffer), 500);
         if (result < 0)
             return "No description available.";
         var errorMessage = Marshal.PtrToStringAnsi(new nint(buffer), 500).Trim('\0').Trim();
@@ -303,26 +303,26 @@ internal class FfmpegCalls
 
     internal static void SetLogLevel(LogLevel level)
     {
-        ffmpeg.av_log_set_level((int)level);
+        FFMPEG.av_log_set_level((int)level);
     }
 
     internal static LogLevel GetLogLevel()
     {
-        return (LogLevel)ffmpeg.av_log_get_level();
+        return (LogLevel)FFMPEG.av_log_get_level();
     }
 
     internal unsafe delegate void LogCallback(void* ptr, int level, byte* fmt, nint vl);
 
     internal static unsafe void SetLogCallback(LogCallback callback)
     {
-        ffmpeg.av_log_set_callback(Marshal.GetFunctionPointerForDelegate(callback));
+        FFMPEG.av_log_set_callback(Marshal.GetFunctionPointerForDelegate(callback));
     }
 
     internal static unsafe LogCallback GetDefaultLogCallback()
     {
         return (ptr, level, fmt, vl) =>
         {
-            ffmpeg.av_log_default_callback(ptr, level, Marshal.PtrToStringAnsi(new nint(fmt)), (sbyte*)vl);
+            FFMPEG.av_log_default_callback(ptr, level, Marshal.PtrToStringAnsi(new nint(fmt)), (sbyte*)vl);
         };
     }
 
@@ -335,7 +335,7 @@ internal class FfmpegCalls
         var buffer = stackalloc byte[bufferSize];
         fixed (int* ppp = &printPrefix)
         {
-            var result = ffmpeg.av_log_format_line2(avcl, level, fmt, (sbyte*)vl, (nint)buffer, bufferSize, ppp);
+            var result = FFMPEG.av_log_format_line2(avcl, level, fmt, (sbyte*)vl, (nint)buffer, bufferSize, ppp);
             if (result < 0)
             {
                 Debug.WriteLine("av_log_format_line2 failed with " + result.ToString("x8"));

+ 15 - 1
Bmp.Core/FFMpeg/CsCorePorts/FFMpegWrap/FfmpegDecoder.cs

@@ -1,4 +1,5 @@
 
+using System.Runtime.InteropServices;
 using Bmp.Core.FFMpeg.CsCoreExt;
 
 namespace Bmp.Core.FFMpeg.CsCorePorts.FFMpegWrap;
@@ -21,6 +22,19 @@ public class FfmpegDecoder : IWaveSource
 
     public unsafe int? BitPerRawSample => _formatContext != null ? _formatContext.SelectedStream.Stream.codec->bits_per_raw_sample : null;
 
+    public unsafe string? FileFormat
+    {
+        get
+        {
+            if (_formatContext == null) return null;
+
+            var codec = Marshal.PtrToStringUTF8((nint)_formatContext.SelectedStream.Stream.codec->codec->long_name);
+            var container = Marshal.PtrToStringUTF8((nint)_formatContext.FormatContext.iformat->name);
+
+            return $"{codec} @ {container}";
+        }
+    }
+
     // Orig
 
     private readonly object _lockObject = new object();
@@ -150,7 +164,7 @@ public class FfmpegDecoder : IWaveSource
                 //    Thread.Sleep(10);
                 //}
                 //else
-                    break; //no webstream -> exit
+                break; //no webstream -> exit
             }
             var bytesToCopy = Math.Min(count - read, bufferLength);
             Array.Copy(_overflowBuffer, 0, buffer, offset, bytesToCopy);

+ 1 - 1
Bmp.Core/FFMpeg/CsCorePorts/FFMpegWrap/Interops/FFmpeg.avcodec.g.cs

@@ -655,7 +655,7 @@ internal enum AVLockOp : int
     @AV_LOCK_DESTROY = 3,
 }
     
-internal unsafe static partial class ffmpeg
+internal unsafe static partial class FFMPEG
 {
     internal const int LIBAVCODEC_VERSION_MAJOR = 57;
     internal const int LIBAVCODEC_VERSION_MINOR = 64;

+ 3 - 3
Bmp.Core/FFMpeg/CsCorePorts/FFMpegWrap/Interops/FFmpeg.avformat.g.cs

@@ -260,7 +260,7 @@ internal unsafe partial struct AVStream
     internal AVPacketSideData* @side_data;
     internal int @nb_side_data;
     internal int @event_flags;
-    internal info* @info;
+    internal FFMPEG_INFO* @info;
     internal int @pts_wrap_bits;
     internal long @first_dts;
     internal long @cur_dts;
@@ -312,7 +312,7 @@ internal unsafe partial struct AVPacketList
 {
 }
     
-internal unsafe partial struct info
+internal unsafe partial struct FFMPEG_INFO
 {
     internal long @last_dts;
     internal long @duration_gcd;
@@ -495,7 +495,7 @@ internal enum AVTimebaseSource : int
     @AVFMT_TBCF_R_FRAMERATE = 2,
 }
     
-internal unsafe static partial class ffmpeg
+internal unsafe static partial class FFMPEG
 {
     internal const int LIBAVFORMAT_VERSION_MAJOR = 57;
     internal const int LIBAVFORMAT_VERSION_MINOR = 56;

+ 1 - 1
Bmp.Core/FFMpeg/CsCorePorts/FFMpegWrap/Interops/FFmpeg.avutil.g.cs

@@ -647,7 +647,7 @@ internal enum av_opt_eval_flags : int
     @AV_OPT_FLAG_IMPLICIT_KEY = 1,
 }
     
-internal unsafe static partial class ffmpeg
+internal unsafe static partial class FFMPEG
 {
     internal const int __STDC_CONSTANT_MACROS = 1;
     internal const int AVCODEC_D3D11VA_H = 1;

+ 1 - 1
Bmp.Core/FFMpeg/CsCorePorts/FFMpegWrap/Interops/FFmpeg.swresample.g.cs

@@ -60,7 +60,7 @@ internal enum SwrFilterType : int
     @SWR_FILTER_TYPE_KAISER = 2,
 }
     
-internal unsafe static partial class ffmpeg
+internal unsafe static partial class FFMPEG
 {
     internal const int LIBSWRESAMPLE_VERSION_MAJOR = 2;
     internal const int LIBSWRESAMPLE_VERSION_MINOR = 3;

+ 1 - 1
Bmp.Core/FFMpeg/CsCorePorts/FFMpegWrap/Interops/ffmpeg.extend.cs

@@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
 
 namespace Bmp.Core.FFMpeg.CsCorePorts.FFMpegWrap.Interops;
 
-internal unsafe partial class ffmpeg
+internal unsafe partial class FFMPEG
 {
     [DllImport(libavformat, EntryPoint = "avio_alloc_context", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
     internal static extern AVIOContext* avio_alloc_context(byte* @buffer, int @buffer_size, int @write_flag, void* @opaque, 

+ 4 - 3
Bmp.Core/Playback/Inputs/FFMPEGAudioStream.cs

@@ -1,5 +1,4 @@
-using System.Reflection.Metadata.Ecma335;
-using Bmp.Core.FFMpeg.CsCorePorts;
+using Bmp.Core.FFMpeg.CsCorePorts;
 using Bmp.Core.FFMpeg.CsCorePorts.FFMpegWrap;
 using Bmp.Core.Lite.Metadata;
 using Bmp.Core.Lite.Playback.Inputs;
@@ -8,8 +7,10 @@ using WaveFormat = NAudio.Wave.WaveFormat;
 
 namespace Bmp.Core.Playback.Inputs;
 
-public class FFMPEGAudioStream : WaveStream, IHaveBitPerRawSample, IHaveMetaData
+public class FFMPEGAudioStream : WaveStream, IHaveBitPerRawSample, IHaveMetaData, IHaveDecoderInfo
 {
+    string IHaveDecoderInfo.DecoderName => "FFMPEG";
+    string? IHaveDecoderInfo.FileFormat => _ffmpegDecoder?.FileFormat;
     MetaDataWrap IHaveMetaData.MetaData => new() { RawTags = _ffmpegDecoder?.Metadata, Duration = TotalTime };
 
     public int? BitPerRawSample => _ffmpegDecoder?.BitPerRawSample;

+ 12 - 8
Bmp.WinForms/MainForm.Designer.cs

@@ -63,7 +63,7 @@
             SeekTrackBar.LargeChange = 1;
             SeekTrackBar.Location = new Point(407, 12);
             SeekTrackBar.Name = "SeekTrackBar";
-            SeekTrackBar.Size = new Size(501, 69);
+            SeekTrackBar.Size = new Size(546, 69);
             SeekTrackBar.TabIndex = 5;
             SeekTrackBar.TickFrequency = 0;
             SeekTrackBar.TickStyle = TickStyle.Both;
@@ -138,7 +138,7 @@
             MainListView.Location = new Point(12, 87);
             MainListView.Name = "MainListView";
             MainListView.ShowItemToolTips = true;
-            MainListView.Size = new Size(982, 428);
+            MainListView.Size = new Size(1020, 609);
             MainListView.TabIndex = 7;
             MainListView.UseCompatibleStateImageBehavior = false;
             MainListView.View = View.Details;
@@ -188,7 +188,7 @@
             // 
             SettingButton.Anchor = AnchorStyles.Top | AnchorStyles.Right;
             SettingButton.Font = new Font("Microsoft YaHei UI", 19F);
-            SettingButton.Location = new Point(930, 12);
+            SettingButton.Location = new Point(959, 12);
             SettingButton.Name = "SettingButton";
             SettingButton.Size = new Size(73, 69);
             SettingButton.TabIndex = 6;
@@ -204,18 +204,22 @@
             // 
             // MainStatusBar
             // 
+            MainStatusBar.GripStyle = ToolStripGripStyle.Visible;
             MainStatusBar.ImageScalingSize = new Size(24, 24);
             MainStatusBar.Items.AddRange(new ToolStripItem[] { StatusBarLabel });
-            MainStatusBar.Location = new Point(0, 518);
+            MainStatusBar.Location = new Point(0, 699);
             MainStatusBar.Name = "MainStatusBar";
-            MainStatusBar.Size = new Size(1006, 22);
+            MainStatusBar.Size = new Size(1044, 31);
             MainStatusBar.TabIndex = 7;
             MainStatusBar.Text = "statusStrip1";
             // 
             // StatusBarLabel
             // 
             StatusBarLabel.Name = "StatusBarLabel";
-            StatusBarLabel.Size = new Size(0, 15);
+            StatusBarLabel.Size = new Size(1029, 24);
+            StatusBarLabel.Spring = true;
+            StatusBarLabel.Text = "Status Bar Label Brr Brr Brr Brr Brr Brr Brr Brr Brr";
+            StatusBarLabel.TextAlign = ContentAlignment.MiddleLeft;
             // 
             // SettingButtonToolTip
             // 
@@ -235,7 +239,7 @@
             MainPanel.Dock = DockStyle.Fill;
             MainPanel.Location = new Point(0, 0);
             MainPanel.Name = "MainPanel";
-            MainPanel.Size = new Size(1006, 518);
+            MainPanel.Size = new Size(1044, 699);
             MainPanel.TabIndex = 8;
             // 
             // SeekTrackBarToolTip
@@ -246,7 +250,7 @@
             // 
             AutoScaleDimensions = new SizeF(11F, 24F);
             AutoScaleMode = AutoScaleMode.Font;
-            ClientSize = new Size(1006, 540);
+            ClientSize = new Size(1044, 730);
             Controls.Add(MainPanel);
             Controls.Add(MainStatusBar);
             Name = "MainForm";

+ 81 - 101
Bmp.WinForms/MainForm.cs

@@ -1,7 +1,4 @@
-using System.CodeDom;
 using System.ComponentModel;
-using System.Diagnostics;
-using System.IO;
 using System.Text;
 using System.Threading.Channels;
 using Bmp.Core.Common.EventBus;
@@ -15,7 +12,6 @@ using Bmp.Core.Playback.Inputs;
 using Bmp.WinForms.SaveLoad;
 using Bmp.WinForms.SaveLoad.Models;
 using EnumsNET;
-using Microsoft.Extensions.Logging.Abstractions;
 using NAudio.Wave;
 using AsioOut = Bmp.Core.Lite.Playback.Outputs.NAudioASIO.AsioOut;
 
@@ -33,8 +29,8 @@ namespace Bmp.WinForms
         private readonly Channel<string> _pendingAddToList = Channel.CreateUnbounded<string>();
 
         private readonly ILogger<MainForm> _logger;
-        private readonly IEventBus? _eventBus;
-        private readonly SaveLoadService? _saveLoadService;
+        private readonly IEventBus _eventBus;
+        private readonly SaveLoadService _saveLoadService;
 
         private bool _isRunning;
 
@@ -81,7 +77,7 @@ namespace Bmp.WinForms
             string? balloonTitle = null;
             string? balloonContent = null;
 
-            bool? flagPlayOk = null;
+            var flagPlayOk = false;
 
             try
             {
@@ -149,7 +145,8 @@ namespace Bmp.WinForms
                     //TODO: 消息机制 警告 无法获取元数据
                 }
 
-                flagPlayOk = await PlayAsync();
+                Play();
+                flagPlayOk = true;
             }
             catch (Exception ex)
             {
@@ -161,7 +158,7 @@ namespace Bmp.WinForms
             finally
             {
                 MainPanel.Enabled = true;
-                if (flagPlayOk == null)
+                if (flagPlayOk == false)
                 {
                     item.SubItems[StateColumnHeader.Index].Text = finalState;
                     if (balloonShow)
@@ -184,10 +181,10 @@ namespace Bmp.WinForms
 
         private async Task ReloadSource()
         {
-            _inputSource = await Task.Run(() => InputSourceProvider.CreateWaveStream(_currentListViewItem!.Name, _saveLoadService?.State?.DecodeDsdToPcm == true));
+            _inputSource = await Task.Run(() => InputSourceProvider.CreateWaveStream(_currentListViewItem!.Name, _saveLoadService.State.DecodeDsdToPcm == true));
         }
 
-        private async Task DeInitOutputDeviceAsync()
+        private void DeInitOutputDevice()
         {
             var local = _outputDevice;
             _outputDevice = null;
@@ -207,13 +204,13 @@ namespace Bmp.WinForms
             }
         }
 
-        private async Task<bool> ReInitOutputDevice()
+        private bool ReInitOutputDevice()
         {
-            await DeInitOutputDeviceAsync();
+            DeInitOutputDevice();
 
             var finalState = EMOJI_PLAY_BIG;
 
-            bool balloonShow = false;
+            var balloonShow = false;
             ToolTipIcon? balloonIcon = null;
             string? balloonTitle = null;
             string? balloonContent = null;
@@ -223,7 +220,7 @@ namespace Bmp.WinForms
                 if (_inputSource is DsdAudioStream)
                 {
                     _nativeDsd = true;
-                    if (_saveLoadService?.State?.SelectedDsdAsioOutputDeviceId == null)
+                    if (_saveLoadService.State.SelectedDsdAsioOutputDeviceId == null)
                     {
                         finalState = EMOJI_X;
                         balloonShow = true;
@@ -235,9 +232,8 @@ namespace Bmp.WinForms
                     }
 
                     var allDevices = OutputDeviceProvider.GetAllSupportedDevices();
-                    IOutputDeviceInfo? selectedDevice = null;
 
-                    selectedDevice = allDevices.FirstOrDefault(p => p.Id == _saveLoadService?.State?.SelectedDsdAsioOutputDeviceId);
+                    var selectedDevice = allDevices.FirstOrDefault(p => p.Id == _saveLoadService.State.SelectedDsdAsioOutputDeviceId);
 
                     if (selectedDevice == null)
                     {
@@ -256,7 +252,7 @@ namespace Bmp.WinForms
                 else
                 {
                     _nativeDsd = false;
-                    if (_saveLoadService?.State?.SelectedPcmOutputDeviceId == null)
+                    if (_saveLoadService.State.SelectedPcmOutputDeviceId == null)
                     {
                         //TODO: 消息机制 错误 未指定输出设备
 
@@ -270,9 +266,8 @@ namespace Bmp.WinForms
                     }
 
                     var allDevices = OutputDeviceProvider.GetAllSupportedDevices();
-                    IOutputDeviceInfo? selectedDevice = null;
 
-                    selectedDevice = allDevices.FirstOrDefault(p => p.Id == _saveLoadService.State.SelectedPcmOutputDeviceId);
+                    var selectedDevice = allDevices.FirstOrDefault(p => p.Id == _saveLoadService.State.SelectedPcmOutputDeviceId);
 
                     if (selectedDevice == null)
                     {
@@ -355,7 +350,7 @@ namespace Bmp.WinForms
         private async Task StopAsync()
         {
             _playbackState = UIPlaybackState.Stopped;
-            await DeInitOutputDeviceAsync();
+            DeInitOutputDevice();
 
             if (_inputSource != null) await _inputSource.DisposeAsync();
             _inputSource = null;
@@ -368,31 +363,18 @@ namespace Bmp.WinForms
             }
         }
 
-        private async Task<bool> PlayAsync()
+        private void Play()
         {
-            if (_inputSource == null) return false;
+            if (_inputSource == null) return;
+            if (ReInitOutputDevice() == false) return;
+            _outputDevice!.Play();
             _playbackState = UIPlaybackState.Playing;
-            if (await ReInitOutputDevice())
-            {
-                try
-                {
-                    _outputDevice!.Play();
-                }
-                catch (Exception e)
-                {
-                    //TODO: 消息机制 错误 播放失败
-                    Console.WriteLine(e);
-                    _playbackState = UIPlaybackState.Error;
-                }
-            }
-
-            return _playbackState == UIPlaybackState.Playing;
         }
 
-        private async Task PauseAsync()
+        private void Pause()
         {
             _playbackState = UIPlaybackState.Paused;
-            await DeInitOutputDeviceAsync();
+            DeInitOutputDevice();
         }
 
         private async Task TrackPrevAsync()
@@ -440,20 +422,16 @@ namespace Bmp.WinForms
 
         private void SaveState()
         {
-            if (_saveLoadService == null) return;
-            if (_saveLoadService.State != null)
-            {
-                _saveLoadService.State.FormPosition = Location;
-                _saveLoadService.State.FormSize = Size;
+            _saveLoadService.State.FormPosition = Location;
+            _saveLoadService.State.FormSize = Size;
 
-                _saveLoadService.State.Playlist = MainListView.Items.Cast<ListViewItem>().Select(p => new SaveLoadPlaylistItem
-                {
-                    Path = p.Name,
-                    Title = p.SubItems[TitleColumnHeader.Index].Text,
-                    Duration = p.SubItems[DurColumnHeader.Index].Text,
-                    ToolTip = p.ToolTipText
-                }).ToArray();
-            }
+            _saveLoadService.State.Playlist = MainListView.Items.Cast<ListViewItem>().Select(p => new SaveLoadPlaylistItem
+            {
+                Path = p.Name,
+                Title = p.SubItems[TitleColumnHeader.Index].Text,
+                Duration = p.SubItems[DurColumnHeader.Index].Text,
+                ToolTip = p.ToolTipText
+            }).ToArray();
 
             _saveLoadService.Save();
         }
@@ -510,8 +488,8 @@ namespace Bmp.WinForms
             ctx.Items.Add("-");
 
             var allSupportedDevices = OutputDeviceProvider.GetAllSupportedDevices();
-            var selectedPcmDevice = allSupportedDevices.FirstOrDefault(p => p.Id == _saveLoadService?.State?.SelectedPcmOutputDeviceId);
-            var selectedDsdDevice = allSupportedDevices.FirstOrDefault(p => p.Id == _saveLoadService?.State?.SelectedDsdAsioOutputDeviceId);
+            var selectedPcmDevice = allSupportedDevices.FirstOrDefault(p => p.Id == _saveLoadService.State.SelectedPcmOutputDeviceId);
+            var selectedDsdDevice = allSupportedDevices.FirstOrDefault(p => p.Id == _saveLoadService.State.SelectedDsdAsioOutputDeviceId);
 
             var pcmOutputSelect = new ToolStripMenuItem($"PCM 输出{(selectedPcmDevice == null ? "(未选择)" : "")}");
             ctx.Items.Add(pcmOutputSelect);
@@ -520,11 +498,10 @@ namespace Bmp.WinForms
             ctx.Items.Add(dsdOutputSelect);
 
             var dsdToPcm = new ToolStripMenuItem("软解码成PCM");
-            if (_saveLoadService?.State?.DecodeDsdToPcm == true) dsdToPcm.CheckState = CheckState.Indeterminate;
+            if (_saveLoadService.State.DecodeDsdToPcm) dsdToPcm.CheckState = CheckState.Indeterminate;
             dsdOutputSelect.DropDownItems.Add(dsdToPcm);
             dsdToPcm.Click += delegate
             {
-                if (_saveLoadService?.State == null) return;
                 _saveLoadService.State.DecodeDsdToPcm = true;
                 _saveLoadService.State.SelectedDsdAsioOutputDeviceId = null;
                 ShowSettingContextMenu();
@@ -538,7 +515,6 @@ namespace Bmp.WinForms
                 if (selectedPcmDevice == deviceInfo) pcmDeviceItem.CheckState = CheckState.Indeterminate;
                 pcmDeviceItem.Click += delegate
                 {
-                    if (_saveLoadService?.State == null) return;
                     _saveLoadService.State.SelectedPcmOutputDeviceId = deviceInfo.Id;
                     _saveLoadService.State.OutputDeviceLatency = null;
                     ShowSettingContextMenu();
@@ -553,7 +529,6 @@ namespace Bmp.WinForms
 
                     dsdDeviceItem.Click += delegate
                     {
-                        if (_saveLoadService?.State == null) return;
                         _saveLoadService.State.SelectedDsdAsioOutputDeviceId = deviceInfo.Id;
                         _saveLoadService.State.DecodeDsdToPcm = false;
                         ShowSettingContextMenu();
@@ -574,14 +549,13 @@ namespace Bmp.WinForms
                     const int mulCount = 4;
 
                     var defaultLatency = selectedPcmDevice.Latency;
-                    var selectedLatency = _saveLoadService?.State?.OutputDeviceLatency ?? defaultLatency;
+                    var selectedLatency = _saveLoadService.State.OutputDeviceLatency ?? defaultLatency;
 
-                    var latencyMenu = new ToolStripMenuItem("PCM 输出缓冲大小:" + (_saveLoadService.State?.OutputDeviceLatency ?? defaultLatency));
+                    var latencyMenu = new ToolStripMenuItem("PCM 输出缓冲大小:" + (_saveLoadService.State.OutputDeviceLatency ?? defaultLatency));
                     ctx.Items.Add(latencyMenu);
 
                     void SetLatency(int latency)
                     {
-                        if (_saveLoadService.State == null) return;
                         _saveLoadService.State.OutputDeviceLatency = latency;
                         ShowSettingContextMenu();
                         //ShowSettingContextMenu(ctx.Items.IndexOf(latencyMenu));
@@ -622,7 +596,7 @@ namespace Bmp.WinForms
 
             if (selectedDsdDevice == null)
             {
-                if (_saveLoadService?.State?.DecodeDsdToPcm == false)
+                if (_saveLoadService.State.DecodeDsdToPcm == false)
                 {
                     ctx.Items.Add("(未选择 DSD 输出设备)").Enabled = false;
                 }
@@ -682,29 +656,23 @@ namespace Bmp.WinForms
 
             MainListView.Items.Clear();
 
-            if (_saveLoadService != null)
+            if (_saveLoadService.State.FormPosition.HasValue) Location = _saveLoadService.State.FormPosition.Value;
+            Application.DoEvents();
+            if (_saveLoadService.State.FormSize.HasValue) Size = _saveLoadService.State.FormSize.Value;
+
+            if (_saveLoadService.State.Playlist != null)
             {
-                if (_saveLoadService.State != null)
+                foreach (var playlistItem in _saveLoadService.State.Playlist)
                 {
-                    if (_saveLoadService.State.FormPosition.HasValue) Location = _saveLoadService.State.FormPosition.Value;
-                    Application.DoEvents();
-                    if (_saveLoadService.State.FormSize.HasValue) Size = _saveLoadService.State.FormSize.Value;
-
-                    if (_saveLoadService.State.Playlist != null)
-                    {
-                        foreach (var playlistItem in _saveLoadService.State.Playlist)
-                        {
-                            var item = new ListViewItem();
-                            for (var i = 0; i < MainListView.Columns.Count - 1; i++) item.SubItems.Add("");
+                    var item = new ListViewItem();
+                    for (var i = 0; i < MainListView.Columns.Count - 1; i++) item.SubItems.Add("");
 
-                            item.Name = playlistItem.Path;
-                            item.SubItems[TitleColumnHeader.Index].Text = playlistItem.Title;
-                            item.SubItems[DurColumnHeader.Index].Text = playlistItem.Duration;
-                            item.ToolTipText = playlistItem.ToolTip;
+                    item.Name = playlistItem.Path;
+                    item.SubItems[TitleColumnHeader.Index].Text = playlistItem.Title;
+                    item.SubItems[DurColumnHeader.Index].Text = playlistItem.Duration;
+                    item.ToolTipText = playlistItem.ToolTip;
 
-                            MainListView.Items.Add(item);
-                        }
-                    }
+                    MainListView.Items.Add(item);
                 }
             }
 
@@ -802,23 +770,25 @@ namespace Bmp.WinForms
         {
             MainListView.InsertionMark.Index = -1;
 
-            if (e.Data?.GetDataPresent(typeof(ListViewItem[])) == true)
+            if (e.Data?.GetDataPresent(typeof(ListViewItem[])) == true
+                && e.Data.GetData(typeof(ListViewItem[])) is ListViewItem[] items && items.Length > 0)
             {
-                var items = e.Data.GetData(typeof(ListViewItem[])) as ListViewItem[];
-
                 var point = MainListView.PointToClient(new Point(e.X, e.Y));
 
                 var targetIndex = MainListView.InsertionMark.NearestIndex(point);
-                //BUG: when move down index -= 1?
+
+                var isMoveDown = items[0].Index < targetIndex;
+                if (isMoveDown) --targetIndex;
+
                 if (targetIndex >= 0 && targetIndex < MainListView.Items.Count)
                 {
-                    foreach (var item in items!)
+                    foreach (var item in items)
                     {
                         MainListView.Items.Remove(item);
                     }
                     targetIndex = Math.Min(targetIndex, MainListView.Items.Count);
 
-                    for (int i = 0; i < items.Length; i++)
+                    for (var i = 0; i < items.Length; i++)
                     {
                         MainListView.Items.Insert(targetIndex + i, items[i]);
                     }
@@ -862,7 +832,7 @@ namespace Bmp.WinForms
             }
         }
 
-        private void MainContextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e)
+        private void MainContextMenu_Opening(object sender, CancelEventArgs e)
         {
             MainContextMenu.Items.Clear();
             var moveTop = new ToolStripMenuItem("置顶");
@@ -900,7 +870,7 @@ namespace Bmp.WinForms
                 var selectedItems = MainListView.SelectedItems.Cast<ListViewItem>().ToArray();
                 var targetIndex = 0;
                 foreach (var item in selectedItems) MainListView.Items.Remove(item);
-                for (int i = 0; i < selectedItems.Length; i++) MainListView.Items.Insert(targetIndex + i, selectedItems[i]);
+                for (var i = 0; i < selectedItems.Length; i++) MainListView.Items.Insert(targetIndex + i, selectedItems[i]);
             };
 
             moveUp.Click += delegate
@@ -908,14 +878,14 @@ namespace Bmp.WinForms
                 var selectedItems = MainListView.SelectedItems.Cast<ListViewItem>().ToArray();
                 var targetIndex = selectedItems[0].Index - 1;
                 foreach (var item in selectedItems) MainListView.Items.Remove(item);
-                for (int i = 0; i < selectedItems.Length; i++) MainListView.Items.Insert(targetIndex + i, selectedItems[i]);
+                for (var i = 0; i < selectedItems.Length; i++) MainListView.Items.Insert(targetIndex + i, selectedItems[i]);
             };
 
             moveDown.Click += delegate
             {
                 var selectedItems = MainListView.SelectedItems.Cast<ListViewItem>().ToArray();
-                ListViewItem nextItem = null;
-                for (int i = selectedItems[0].Index; i < MainListView.Items.Count; i++)
+                ListViewItem? nextItem = null;
+                for (var i = selectedItems[0].Index; i < MainListView.Items.Count; i++)
                 {
                     if (MainListView.Items[i].Selected == false)
                     {
@@ -931,7 +901,7 @@ namespace Bmp.WinForms
                 var targetIndex = nextItem.Index + 1;
 
                 if (targetIndex > MainListView.Items.Count) targetIndex = MainListView.Items.Count;
-                for (int i = 0; i < selectedItems.Length; i++) MainListView.Items.Insert(targetIndex + i, selectedItems[i]);
+                for (var i = 0; i < selectedItems.Length; i++) MainListView.Items.Insert(targetIndex + i, selectedItems[i]);
             };
 
             moveBottom.Click += delegate
@@ -939,12 +909,12 @@ namespace Bmp.WinForms
                 var selectedItems = MainListView.SelectedItems.Cast<ListViewItem>().ToArray();
                 foreach (var item in selectedItems) MainListView.Items.Remove(item);
                 var targetIndex = MainListView.Items.Count;
-                for (int i = 0; i < selectedItems.Length; i++) MainListView.Items.Insert(targetIndex + i, selectedItems[i]);
+                for (var i = 0; i < selectedItems.Length; i++) MainListView.Items.Insert(targetIndex + i, selectedItems[i]);
             };
 
             clearSelected.Click += delegate
             {
-                foreach (ListViewItem selectedItem in MainListView.SelectedItems.Cast<ListViewItem>().ToArray()) MainListView.Items.Remove(selectedItem);
+                foreach (var selectedItem in MainListView.SelectedItems.Cast<ListViewItem>().ToArray()) MainListView.Items.Remove(selectedItem);
             };
 
             clearAll.Click += delegate
@@ -957,13 +927,13 @@ namespace Bmp.WinForms
 
         private void SettingButton_Click(object sender, EventArgs e) => ShowSettingContextMenu();
 
-        private void StopButton_Click(object sender, EventArgs e) => StopAsync();
+        private async void StopButton_Click(object sender, EventArgs e) => await StopAsync();
 
         private async void PlayButton_Click(object sender, EventArgs e)
         {
             if (_playbackState == UIPlaybackState.Paused)
             {
-                await PlayAsync();
+                Play();
                 return;
             }
 
@@ -971,7 +941,7 @@ namespace Bmp.WinForms
             if (selectedItem != null) await LoadItemAsync(selectedItem);
         }
 
-        private async void PauseButton_Click(object sender, EventArgs e) => await PauseAsync();
+        private void PauseButton_Click(object sender, EventArgs e) => Pause();
 
         private async void PrevButton_Click(object sender, EventArgs e) => await TrackPrevAsync();
 
@@ -1009,6 +979,9 @@ namespace Bmp.WinForms
                 }
             }
 
+
+
+            //参数
             if (_inputSource != null)
             {
                 sb.Append(" | ");
@@ -1051,6 +1024,7 @@ namespace Bmp.WinForms
                     : $"/{Math.Floor((float)sampleRate / 100) / 10.0:0.0}{unit}Hz");
             }
 
+            //输出设备
             if (_selectedOutputDevice != null && _playbackState == UIPlaybackState.Playing)
             {
                 sb.Append(" | ");
@@ -1070,6 +1044,12 @@ namespace Bmp.WinForms
                 sb.Append($"{_selectedOutputDevice.DisplayName}");
             }
 
+            //解码器 格式编码
+            if (_inputSource is IHaveDecoderInfo dn)
+            {
+                sb.Append($" | {dn.DecoderName} {dn.FileFormat}");
+            }
+
             StatusBarLabel.Text = sb.ToString();
         }
 
@@ -1096,7 +1076,7 @@ namespace Bmp.WinForms
             {
                 try
                 {
-                    await PauseAsync();
+                    Pause();
 
                     _playbackState = UIPlaybackState.Seeking;
 
@@ -1123,7 +1103,7 @@ namespace Bmp.WinForms
                         });
                     }
 
-                    await PlayAsync();
+                    Play();
                 }
                 catch (Exception exception)
                 {

+ 4 - 13
Bmp.WinForms/SaveLoad/SaveLoadService.cs

@@ -10,12 +10,9 @@ internal class SaveLoadService : IAssemblyInjectSingleton, IAssemblyInjectSyncIn
     private readonly IEventBus _eventBus;
     private static readonly string FileName = $"{Application.ExecutablePath}.{nameof(SaveLoadState)}.json";
 
-    public SaveLoadState? State { get; private set; }
+    public SaveLoadState State { get; private set; } = new();
 
-    public SaveLoadService(IEventBus eventBus)
-    {
-        _eventBus = eventBus;
-    }
+    public SaveLoadService(IEventBus eventBus) => _eventBus = eventBus;
 
     public void Init()
     {
@@ -27,13 +24,7 @@ internal class SaveLoadService : IAssemblyInjectSingleton, IAssemblyInjectSyncIn
         else State = new();
     }
 
-    public void Stop()
-    {
-        Save();
-    }
+    public void Stop() => Save();
 
-    public void Save()
-    {
-        File.WriteAllText(FileName, JsonConvert.SerializeObject(State));
-    }
+    public void Save() => File.WriteAllText(FileName, JsonConvert.SerializeObject(State));
 }