Play sound in .NET using generated waveform data
Asked Answered
E

6

7

How can I play a sound based on waveform data that my .NET program is generating from user input and mathematical functions?

By "waveform data" I mean SPL (sound pressure level) values in a fixed interval time-series (probably 44.1 kHz). I presume that this requires some kind of streaming buffer arrangement.

Note, that this has to be live/real-time, so just creating a .wav file and then playing that will not be sufficient. VB.NET is preferred, but C# is acceptable also.

Just to clarify: What I am looking for is a simple working code example.

Epithelioma answered 5/7, 2009 at 16:59 Comment(2)
I have finally gotten around to trying the NAudio solution, and it is excellent! So much better and easier than I had feared, I really should have tried it long ago.Epithelioma
A more operational answer to this question is in Stack Overflow question NAudio playing a sine wave for x milliseconds using C#.Giuliana
D
4

You can do this using NAudio. You create a stream that derives from WaveStream and in its overriden Read method, you return your samples which you can generate on the fly. You have control over the size of the buffers used by the soundcard which gives you control over the latency.

Diverticulitis answered 5/7, 2009 at 18:53 Comment(1)
I did finally get a chance to try NAudio and it is excellent. Thanks for the pointer.Epithelioma
J
3

How to play from an array of doubles

    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();
    }
Jell answered 2/2, 2014 at 16:11 Comment(0)
C
1

Check out this thread on loading up a DirectSound buffer with arbitrary data and playing it.

Per comment: Yes, I know. You will need to translate the C++ into C# or VB.NET. But, the concept is what's important. You create a secondary DirectSound buffer and then use it to stream over to your primary buffer and play.

Choreograph answered 5/7, 2009 at 18:17 Comment(1)
Well that's not VB.net or C#. In fact it doesn't look like .Net at all.Epithelioma
L
1

IrrKlang, BASS.net (under "Other APIs"), NAudio, CLAM, G3D, and others that can do this.

Lunnete answered 14/11, 2009 at 18:46 Comment(0)
E
0

I think you'll need to use DirectSound (DirectX API) for that. It works off buffers which you could fill with your generated data.

Maybe something like this (here in way back machine) will help

Emaciation answered 5/7, 2009 at 17:59 Comment(0)
L
0

I have this code but you will have to have code to generate your wav file in memory.

Option Strict Off
Option Explicit On
Imports Microsoft.DirectX.DirectSound
Imports Microsoft.DirectX
Imports System.Threading

Public Class Form1
Const SRATE As Integer = 44100
Const FREQ As Integer = 440
Const DUR As Integer = 1

Private dsDesc As BufferDescription
Private wvFormat As WaveFormat
Private DS As Device

Dim SecondaryBuffer As Microsoft.DirectX.DirectSound.SecondaryBuffer
Dim BufferDescription As Microsoft.DirectX.DirectSound.BufferDescription
Dim DXFormat As Microsoft.DirectX.DirectSound.WaveFormat
Dim sbuf(DUR * SRATE) As Short


Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Show()
    DS = New Microsoft.DirectX.DirectSound.Device
    DS.SetCooperativeLevel(Me, CooperativeLevel.Normal)
    wvFormat.FormatTag = WaveFormatTag.Pcm
    wvFormat.Channels = 1
    wvFormat.SamplesPerSecond = SRATE
    wvFormat.BitsPerSample = 16
    wvFormat.AverageBytesPerSecond = 2 * SRATE
    wvFormat.BlockAlign = 2
    dsDesc = New BufferDescription(wvFormat)
    dsDesc.BufferBytes = 2 * DUR * SRATE
    dsDesc.Flags = 0
    Dim buff1 = PlayWave(400)
    Dim buff2 = PlayWave(600)
    buff1 = PlayWave(400)
    buff1.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Default)
    Thread.Sleep(1000)
    buff1 = PlayWave(600)
    buff1.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Default)
    ' End
End Sub
Function PlayWave(FREQ As Integer) As SecondaryBuffer
    ' create a buffer
    Dim dsBuffer As SecondaryBuffer
    dsBuffer = New SecondaryBuffer(dsDesc, DS)
    Dim sbuf(DUR * SRATE) As Short
    ' create tone                
    For i As Integer = 0 To DUR * SRATE
        sbuf(i) = CShort(10000 * Math.Sin(2 * Math.PI * FREQ * i / SRATE))
    Next
    ' copy to buffer
    dsBuffer.Write(0, sbuf, LockFlag.EntireBuffer)
    Return dsBuffer
End Function
Leeann answered 20/7, 2016 at 14:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.