Click here to Skip to main content
15,880,608 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hello Everyone!
i have created a little app that can emulate an electric piano(but it only will play sine waves) i have based my app on this article
now i am using the play to stream part of the demo. what this does is it writes the entire wave file of what ever note i specify into memory and then plays it back using the System.Media.SoundPlayer class.
but the problem is that every time i click the button in the form to play a note, the memory usage (i am watching this happen on task manager) increases by a few hundred KBs
i think this is due to the fact that i am storing a lot of data into memory but i am never deleting it. so my question is how to i clear the contents of a MemoryStream?
here is what i have so far:

this is executed on a separate thread
C#
private void Operation()
{
    //Instalize The MemoryStream(This Is To Store All The Wave Data Into Memory
    MemoryStream stream = new MemoryStream();

    //Construct The Note Sound And Put It Into The Stream
    PlayNote(stream, 180, 5000, false, NoteToPlayFreq, 1, 5000);

    // Jump Back To The Beginning Of The Stream
    stream.Position = 0;

    //Instalize A New Instance Of The Soundplayer Class
    SoundPlayer player = new SoundPlayer(stream);
        
    //Play The Note
    player.PlaySync();

    //This Is Supposed To Clear The Stream But It Dosent
    stream.Dispose();

    //Raise the NoteCompletedEvent event at the end of the program loop
    NoteCompletedEvent();
        
}

now the PlayNote method is this:
C#
public void PlayNote(Stream output, double tempo, short defaultVolume, bool closeUnderlyingStream, float frequency, double length, short volume)
{
    this._tempo = tempo;

    _writer = new WaveWriter16Bit(output, 44100, false, closeUnderlyingStream);

    _defaultVolume = defaultVolume;


    if (volume < 0)
        throw new ArgumentOutOfRangeException("volume", "Volume must be greater than or equal to zero.");

    double samplesPerCycle = _writer.SampleRate / frequency;

    int samplesForNote = (int)(_writer.SampleRate * length * 60 / Tempo);


    Sample16Bit sample = new Sample16Bit();

    for (int currentSample = 0; currentSample < samplesForNote; currentSample++)
    {
        bool endOfStream = true;

        if (_writer.CurrentSample < _writer.NumberOfSamples)
        {
            sample = _writer.Read();
            endOfStream = false;
        }
        else
            sample.LeftChannel = 0;

        // If we're at the end of the note, fade out linearly.
        // This causes two back-to-back notes with the same frequency
        // to have a break between them, rather than being one
        // continuous tone.
        int distanceFromEnd = samplesForNote - currentSample;
        short finalVolume = distanceFromEnd < 1000 ? ((short)(volume * distanceFromEnd / 1000)) : volume;

        double sampleValue = Math.Sin(currentSample / samplesPerCycle * 2 * Math.PI) * finalVolume + sample.LeftChannel;

        if (sampleValue > short.MaxValue)
            sampleValue = short.MaxValue;
        else if (sampleValue < short.MinValue)
            sampleValue = short.MinValue;

        sample.LeftChannel = (short)(sampleValue);

        if (endOfStream == false)
            _writer.CurrentSample--;

        _writer.Write(sample);
    }

    if (_writer != null)
        _writer.Close();
}

i figure that if i played a long song(in excess of a few thousand button clicks) then my memory usage for this app would be through the roof.
so what am i doing wrong?
thanks for your help in advance,
MasterCodeon
Posted

I would suggest that if you are using the code from the article you referenced that the issue is in there. I browsed through the source code and there are several items that I would clean up. The first two being the binary reader and writer. These should be specifically disposed when the class is being disposed. They will be disposed of when the garbage collection routine runs, but it is good practice to always close streams and dispose of them as soon as you are finished with them.

You also need to remember that the dot.net Garbage Collector does not neccesarilly remove objects from memory as soon as they are disposed of. You would not want your CPU cycles to be sucked up by the GC. It can take a short time before the GC decides to remove the objects. There are numerous articles on how the GC works if you are interested.

Hopefully this gives you a starting point to look into. There are also some very good products out for debugging memory issues. One is JetBrains dotMemory. Another one is red-gates Ants Memory Profiler. I have had minor experience with both and they are both very good. Microsoft also has a free one, I think it is called CLR Profiler or something like that. I have never used it, so I cannot speak to how useful it is.
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 30-Dec-14 11:59am    
5ed.
—SA
Generally, there are at least two good ways to clean-up a memory stream without wasting much of the CPU and effort.

First of all, if, at some moment, you have a stream and want to get a clear stream without any data, it means that you don't need this available stream instance at all. Therefore, you can safely abandon this instance and create a new one, initialized to empty data; and than you can add data from scratch. Therefore, this is the most trivial way: http://msdn.microsoft.com/en-us/library/ad966f9s%28v=vs.110%29.aspx[^].

Another way operating on the same instance is also related to the possible use of the stream and is this. If you want clear stream, it means that you are going to write new content to it. That is, you may need just to ignore the previous content, not to remove it. Therefore, you can just rewind the stream to zero position and write from this place. This is how:
http://msdn.microsoft.com/en-us/library/system.io.memorystream.position%28v=vs.110%29.aspx[^],
http://msdn.microsoft.com/en-us/library/system.io.memorystream.seek%28v=vs.110%29.aspx[^].

Isn't it logical?

—SA
 
Share this answer
 
v2
Comments
MasterCodeon 29-Dec-14 12:50pm    
what do you mean when you said "initialized to empty data"?
Sergey Alexandrovich Kryukov 29-Dec-14 12:54pm    
Empty content. Something you were looking for.
—SA
MasterCodeon 29-Dec-14 12:58pm    
ah.
i don't know how to directly write something to a MemoryStream.
i am using the classes the author of the article i linked to write sound data to the stream.
so how would one overwrite the data in a memory stream with nothing?
also i do know how to set the streams write position to 0
Sergey Alexandrovich Kryukov 29-Dec-14 13:02pm    
I don't understand what is "directly". You always can write whatever you want using the general stream interface. But I did not mean directly. You can write by any means, it does not matter. If you ever need empty stream content, it means that you are going to write to it, otherwise it would not make any sense.
I answered all your questions; just read the answer carefully.
—SA
MasterCodeon 29-Dec-14 13:10pm    
ok, i reread your solution and the first suggestion seems like i would just Dispose or Close the Stream and then open a new one and all the data in the old one would be gone.
so maybe i just am not doing something right, or the MemoryStream isn't my problem.

i have done this(this referring to me using the Dispose/Close Methods every time i am done with the stream) that already but when i was watching the process in Task manager, the memory usage still climbed every time i click a note on the keyboard.

and as regards to your first suggestion, i found the Write method in the MemoryStream class its just i don't know how to use a byte[](i think its called a byte array) parameter.
and when i say "directly write something to a MemoryStream" i mean use the Write method to write something to the stream

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900