C# 如何从实时流播放音频

C# 如何从实时流播放音频,c#,multimedia,C#,Multimedia,我有一个程序,它可以产生音频信号,应该可以同时播放。为此,我在每个100ms周期中播放100ms的音频流。但是我在每个100ms音频流的开始和结束都有不想要的信号(因为DC),所以即使信号值相同,输出的声音也不平滑。我的代码附在下面。请帮我做什么,我应该有一个正确的实时音频 using System; using System.Windows.Forms; using Microsoft.DirectX.DirectSound; using System.IO; namespace TestS

我有一个程序,它可以产生音频信号,应该可以同时播放。为此,我在每个100ms周期中播放100ms的音频流。但是我在每个100ms音频流的开始和结束都有不想要的信号(因为DC),所以即使信号值相同,输出的声音也不平滑。我的代码附在下面。请帮我做什么,我应该有一个正确的实时音频

using System;
using System.Windows.Forms;
using Microsoft.DirectX.DirectSound;
using System.IO;

namespace TestSound
{
    class CSound : Form
    {
        const int HEADER_SIZE = 44;
        const bool FLAG_STEREO = true;
        const short BITS_PER_SAMPLE = 16;
        const int SAMPLE_RATE = 44100;

        int numberOfSamples;
        MemoryStream stream;
        BinaryWriter writer;
        Device ApplicationDevice = null;
        SecondaryBuffer buffer = null;
        BufferDescription description;

        public CSound()
        {
            try
            {
                ApplicationDevice = new Device();
            }
            catch
            {
                MessageBox.Show("Unable to create sound device.");
                ApplicationDevice = null;
                return;
            }
            ApplicationDevice.SetCooperativeLevel(this, CooperativeLevel.Priority);
            description = new BufferDescription();
            description.ControlEffects = false;
            stream = new MemoryStream();
            writer = new BinaryWriter(stream);
        }

        private void AddHeader()
        {
            stream.Position = 0;

            writer.Write(0x46464952); // "RIFF" in ASCII
            writer.Write((int)(HEADER_SIZE + (numberOfSamples * BITS_PER_SAMPLE * (FLAG_STEREO ? 2 : 1) / 8)) - 8);
            writer.Write(0x45564157); // "WAVE" in ASCII
            writer.Write(0x20746d66); // "fmt " in ASCII
            writer.Write(16);
            writer.Write((short)1);
            writer.Write((short)(FLAG_STEREO ? 2 : 1));
            writer.Write(SAMPLE_RATE);
            writer.Write(SAMPLE_RATE * (FLAG_STEREO ? 2 : 1) * BITS_PER_SAMPLE / 8);
            writer.Write((short)((FLAG_STEREO ? 2 : 1) * BITS_PER_SAMPLE / 8));
            writer.Write(BITS_PER_SAMPLE);
            writer.Write(0x61746164); // "data" in ASCII
            writer.Write((int)(numberOfSamples * BITS_PER_SAMPLE * (FLAG_STEREO ? 2 : 1) / 8));
        }

        public void Play(short[] samples)
        {
            if (ApplicationDevice == null)
                return;

            stream.Position = HEADER_SIZE;
            numberOfSamples = samples.Length;
            for (int i = 0; i < numberOfSamples; i++)
            {
                writer.Write(samples[i]);
                if (FLAG_STEREO)
                    writer.Write(samples[i]);
            }
            AddHeader();
            stream.Position = 0;

            try
            {
                if (buffer != null)
                {
                    buffer.Dispose();
                    buffer = null;
                }
                buffer = new SecondaryBuffer(stream, description, ApplicationDevice);
                buffer.Play(0, BufferPlayFlags.Default);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }

        static short[] samples = new short[4410]; // 100 ms
        static CSound sound;

        static void Main()
        {
            Form form = new Form();
            form.Show();

            sound = new CSound();
            Random random = new Random();
            for (int i = 0; i < samples.Length; i++)
                samples[i] = 1000; // constant value

            while (true)
            {
                sound.Play(samples);
                System.Threading.Thread.Sleep(100); // 100 ms
            }
        }
     }
}
使用系统;
使用System.Windows.Forms;
使用Microsoft.DirectX.DirectSound;
使用System.IO;
名称空间测试声音
{
C类:表格
{
const int HEADER_SIZE=44;
const bool FLAG_立体声=真;
const short BITS_PER_SAMPLE=16;
常数int采样率=44100;
样本数;
记忆流;
二进制编写器;
设备ApplicationDevice=null;
SecondaryBuffer=null;
缓冲区描述;
公共CSound()
{
尝试
{
ApplicationDevice=新设备();
}
抓住
{
MessageBox.Show(“无法创建声音设备”);
ApplicationDevice=null;
返回;
}
ApplicationDevice.setCollaborativeLevel(这是CollaborativeLevel.Priority);
description=新的BufferDescription();
description.ControlEffects=false;
流=新内存流();
writer=新的二进制writer(流);
}
私有void AddHeader()
{
流位置=0;
writer.Write(0x464952);//ASCII格式的“RIFF”
Write.Write((int)(头大小+(样本数*每样本位*(标志立体声?2:1)/8))-8);
writer.Write(0x45564157);//ASCII格式的“WAVE”
writer.Write(0x20746d66);/“fmt”在ASCII中
写作(16);
作者:写((短)1);
写((短)(FLAG_STEREO?2:1));
写入(采样率);
写入(采样率*(标志立体声?2:1)*每采样位/8位);
writer.Write((短)((FLAG_STEREO?2:1)*每采样位/8位));
writer.Write(每个样本的位);
writer.Write(0x61746164);//ASCII格式的“数据”
Write.Write((int)(numberOfSamples*每个样本的位*(FLAG\u stereo2:1)/8));
}
公开无效播放(短[]个示例)
{
if(ApplicationDevice==null)
返回;
流位置=收割台尺寸;
numberOfSamples=样本长度;
对于(int i=0;i
此代码存在许多错误。我猜当你运行这段代码时,你每100毫秒就会听到一次点击或弹出的声音。这是因为while(true)循环中的Thread.Sleep(100)调用。基本上,您的应用程序正在等待100毫秒(给予或带走一小段时间),然后调用Play(),这会进行一些处理,然后将阵列排队等待播放。因此,在播放每个100ms阵列之间有一点时间间隔,这会产生点击

然而,如果你只是注释掉了Thread.Sleep(100)行,你的应用程序就会进入一个无限循环,它会一个接一个地排队等待100毫秒的数组,直到你的内存耗尽。但至少回放不会每100毫秒出现一次伪影

如果您将行更改为Thread.Sleep(80),它会工作得更好一些,从某种意义上说,内存耗尽的时间会更长,但这种情况仍然会发生,因为您将缓冲区转储到音频播放系统中的速度仍然快于系统播放缓冲区的速度

此外,即使您不再每隔100毫秒点击一次,您仍然听不到扬声器发出的任何声音,因为您的代码将每个示例值设置为1000。只有随着时间的推移改变采样值,才能听到任何声音。顺便说一句,您听到点击的唯一原因是因为此示例值设置为1000,并且在块之间的小时间间隔内,播放值返回到0。如果将每个采样值设置为0,则根本听不到任何声音


我可以在这方面进一步帮助你,但我需要更好地了解你到底想做什么。您是否正在尝试以特定频率播放连续音调?

如果您正在寻找通过定义的流播放音频的方法,您是否考虑过NAudio

您可以从文件或其他位置(即内存)定义流,然后用您想要播放的数据填充流。只要你