Commit 27482d3d authored by shrabbit's avatar shrabbit
Browse files

feat(ImageAudio): 添加 ImageAudio 游乐场,将像素转为音频采样并播放,新增 NAudio 依赖并注册其他游乐场

parent d756f901
Loading
Loading
Loading
Loading
+117 −0
Original line number Diff line number Diff line
using NAudio.Wave;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using Spectre.Console;
using TestV.Tools;

namespace TestV.Playgrounds;

public class ImageAudio : IPlayground
{
    public string LaunchCmd { get; } = "ia";
    public string? Name { get; } = "Image Audio";
    public string? Description { get; } = "将32位像素转换成音频采样";
    public async Task Run(string[] args)
    {
        if (Errors.ValidArgsLength(args, 1))
        {
            
        }

        if (!File.Exists(args[0]))
        {
            AnsiConsole.MarkupLine("[red]文件不存在。[/]");
            return;
        }
        
        var sample = ToSample(Image.Load<Rgba32>(args[0]));
        Console.WriteLine($"SAMPLE: {sample.Length}");
        
        PlayFloatArray(sample, 44100, 1);
    }

    public static void PlayFloatArray(float[] samples, int sampleRate, int channels)
    {
        // 1. 将 float[] 转换为 byte[]
        byte[] buffer = new byte[samples.Length * sizeof(float)];
        Buffer.BlockCopy(samples, 0, buffer, 0, buffer.Length);

        // 2. 创建 RawSourceWaveStream(指定 32-bit Float 格式)
        var waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels);
        using var stream = new MemoryStream(buffer);
        using var rawStream = new RawSourceWaveStream(stream, waveFormat);

        // 3. 播放
        using var outputDevice = new WaveOutEvent();
        outputDevice.Init(rawStream);
        outputDevice.Play();

        // 等待播放完成
        while (outputDevice.PlaybackState == PlaybackState.Playing)
        {
            Thread.Sleep(100);
        }
    }

    private float[] ToSample(Image<Rgba32> image)
    {
        Console.WriteLine($"w: {image.Width}, h: {image.Height}");
        
        var list = new List<float>();

        foreach (var pixel in GetPixels(image))
        {
            list.AddRange(PixelToSample(pixel));
        }
        
        return list.ToArray();
    }

    public static Rgba32[] GetPixels(Image<Rgba32> image)
    {
        int width = image.Width;
        int height = image.Height;
        var pixels = new Rgba32[width * height];

        image.ProcessPixelRows(accessor =>
        {
            for (int y = 0; y < height; y++)
            {
                Span<Rgba32> row = accessor.GetRowSpan(y);
                // 将整行像素拷贝到数组对应位置
                row.CopyTo(pixels.AsSpan(y * width, width));
            }
        });

        return pixels;
    }

    private float[] PixelToSample(Rgba32 pixel)
    {
        var arr = new float[32];
        Array.Copy(ChannelToSample(pixel.R), 0, arr, 0, 8);
        Array.Copy(ChannelToSample(pixel.G), 0, arr, 8, 8);
        Array.Copy(ChannelToSample(pixel.B), 0, arr, 16, 8);
        Array.Copy(ChannelToSample(pixel.A), 0, arr, 24, 8);
        return arr;
    }

    private float[] ChannelToSample(byte channel)
    {
        return
        [
            GetBit(channel, 0),
            GetBit(channel, 1),
            GetBit(channel, 2),
            GetBit(channel, 3),
            GetBit(channel, 4),
            GetBit(channel, 5),
            GetBit(channel, 6),
            GetBit(channel, 7),
            GetBit(channel, 7),
        ];
    }

    private float GetBit(byte value, int n) => ((value >> n & 1) != 0) ? 1f : 0f;
}
 No newline at end of file
+4 −0
Original line number Diff line number Diff line
@@ -163,6 +163,10 @@ class Program
                .Add<ArraySpan>()
                .Add<MsPinyinAnalysis>()
                .Add<FrameworkDetect>()
                .Add<EEEnum>()
                .Add<Factorial>()
                .Add<SubProgram>()
                .Add<ImageAudio>()
                .Add<Exp1>();
            
            play.Startup(args);
+2 −0
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@
    
      <PackageReference Include="CommunityToolkit.HighPerformance" Version="8.4.2" />
    
      <PackageReference Include="NAudio" Version="2.3.0" />
    
      <PackageReference Include="RadLine" Version="0.10.0" />
    
      <PackageReference Include="Spectre.Console" Version="0.55.2" />