SoundPlayer causing Memory Leaks?
Asked Answered
D

8

8

I'm writing a basic writing app in C# and I wanted to have the program make typewriter sounds as you typed. I've hooked the KeyPress event on my RichTextBox to a function that uses a SoundPlayer to play a short wav file every time a key is pressed, however I've noticed after a while my computer slows to a crawl and checking my processes, audiodlg.exe was using 5 GIGABYTES of RAM.

The code I'm using is as follows:

I initialise the SoundPlayer as a global variable on program start with

SoundPlayer sp = new SoundPlayer("typewriter.wav")

Then on the KeyPress event I simply call

sp.Play();

Does anybody know what's causing the heavy memory usage? The file is less than a second long, so it shouldn't be clogging the thing up too much.

Dorrie answered 5/11, 2010 at 16:27 Comment(6)
If you comment out sp.Play(); do you notice your memory no longer grows to 5 Gb? If it still does, there's something else wrong with your code.Patton
Yes, definitely. I assigned one key to do nothing, and one to play the noise. Holding down the "do nothing" key makes no change to audiodg.exe's memory usage. Holding down the "play noise" key causes it to spike rapidly. It then doesn't drop if I leave it for several minutes.Dorrie
I've also checked my drivers, and apparently they're all up to date. I'll try reinstalling them though, in case it helps.Dorrie
Have you used a memory profiler to determine what object was leaking?Zeldazelde
Found the issue. It's a known bug with Windows since Vista that Microsoft have a Hotfix for. Hotfix can be found here: support.microsoft.com/kb/981013. For some reason stack overflow is preventing me from answering my question, so the answer will have to go here for nowDorrie
Scratch that, that didn't work and the problem persists. Also for some reason half the buttons on this site are now not working for me, so I cannot delete or edit my prior comment. The day's going well...Dorrie
S
6

Don't use SoundPlayer - use the waveOut... API instead:

http://www.codeproject.com/Articles/4889/A-full-duplex-audio-player-in-C-using-the-waveIn-w

SoundPlayer is more like a toy than a production-ready component, although I'm sure the MS intern that wrote it meant well. :)

Update: if you use the linked sample and get familiar with the code, you'll see what's probably wrong with the SoundPlayer implementation. Playing audio with the waveOut... functions involves two in-memory buffers: one small one for the header, and one potentially large buffer than contains the actual sample data. The hotfix article you linked to mentions the leak of a few hundred bytes each time Play is called, which means the code is probably instantiating a new header each time and then not disposing of it properly. (This is assuming SoundPlayer wraps the waveOut... API - I don't know whether this is the case or not)

Programmers take for granted the maxim "don't reinvent the wheel". Well, sometimes the wheel desperately needs reinventing.

Syncretism answered 29/1, 2011 at 17:50 Comment(2)
As at 2014/09/17 the link is broken.Persistence
Couldn't find the original source code so I added another (possibly better) link.Tighten
U
2

It could be a bug in the SoundPlayer.

Try this article on code project, maybe it will give you some hints.

Unpredictable answered 5/11, 2010 at 16:48 Comment(1)
I tried the article's implementation, which also didn't work I'm afraid.Dorrie
U
0

Try using the Load method of the sound player to load the sound, and then call play. Play uses a second thread to load(if not loaded already) and play the file.

Maybe the constructor does not load the file initially (which I think is quite possible) , it mearly associates the player with the sound file name.

Unpredictable answered 5/11, 2010 at 16:33 Comment(1)
I've just tried that and the memory is still spiking when the sound plays. I'm running Load() on my window's initializer, so it's definitely happening.Dorrie
F
0

I've done with this sample. WWFM (aka "Worked Well For Me). Try searching errors in your code (which, i'm almost sure, is pure enough) or another sound file.

Forelimb answered 25/1, 2011 at 12:15 Comment(2)
Still broke for me. Must be something wrong with my computer.Dorrie
Try updating your sound drivers, .NET and/or reinstall windows (as the most extreme way). Should help =)Forelimb
B
0

Try disposing the SoundPlayer after playing the sound. Then run the Garbage Collector. If it still consumes additional memory, something really nasty is happening and you should run the tests on another computer.

Bertina answered 26/1, 2011 at 14:1 Comment(2)
It's still not working for me, I'll try testing on my work pc, see if it also does it.Dorrie
Beware that using Garbage Collector also comes with a cost. Use it there as a temporary fix, but not as a final solution.Goldfinch
T
0

I've used the PlaySound function inside the Win32 API before to do something similar. Although this isn't in the same language that you're using, below is an example of a program that will play 'mahnamahna.wav' on every 100th keystroke.(Yes, it was quite funny)

 format PE GUI 4.0
entry start

;Mahna Mahna.

include 'win32a.inc'

include 'helper.asm'

section '.idata' import data readable writeable

    library kernel32,'KERNEL32.DLL',\
            user32,'USER32.DLL',\
            hook,'HOOK.DLL',\
            winmm,'WINMM.DLL'

    import  hook,\
            SetKeyPressedHandler,'SetKeyPressedHandler'

    import winmm,\
            PlaySound,'PlaySound'

    include 'api\kernel32.inc'
    include 'api\user32.inc'

