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

jss*_*jrm 11 c# audio wav low-level

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

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

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

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

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

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

谢谢.

arx*_*arx 8

这看起来很有趣所以我打破了一个简单的应用:

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

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

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace playwav
{
    class Program
    {
        [DllImport("winmm.dll", EntryPoint = "PlaySound", SetLastError = true)]
        private extern static int PlaySound(byte[] wavData, IntPtr hModule, PlaySoundFlags flags);

        //#define SND_SYNC            0x0000  /* play synchronously (default) */
        //#define SND_ASYNC           0x0001  /* play asynchronously */
        //#define SND_NODEFAULT       0x0002  /* silence (!default) if sound not found */
        //#define SND_MEMORY          0x0004  /* pszSound points to a memory file */
        //#define SND_LOOP            0x0008  /* loop the sound until next sndPlaySound */
        //#define SND_NOSTOP          0x0010  /* don't stop any currently playing sound */

        //#define SND_NOWAIT      0x00002000L /* don't wait if the driver is busy */
        //#define SND_ALIAS       0x00010000L /* name is a registry alias */
        //#define SND_ALIAS_ID    0x00110000L /* alias is a predefined ID */
        //#define SND_FILENAME    0x00020000L /* name is file name */
        //#define SND_RESOURCE    0x00040004L /* name is resource name or atom */

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

        // Play a wav file appearing in a byte array
        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(short) * channelCount * left.Length;
            int totalSize = 12 + 24 + 8 + sampleSize;

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

            // RIFF header
            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';

            // Format header
            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; // Chunk size
            wav[b++] = 1;
            wav[b++] = 0; // Compression code
            wav[b++] = channelCount;
            wav[b++] = 0; // Number of channels
            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); // byte rate for all channels
            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; // Block align (bytes per sample)
            wav[b++] = sizeof(short) * 8;
            wav[b++] = 0; // Bits per sample

            // Data chunk header
            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)(right[s] & 0xff);
                wav[b++] = (byte)(((ushort)right[s] >> 8) & 0xff);
            }

            Debug.Assert(b == totalSize);

            return wav;
        }

        // Create a simple sine wave
        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; // Two seconds
            left = new short[count];
            right = new short[count];

            for (int i = 0; i != count; ++i)
            {
                double t = (double)i / sampleRate; // Time of this sample in seconds
                short s = (short)Math.Floor(Math.Sin(t * 2 * Math.PI * frequency) * short.MaxValue);
                left[i] = s;
                right[i] = s;
            }
        }

        static void Main(string[] args)
        {
            short[] left;
            short[] right;
            int sampleRate = 44100;
            CreateSamples(out left, out right, sampleRate);
            byte[] wav = ConvertSamplesToWavFileFormat(left, right, sampleRate);
            PlayWav(wav);

            /*
            // Write the data to a wav file
            using (FileStream fs = new FileStream(@"C:\documents and settings\carlos\desktop\a440stereo.wav", FileMode.Create))
            {
                fs.Write(wav, 0, wav.Length);
            }
            */
        }
    }
}