Подтвердить что ты не робот

Реальное поколение звука низкого уровня в С#?

Кто-нибудь знает о разумном способе создания звуковой волны ARBITRARY в С# и воспроизвести ее из динамиков?

Эта проблема периодически повторяется в течение многих лет, и я всегда оставляю ее после много неудач, не найдя решения.

Что я хочу сделать, это как обратный визуализатор, то есть я не хочу генерировать "числа" из звука, я хочу генерировать звук из чисел.

Как получить функцию, которую я предоставляю с частотой дискретизации, размером выборки и звуковыми данными (массив целых чисел, например), и он будет генерировать соответствующий wav файл из него (воспроизведение в реальном времени звука было бы идеальным, но я был бы более чем доволен этим).

Я знаю, что спецификации wav файлов находятся во всем interweb, и делали несколько попыток создания вышеупомянутой функции, имели некоторый успех для низких частот, но как только я начинаю возиться с битами на образец и т.д.... он становится ОГРОМНЫМ, неуправляемый беспорядок.

Разве это уже не сделано? Я бы не возражал против того, что он использует, до тех пор, пока для него существует управляемая оболочка .NET(и я могу получить к нему доступ с самого последнего VS на время). Таким образом, XNA не поддерживает звук низкого уровня. Также найдено несколько примеров, которые претендуют на достижение чего-то подобного, но они либо вообще не работают, либо делают что-то совершенно другое.

Спасибо.

4b9b3361

Ответ 1

Это выглядело интересно, поэтому я выбил простое приложение, которое:

  • Создает образцы в течение двух секунд чистого тона (440 Гц A).
  • Преобразует их в массив байтов в формате WAV.
  • Воспроизведение звука путем передачи байтового массива в API PlaySound.
  • Также включает код для сохранения данных 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);
            }
            */
        }
    }
}

Ответ 2

FMOD может выполнять выборку нагрузок из памяти и имеет оболочку С#.

Ответ 3

Как играть из массива ниже

    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();
    }