section '.data' data readable writeable

    szWavFile db "mahnamahna.wav",0

    ;String saying what the dll is called.
    szDllName db "HOOK.DLL",0

    ;Name of the function in the dll for the keyboard procedure
    szf_KeyboardProc db "KeyboardProc",0

    ;handle to the dll
    hDll dd ?
    ;handle to the keyboard procedure
    hKeyboardProc dd ?
    ;handle to the hook
    hHook dd ?

    kInput KBINPUT

    keyCount dd 0x0 ;

    ;msg for the message pump
    msg MSG


section '.text' code readable executable

    start:

        ;Load the DLL into memory.
        invoke LoadLibraryA,szDllName
        cmp eax,0x0
        je exit
        mov [hDll],eax


        invoke GetProcAddress,[hDll],szf_KeyboardProc
        cmp eax,0x0
        je freeLibrary
        mov [hKeyboardProc],eax

        invoke SetKeyPressedHandler,KeyPressedHandler

    hook:
        invoke SetWindowsHookEx,WH_KEYBOARD_LL,[hKeyboardProc],[hDll],0x0
        cmp eax,0x0
        je freeLibrary
        mov [hHook],eax

    msg_loop:
        invoke  GetMessage,msg,NULL,0,0
        cmp eax,1
        jb  unhook
        jne msg_loop
        invoke  TranslateMessage,msg
        invoke  DispatchMessage,msg
    jmp msg_loop



    proc KeyPressedHandler code,wparam,lparam

        ;Move the VK Code of the key they pressed into al.
        xor eax,eax
        mov eax,[lparam]
        mov cx,word [eax]

        cmp [wparam],WM_KEYDOWN
        je .ProcessKeyDown
        cmp [wparam],WM_KEYUP
        je .ProcessKeyUp

        .ProcessKeyDown:

            ret ;No need to go any further - we only process characters on key up
        .ProcessKeyUp:
            mov edx,[keyCount]
            inc edx

            cmp cx,VK_F12
            je unhook

            ;Hotkeys.
            ;F12 - Quit.
            cmp edx,0x64
            jne .done
            call MahnaMahna
            xor edx,edx
            .done:
            mov [keyCount],edx
        ret
    endp

    proc MahnaMahna
        invoke PlaySound,szWavFile,0x0,0x20000
        ret
    endp

    unhook:
        invoke UnhookWindowsHookEx,[hHook]

    freeLibrary:
        invoke FreeLibrary,[hDll]
    exit: 
        invoke ExitProcess,0

The above will not work without the following dll(hook.dll)

 format PE GUI 4.0 DLL
entry _DllMain

include 'win32a.inc'

section '.data' data readable writeable
    hKeyPressedHandler dd 0x0
section '.text' code readable executable

proc _DllMain hinstDLL,fdwReason,lpvReserved
    mov eax,TRUE
    ret
endp

    proc SetKeyPressedHandler hProc
        mov eax,[hProc]
        mov [hKeyPressedHandler],eax
        ret
    endp

    proc KeyboardProc code,wparam,lparam
        cmp [code],0x0
        jl CallNextHook

        cmp [hKeyPressedHandler],0x0;Make sure our event handler is set.
        je CallNextHook

        ;Call our handler.
        invoke hKeyPressedHandler,[code],[wparam],[lparam]

        CallNextHook:
            invoke CallNextHookEx,0x0,[code],[wparam],[lparam]
            ret
    endp

section '.idata' import data readable writeable

    library kernel32,'KERNEL32.DLL',\
            user32,'USER32.DLL'

    include 'api\kernel32.inc'
    include 'api\user32.inc'

section '.edata' export data readable
    export 'hook.DLL',\
        KeyboardProc,'KeyboardProc',\
        SetKeyPressedHandler,'SetKeyPressedHandler'

section '.reloc' fixups data discardable
Tesler answered 30/1, 2011 at 0:30 Comment(0)
D
0

This isn't strictly speaking an answer, so I won't confirm this as the accepted answer to my question, but it is a solution for those who have had the same problems (and also confirms it's not my system at fault)

I decided to implement the sound using ManagedDirectX's AudioPlayback library, which is about as easy to use as SoundPlayer, but has successfully solved my problem.

For those who want to know, the code is simple:

1) Add a reference to the audioplayback dll.

2) Create an Audio object (I named mine sound), make it a variable on your form so you can refer to it again, use the constructor to set the filename it should play

3) Play the file with sound.Play();

4) If you need to play the file again, use the following line:

sound.SeekCurrentPosition(0, SeekPositionFlags.AbsolutePositioning);

It's fairly fast, and fairly good. There'll be memory issues if you need a lot of different sound effects, because they'll all constantly be in memory, but if you need one sound to play a lot, this one will do it without ballooning your Audiodlg.exe

Dorrie answered 1/2, 2011 at 23:17 Comment(0)
D
0

You should try using()

using(SoundPlayer sp = new SoundPlayer("typewriter.wav")) {
   sp.Play();
}

when process finish sp.Play() memory return to your system automatics.

Deferment answered 12/11, 2011 at 9:48 Comment(2)
How is my system using more memory though? I want to create it at the start, use it as much as I want and then remove it, instead of creating and destroying it every time it's run (which is a lot)Dorrie
Sorry I forgot u try to use sound for typing.Deferment

© 2022 - 2024 — McMap. All rights reserved.