Реальный низкий уровень генерации звука в C#?

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

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

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

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

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

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

Спасибо.

3 ответа

Решение

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

  • Создает сэмплы в течение двух секунд чистого тона (440 Гц A).
  • Преобразует их в байтовый массив в формате файла WAV.
  • Воспроизведение звука путем передачи байтового массива в API PlaySound.
  • Также включает в себя код для сохранения данных WAV в файл WAV.

Вы можете легко изменить частоту дискретизации, частоту тона и продолжительность выборки. Код очень уродливый и неэффективный, но работает. Ниже приведено полное приложение командной строки:

используя Систему; использование System.Diagnostics; используя 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);

        //#define SND_SYNC            0x0000  /* играть синхронно (по умолчанию) */
        //#define SND_ASYNC           0x0001  /* играть асинхронно * / // # определить SND_NODEFAULT       0x0002  /* тишина (! по умолчанию), если звук не найден * / // # определить 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 /* псевдоним - предопределенный идентификатор */
        //#define SND_FILENAME    0x00020000L /* имя - это имя файла */
        //#define SND_RESOURCE    0x00040004L /* имя - это имя ресурса или атом */

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

        // Воспроизведение файла wav с отображением в формате wav статический массив 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 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; // Количество каналов 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(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);

            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); возврат WAV;
        }

        // Создать простую синусоидальную статическую пустоту CreateSamples(out short[] left, out short[] right, int sampleRate)
        {
            const double middleC = 261.626; конст двойной стандарт А = 440; постоянная двойная частота = стандартная А;

            int count = sampleRate * 2; // две секунды осталось = new short [count]; право = новый короткий [количество];

            for (int i = 0; i!= count; ++i)
            {
                double t = (double)i / sampleRate; // Время этой выборки в секундах, короткое s = (короткое)Math.Floor(Math.Sin(t * 2 * Math.PI * частота) * short.MaxValue);
                left[i] = s; право [я] = с;
            }
        }

        static void Main(string[] args)
        {
            short[] left; короткий [] правый;
            int sampleRate = 44100;
            CreateSamples(слева, справа, 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);
            }
            */
        }
    }
}

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

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

    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();
    }
Другие вопросы по тегам