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

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

这个问题已经不断出现多年,我总是在经历了很多失败之后放弃了它而没有找到解决方案。

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

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

我知道wav文件规格遍布整个interweb,并且确实做了几次创建上述function的尝试,对于低频率有一些成功,但是一旦我开始弄乱每个样本的比特等……它就变成了一个巨大的,无法控制的混乱。

这还没有以任何方式完成吗? 我不介意它使用什么,只要它有一个.NET托管包装(我可以从最近的VS访问它到时间)。 XNA不支持低级音频。 还发现了几个声称可以实现类似function的例子,但它们要么根本不起作用,要么做一些完全不同的事情。

谢谢。

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

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

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

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

命名空间playwav
 {
    课程
     {
         [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 / * name是注册表别名* /
         // #define SND_ALIAS_ID 0x00110000L / *别名是预定义的ID * /
         // #define SND_FILENAME 0x00020000L / * name是文件名* /
         // #define SND_RESOURCE 0x00040004L / * name是资源名称或primefaces* /

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

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

         static byte [] ConvertSamplesToWavFileFormat(short [] left,short [] right,int sampleRate)
         {
             Debug.Assert(left.Length == right.Length);

             const int channelCount = 2;
             int sampleSize = sizeof(短)* channelCount * left.Length;
             int totalSize = 12 + 24 + 8 + sampleSize;

             byte [] wav = new byte [totalSize];
             int b = 0;

             // RIFF标题
             wav [b ++] =(byte)'R';
             wav [b ++] =(byte)'我';
             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;  //频道数量
             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);  //所有通道的字节速率
             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(短);
             wav [b ++] = 0;  //块对齐(每个样本的字节数)
             wav [b ++] = sizeof(短)* 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);

             Debug.Assert(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)(右[s]&0xff);
                 wav [b ++] =(byte)(((ushort)right [s] >> 8)&0xff);
             }

             Debug.Assert(b == totalSize);

            返回wav;
         }

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

             const double frequency = standardA;

             int count = sampleRate * 2;  //两秒钟
             left = new short [count];
             right = new short [count];

             for(int i = 0; i!= count; ++ i)
             {
                 double t =(double)i / sampleRate;  //这个样本的时间,以秒为单位
                 short s =(short)Math.Floor(Math.Sin(t * 2 * Math.PI * frequency)* short.MaxValue);
                 left [i] = s;
                对[i] = s;
             }
         }

         static void Main(string [] args)
         {
            短[]左;
            短[]右;
             int sampleRate = 44100;
             CreateSamples(左,右,sampleRate);
             byte [] wav = ConvertSamplesToWavFileFormat(left,right,sampleRate);
             PlayWav(WAV);

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

FMOD可以从内存中进行样本加载,并具有C#包装器。

如何从下面的arrays播放

  PlayerEx pl = new PlayerEx(); private static void PlayArray(PlayerEx pl) { double fs = 8000; // sample freq double freq = 1000; // desired tone short[] mySound = new short[4000]; for (int i = 0; i < 4000; i++) { double t = (double)i / fs; // current time mySound[i] = (short)(Math.Cos(t * freq) * (short.MaxValue)); } IntPtr format = AudioCompressionManager.GetPcmFormat(1, 16, (int)fs); pl.OpenPlayer(format); byte[] mySoundByte = new byte[mySound.Length * 2]; Buffer.BlockCopy(mySound, 0, mySoundByte, 0, mySoundByte.Length); pl.AddData(mySoundByte); pl.StartPlay(); }