gpt4 book ai didi

c# - C# 中真正的低级声音生成?

转载 作者:行者123 更新时间:2023-12-04 02:06:20 25 4
gpt4 key购买 nike

任何人都知道在 C# 中创建任意声波并从扬声器播放的明智方法吗?

多年来,这个问题时不时地出现,我总是在多次失败后最终放弃而没有找到解决方案。

我想做的就像一个反向可视化器,即我不想从声音中生成“数字”,我想从数字中生成声音。

就像获取我提供的采样率、样本大小和声音数据(例如整数数组)的函数一样,它会从中生成适当的 wav 文件(实时声音播放是理想的,但我对此也非常满意)。

我知道 wav 文件规范在互联网上随处可见,并且确实做了几次尝试创建上述功能,在低频方面取得了一些成功,但是一旦我开始弄乱每个样本的位数等......它就变成了一个巨大的、无法控制的困惑。

这不是已经以任何方式完成了吗?
我不介意它使用什么,只要它有一个 .NET 托管包装器(并且我可以从最近的 VS 访问它)。
XNA 不支持这种方式的低电平音频。还发现了几个声称可以实现类似目标的示例,但它们要么根本不起作用,要么做一些完全不同的事情。

谢谢你。

最佳答案

这看起来很有趣,所以我敲了一个简单的应用程序:

  • 创建两秒钟纯音 (440Hz A) 的样本。
  • 将它们转换为 WAV 文件格式的字节数组。
  • 通过将字节数组传递给 PlaySound API 来播放声音。
  • 还包括将 WAV 数据保存到 WAV 文件的代码。

  • 您可以轻松更改采样率、音调频率和采样持续时间。该代码非常丑陋且空间效率低下,但它有效。以下是一个完整的命令行应用程序:

    使用系统;
    使用 System.Diagnostics;
    使用 System.IO;
    使用 System.Runtime.InteropServices;

    命名空间播放
    {
    类(class)计划
    {
    [DllImport("winmm.dll", EntryPoint = "PlaySound", SetLastError = true)]
    private extern static int PlaySound(byte[] wavData, IntPtr hModule, PlaySoundFlags flags);

    //#define SND_SYNC 0x0000/* 同步播放(默认)*/
    //#define SND_ASYNC 0x0001/* 异步播放 */
    //#define SND_NODEFAULT 0x0002/* 如果没有找到声音,则静音(!默认)*/
    //#define SND_MEMORY 0x0004/* pszSound 指向内存文件 */
    //#define SND_LOOP 0x0008/* 循环播放直到下一个 sndPlaySound */
    //#define SND_NOSTOP 0x0010/* 不停止任何当前播放的声音 */

    //#define SND_NOWAIT 0x00002000L/* 驱动忙不等待 */
    //#define SND_ALIAS 0x00010000L/* 名称是注册表别名 */
    //#define SND_ALIAS_ID 0x00110000L/* 别名是预定义的 ID */
    //#define SND_FILENAME 0x00020000L/* name 是文件名 */
    //#define SND_RESOURCE 0x00040004L/* 名称是资源名称或原子 */

    枚举 PlaySoundFlags
    {
    SND_SYNC = 0x0000,
    SND_ASYNC = 0x0001,
    SND_MEMORY = 0x0004
    }

    //播放出现在字节数组中的 wav 文件
    静态无效 PlayWav(byte[] wav)
    {
    PlaySound(wav, System.IntPtr.Zero, PlaySoundFlags.SND_MEMORY | PlaySoundFlags.SND_SYNC);
    }

    静态字节[] ConvertSamplesToWavFileFormat(short[] left, short[] right, int sampleRate)
    {
    Debug.Assert(left.Length == right.Length);

    const int channelCount = 2;
    int sampleSize = sizeof(short) * channelCount * left.Length;
    int totalSize = 12 + 24 + 8 + 样本大小;

    字节[] wav = 新字节[总大小];
    int b = 0;

    //RIFF 头
    wav[b++] = (byte)'R';
    wav[b++] = (byte)'I';
    wav[b++] = (byte)'F';
    wav[b++] = (byte)'F';
    int chunkSize = totalSize - 8;
    wav[b++] = (byte)(chunkSize & 0xff);
    wav[b++] = (byte)((chunkSize >> 8) & 0xff);
    wav[b++] = (byte)((chunkSize >> 16) & 0xff);
    wav[b++] = (byte)((chunkSize >> 24) & 0xff);
    wav[b++] = (byte)'W';
    wav[b++] = (byte)'A';
    wav[b++] = (byte)'V';
    wav[b++] = (byte)'E';

    //格式标题
    wav[b++] = (byte)'f';
    wav[b++] = (byte)'m';
    wav[b++] = (byte)'t';
    wav[b++] = (byte)' ';
    wav[b++] = 16;
    wav[b++] = 0;
    wav[b++] = 0;
    wav[b++] = 0;//块大小
    wav[b++] = 1;
    wav[b++] = 0;//压缩代码
    wav[b++] = channelCount;
    wav[b++] = 0;// channel 数
    wav[b++] = (byte)(sampleRate & 0xff);
    wav[b++] = (byte)((sampleRate >> 8) & 0xff);
    wav[b++] = (byte)((sampleRate >> 16) & 0xff);
    wav[b++] = (byte)((sampleRate >> 24) & 0xff);
    int byteRate = sampleRate * channelCount * sizeof(short);//所有 channel 的字节速率
    wav[b++] = (byte)(byteRate & 0xff);
    wav[b++] = (byte)((byteRate >> 8) & 0xff);
    wav[b++] = (byte)((byteRate >> 16) & 0xff);
    wav[b++] = (byte)((byteRate >> 24) & 0xff);
    wav[b++] = channelCount * sizeof(short);
    wav[b++] = 0;//块对齐(每个样本的字节数)
    wav[b++] = sizeof(short) * 8;
    wav[b++] = 0;//每个样本的位数

    //数据块头
    wav[b++] = (byte)'d';
    wav[b++] = (byte)'a';
    wav[b++] = (byte)'t';
    wav[b++] = (byte)'a';
    wav[b++] = (byte)(sampleSize & 0xff);
    wav[b++] = (byte)((sampleSize >> 8) & 0xff);
    wav[b++] = (byte)((sampleSize >> 16) & 0xff);
    wav[b++] = (byte)((sampleSize >> 24) & 0xff);

    调试.断言(b == 44);

    for (int s = 0; s != left.Length;++s)
    {
    wav[b++] = (byte)(left[s] & 0xff);
    wav[b++] = (byte)(((ushort)left[s] >> 8) & 0xff);
    wav[b++] = (byte)(right[s] & 0xff);
    wav[b++] = (byte)(((ushort)right[s] >> 8) & 0xff);
    }

    Debug.Assert(b == totalSize);

    回波;
    }

    //创建一个简单的正弦波
    static void CreateSamples(out short[] left, out short[] right, int sampleRate)
    {
    const double middleC = 261.626;
    常量双重标准A = 440;

    const 双频 = 标准 A;

    int 计数 = 采样率 * 2;//两秒
    左 = 新短[计数];
    右 = 新短[计数];

    for (int i = 0; i != count;++i)
    {
    双 t = (double)i/采样率;//此样本的时间(以秒为单位)
    short s = (short)Math.Floor(Math.Sin(t * 2 * Math.PI * frequency) * short.MaxValue);
    左[i] = s;
    右[i] = s;
    }
    }

    static void Main(string[] args)
    {
    短[]离开;
    短[]对;
    int 采样率 = 44100;
    CreateSamples(out left, out right, sampleRate);
    byte[] wav = ConvertSamplesToWavFileFormat(left, right, sampleRate);
    PlayWav(wav);

    /*
    //将数据写入wav文件
    使用 (FileStream fs = new FileStream(@"C:\documents and settings\carlos\desktop\a440stereo.wav", FileMode.Create))
    {
    fs.Write(wav, 0, wav.Length);
    }
    */
    }
    }
    }

    关于c# - C# 中真正的低级声音生成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3743591/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com