This project has moved and is read-only. For the latest updates, please go here.

Simpler OpenTK testing app

Jan 29, 2014 at 10:21 PM
Edited Jan 29, 2014 at 10:30 PM
I'm trying to figure out how to use NVorbis with OpenAL (OpenTK), but I'm having a difficult time understanding the test app since it's overly complex and uncommented. I'd like to request a simple example of how to correctly just play a file and another one slightly more complex with volume control and panning (and perhaps other sound effects), with the minimum of "extra features".

Thanks for this amazing project!
Jan 30, 2014 at 9:55 PM
The test app is probably not the "best" place to look, but it's about all I have at the moment. When I get a chance (in a few weeks, minimum) I might try to put together a simple example. Feel free to send one of your own if you'd like... :)
Jan 31, 2014 at 3:54 PM
Edited Feb 1, 2014 at 12:54 AM
I've found the "core" of how to use NVorbis through OpenTK:
            // This test file doesn't play properly. It gets cut near the end.
            string file = "3test.ogg";
            AudioContext audioContext = new AudioContext();
            OggStreamer oggStreamer = new OggStreamer(65536);
            OggStream oggStream = new OggStream(file);
            // When finished playing (add logic here):
For some reason the test file 3test.ogg doesn't play properly, it gets cut near the end. I've also made a simpler test app which has serious issues with not freeing up RAM (I hope it isn't too big to post here; and feel free to recycle the code if it's of any value):
(Edit: removed to save space. Check the Pastebin link on the next post.)
It also lacks panning, which would be a nice thing to have.
Feb 1, 2014 at 12:53 AM
I've added sound positioning to the test app. It still has a giant memory leak and doesn't play until the very end.
Feb 2, 2014 at 3:03 PM
A few comments RE: memory leak:
  • Make Player inherit from IDisposable... The method is there, but the interface is not.
  • Add an event (StreamFinished?) to NewOggStream that is raised from NotifyFinished(), then
  • Add an event handler in Player for the new "StreamFinished" event that calls Player's Dispose().
  • Keep track of Player instances somehow and add a StopAll() method, then
  • Add a keystroke to the control loop that calls the StopAll() method and exits immediately.
Finally, change
            string input;


                    input = Console.ReadKey(true).KeyChar.ToString().ToLowerInvariant();
                    switch (input)
            Key input;


                    input = Console.ReadKey(true);
                    switch (input.Key)
and update all the switch cases to use the enumeration... Strings are the death of a managed app's efficiency.
Feb 2, 2014 at 9:49 PM
Edited Feb 2, 2014 at 11:33 PM
I think I found (part of) the problem. The OggStreamer class notifies the base OggStream class but not the NewOggStream... so the terminated bool never changed nor does the event. I tested it with a modified OggStream but I'm still losing around 2MB per playback (but at least the memory usage decreases a bit after finishing). I also renamed the Dispose() to Unload() since audioChecker and oggStreamer are static and they give a lot of lag each time they are recreated (I tested this way and still lost the 2MB).

(Edit: Correcting a typo and removing now useless questions.)
Feb 2, 2014 at 11:05 PM
Here's the code to the 3rd version:
I've removed NewOggStream because it wasn't actually necessary. I still can't figure out how to solve the memory problem...
Feb 3, 2014 at 2:17 AM
More comments:
  • Player.Execute() should just "return;" instead of "thread.Abort();" The latter isn't recommended for pretty much any use.
  • Your usage of listOfPlayers is multi-threaded, but List<> is not thread-safe... locking will work, but you might want something more thoughtful.
  • Really investigate race conditions with the commented-out items in Player.Dispose()... They are probably a clue.
  • Lastly, check to see when each component downstream from your Player is being deallocated (disposed)... Sounds like VorbisReader is getting held on to somewhere.
Feb 3, 2014 at 10:52 PM
Solved the memory leak! I had to lock the list and had to call oggStream.Close() after oggStream.Dispose().
There are still some issues related to crashing (not everything is thread safe yet, I think). Sometimes ALHelper crashes on Check() (I included the detailed error report in the code). Also Play() won't play after Stop() (although it did on version 2).
Feb 4, 2014 at 4:17 PM
OK, OggStream is bugged if it has to be disposed AND closed. It should handle all of that in the Dispose logic and have Close() just call Dispose(). Feel free to submit a patch.

Also, the Finished event should probably be renamed and a true "Finished" event added. Basically the "Finished' event is firing on the wrong trigger, but I think the trigger it is hooked to is probably useful for certain scenarios. Either way, Finished should be raise once the last samples of the stream have been read.

Your Stop()/Play() sequence probably only worked by happenstance before... The Player instance should be recreated after being stopped due to the fact that the OggStream instance is probably raising the "Finished" event once it has stopped and is therefore triggering its own disposal...

I'm no expert on OpenAL, so I'm not sure how to debug the ALHelper.Check() call... Obviously it's an invalid value, but how? Maybe alStreamId is invalid for the current state? Not sure...
Feb 4, 2014 at 4:40 PM
My fault, Close() doesn't need to be called after Dispose(). I'll see what I can do about a true "Finished" event, the Stop()/Play() sequence and the ALHelper.Check() crashing, but I don't promise too much since I'm just a hobbyist. Also what do you think about the last samples of the file being cut off? Are they caused by the Finished event?
Mar 26, 2014 at 8:06 PM
I've been busy so I couldn't finish this. For anyone who's interested in playing Ogg Vorbis files in MonoGame, OpenTK or XNA, I recommend checking how Delta Engine did it: