如何播放实时流中的音频

发布于 2024-08-02 06:56:20 字数 3642 浏览 5 评论 0原文

我有一个程序可以产生应该同时播放的音频信号。为此,我在每个 100 毫秒周期内播放 100 毫秒间隔的音频流。但是我在每个 100 ms 音频流的开始和结束处都有不需要的信号(因为 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
            }
        }
     }
}

I have a program which produces audio signals which supposed to be played simultaneously. For this, I play an interval of 100 ms of audio stream in each 100 ms period. But I have undesired signals in beginning and ending of each 100 ms audio stream (because of DC) so that the output sound is not smooth even the value of signals is the same. My code is attached bellow. Please help me what I should do to have a correct real-time audio.

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
            }
        }
     }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

病女 2024-08-09 06:56:20

如果您正在寻找一种通过定义的流播放音频的方法,您是否考虑过 NAudio http://naudio.codeplex。 com/

您可以从文件或其他位置(即内存)定义一个流,然后用您想要播放的数据填充该流。只要您能够在读取指针到达缓冲区末尾之前继续向流提供音频数据,您就不会在生成的音频中听到这些声音。

顺便说一句 - 我假设您知道 .Net 的 Managed Direct X 库不再被开发,并且实际上是此类音频开发的死胡同?

If your looking for a way to play audio out through a defined stream, have you considered NAudio http://naudio.codeplex.com/?

You can define a stream, from either a file or some other location (i.e. memory) and then populate the stream with the data you want played back. As long as you are able to continue providing audio data to the stream before the read pointer arrives at the end of the buffer, you wont hear these artefacts in the generated audio.

BTW - I assume you know that the Managed Direct X libraries for .Net are no longer being developed and is effectively a dead end for this sort of Audio development?

梅窗月明清似水 2024-08-09 06:56:20

这段代码有很多问题。我猜当您运行此代码时,您每 100 毫秒就会听到咔哒声或爆裂声。这是因为 while(true) 循环内调用了 Thread.Sleep(100)。基本上,您的应用程序等待 100 毫秒(给予或花费一小段时间),然后调用 Play(),它会进行一些处理,然后将数组排队等待播放。因此,每个 100 毫秒阵列的播放之间存在一点时间间隙,从而产生喀哒声。

但是,如果您只是注释掉 Thread.Sleep(100) 行,您的应用程序将进入无限循环,在 100 毫秒数组之后不断排队,直到内存耗尽。但至少回放不会每隔 100 毫秒出现伪影。

如果您将该行更改为 Thread.Sleep(80),它会工作得更好一点,因为您需要更长的时间才能耗尽内存,但这种情况仍然会发生,因为您仍然会将缓冲区转储到音频中播放系统的速度比系统播放它们的速度快。

另外,即使您消除了每 100 毫秒一次的喀哒声,您仍然不会听到扬声器发出任何声音,因为您的代码将每个样本值设置为 1000。只有当您改变随时间变化的样本值。顺便说一句,您听到咔嗒声的唯一原因是因为此样本值设置为 1000,并且在块之间的小时间间隔内,播放值返回到 0。如果将每个样本值设置为 0,您将永远不会听到任何声音。

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

There are a number of things wrong with this code. I'm guessing that when you run this code, you're hearing clicking or popping sounds every 100 ms. This is because of the Thread.Sleep(100) call inside the while(true) loop. Basically, your app is waiting 100 ms (give or take a small time amount) then calling Play(), which does a little processing and then queues up the array for playback. As a result, there is a little time gap between the playing of each 100 ms array, which is producing the clicks.

However, if you just commented out the Thread.Sleep(100) line, your app would go into an infinite loop where it keeps queueing up 100 ms array after 100 ms array until you ran out of memory. But at least the playback would not have the artifacts every 100 ms.

If you changed the line to Thread.Sleep(80), it would work a little better, in the sense that it would take you longer to run out of memory, but this would still happen because you would still be dumping buffers into the audio playback system faster than the system can play them.

Also, even if you get rid of the clicking every 100 ms, you still won't hear anything at all coming out of your speakers, because your code is setting every sample value to 1000. You will only ever hear anything if you vary the sample values over time. Incidentally, the only reason you're hearing clicks at all is because this sample value is set to 1000, and during those little time intervals between chunks the playback value goes back to 0. If you set each sample value to 0, you would never hear anything at all.

I could help you further with this, but I'd need to have a better idea of what you're trying to do, exactly. Are you trying to play a continuous tone at a certain frequency?

梦里泪两行 2024-08-09 06:56:20

如果“不良信号”指的是两端都有轻微的爆裂声,则可能是包络问题,在 Csound 中可以通过“linen”操作码或类似的东西来控制。这个想法是,您需要提高前端的振幅,并稍微降低后端的振幅,以避免扬声器在中波中突然停止输出的咔嗒声,可以这么说。几毫秒就足够了——进行实验,直到消除爆音而不会注意到幅度调制。

看这里:http://www.csounds.com/journal/issue11/csoundEnvelopes.html< /a>

如果您尝试通过以规则的时间间隔顺序地发出相同的波形来产生无缝信号,那么您总是会听到这种爆裂声,因为一个波形的结尾与下一个波形的开头不对齐。让波形精确排列是非常困难的,这并不是一个好的策略。更好的策略是使用包络(如上所述)并重叠波形(称为燕尾形),以便旧清晰度的衰减与新清晰度的上升同时发生。

然而,这种策略不会产生完全纯净的声音,因为两个有些相同的波形异步重叠的存在会稍微相互抵消,并导致波形每个接合点的幅度减小。

If by "undesirable signals" you mean a slight popping sound at either end, it might be an envelope problem, which in Csound could be controlled by the 'linen' opcode or something similar. The idea being that you need to ramp up the amplitude on the front end, and down on the back end slightly to avoid the clicking sound of the loudspeakers abruptly ceasing their output in mid wave, so to speak. A few ms should suffice--experiment with it until you get rid of the popping without noticing the amplitude modulation.

Look here: http://www.csounds.com/journal/issue11/csoundEnvelopes.html

If you are trying to produce a seamless signal by articulating an identical waveform sequentially at regular intervals, then you will always hear this popping sound because the end of one waveform doesn't line up with the beginning of the next waveform. It's very difficult to get the waveforms to line up precisely, and that's not really a good strategy. A better strategy would be to use an envelope (as described above) and overlap the waveforms (called dove-tailing) so that the decay of the old articulation happens simultaneously with the rise of the new articulation.

However, this strategy won't produce a completely pure sound because the presence of two somewhat identical waveforms overlapping asynchronously will cancel each other out a bit and cause the amplitude to decrease at each juncture of the waveforms.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文