5

Whole day I was looking for some tutorial or piece of code, "just" to play simple sin wave for "infinity" time. I know it sounds a little crazy.

But I want to be able to change frequency of tone in time, for instance - increase it. Imagine that I want to play tone A, and increase it to C in "+5" frequency steps each 3ms (it's really just example), don't want to have free places, stop the tone.

Is it possible? Or can you help me?

G5W
  • 36,531
  • 10
  • 47
  • 80
eCorke
  • 858
  • 1
  • 10
  • 24

2 Answers2

5

Use NAudio library for audio output.

Make notes wave provider:

class NotesWaveProvider : WaveProvider32
{
    public NotesWaveProvider(Queue<Note> notes)
    {
        this.Notes = notes;
    }
    public readonly Queue<Note> Notes;
    int sample = 0;

    Note NextNote()
    {
        for (; ; )
        {
            if (Notes.Count == 0)
                return null;
            var note = Notes.Peek();
            if (sample < note.Duration.TotalSeconds * WaveFormat.SampleRate)
                return note;

            Notes.Dequeue();
            sample = 0;
        }

    }
    public override int Read(float[] buffer, int offset, int sampleCount)
    {
        int sampleRate = WaveFormat.SampleRate;
        for (int n = 0; n < sampleCount; n++)
        {
            var note = NextNote();
            if (note == null)
                buffer[n + offset] = 0;
            else
                buffer[n + offset] = (float)(note.Amplitude * Math.Sin((2 * Math.PI * sample * note.Frequency) / sampleRate));
            sample++;
        }
        return sampleCount;
    }
}
class Note
{
    public float Frequency;
    public float Amplitude = 1.0f;
    public TimeSpan Duration = TimeSpan.FromMilliseconds(50);

}

start play:

WaveOut waveOut;
this.Notes = new Queue<Note>(new[] { new Note { Frequency = 1000 }, new Note { Frequency = 1100 } });
var waveProvider = new NotesWaveProvider(Notes);
waveProvider.SetWaveFormat(16000, 1); // 16kHz mono    

waveOut = new WaveOut();
waveOut.Init(waveProvider);
waveOut.Play();

add new notes:

void Timer_Tick(...)
{
 if (Notes.Count < 10)
   Notes.Add(new Note{Frecuency = 900});
}

ps this code is idea only. for real using add mt-locking etc

Aneri
  • 1,342
  • 8
  • 21
Serj-Tm
  • 16,581
  • 4
  • 54
  • 61
  • Works very nice to launch a sequence of Notes. However, adding more notes to the queue after the initial "melody" has finished does not work properly. You'd expect the new note to start immediately. This can be achieved with above code by inserting a reset of the sample counter in statement if (Notes.Count == 0) return null;,.. my code is now: if (Notes.Count==0) { sample=0; return null; }. In this case, the next note will start immediately, also if it is launched a few seconds later. Also, the for (;;) is not needed in NextNote. – Goodies Jul 10 '17 at 14:47
1

use NAudio and SineWaveProvider32: http://mark-dot-net.blogspot.com/2009/10/playback-of-sine-wave-in-naudio.html

private WaveOut waveOut;

private void button1_Click(object sender, EventArgs e)
{
  StartStopSineWave();
}

private void StartStopSineWave()
{
  if (waveOut == null)
  {
    var sineWaveProvider = new SineWaveProvider32();
    sineWaveProvider.SetWaveFormat(16000, 1); // 16kHz mono
    sineWaveProvider.Frequency = 1000;
    sineWaveProvider.Amplitude = 0.25f;
    waveOut = new WaveOut();
    waveOut.Init(sineWaveProvider);
    waveOut.Play();
  }
  else
  {
    waveOut.Stop();
    waveOut.Dispose();
    waveOut = null;
  }
}
Serj-Tm
  • 16,581
  • 4
  • 54
  • 61
  • thanks...I tried it, but if I'm trying to change frequency and it stopped playing for short time. What I need is maybe to have one function for playing simple array, in cycle, for instance one cycle is 5ms (5ms delay for me is enough) and on the end of each cycle check bufferArray for new "tone" to play...and in another thread to add after some time new "tone"... – eCorke Mar 07 '12 at 12:35
  • but I don't know how to play it.. and haven't found any solution :( – eCorke Mar 07 '12 at 12:35