Need a really simple library for playing sounds and music? Try TinySound.

I posted it there a few days ago, but I forgot to thank you for the suggestion. So, thanks for the suggestion.

Cool, thanks. The license I used doesn’t require attribution like this, but I appreciate it. I don’t actually have a web page for TinySound, but you could just link to the repository (https://github.com/finnkuusisto/TinySound) if you want to link it to something. I should probably think about making a web page.

I’ve merged in the streams_and_redesign branch and now have a prettier page for TinySound thanks to GitHub Pages. You can link to this instead if you want.
http://finnkuusisto.github.com/TinySound/

Hi kuusisto, I’ve to say that you made a great work with this library. It not only gives just what I wanted and nothing more but it is also so clearly written!! I love it.

My congratulations. We definitely will be using it in Izzy’s Revenge.

I have just one comment. In my desktop the library worked OK, but in my laptop I noticed some serious crackling, quite annoying.

I have been checking your code. It seems that in UpdateRunner class, you read data with a 25ms buffer. Then you perform a Thread.sleep(1);

It is know that sleep command in several windows machines totally sucks, in my laptop, sometimes Ihave more than 30 ms sleep after calling Thread.sleep(1);

Increasing maxFramesPerUpdate for storing up to 100ms of data per iteration into audioBuffer worked great! No more crackling, the music became smooth and perfect.

So, if there is not any extra problem, maybe is a good idea for futures updates to increase the default buffer size from 25ms to 100 ms or even more.

Have you tried the approach mentioned here? http://www.java-gaming.org/topics/about-on-my-main-loop/26222/msg/229028/view.html#msg229028

Hi again! yes, that maybe would work too, but inside the kuusisto code, UpdateRunner method doesn’t need an accurate loop, just a (more or less) regular loop for filling the buffer, so just increasing the buffer size is enough

Also, I’ve been working some more with this library. I found (in my opinion) an important limitation. As I can understand, by now, a sound cannot be played twice simultaneously. You need to load different sounds with the source file (waste of memory).

What I did is just add to the SteamMusic/MemMusic/SteamSound/MemSound a new method called getInstance() This method return a new music/sound that shares the source data. This is a quite small code addition:

           
        public MemMusic getInstance(){return new MemMusic(left,right,mixer,lengthSeconds);}

        public MemSound getInstance(){return new MemSound(left,right,mixer,tinysound.TinySound.getSoundID(),lengthSeconds);}


But was very useful for adding environmental sounds when the same sound comes from several different locations

Sorry I haven’t been on in so long. I’m rapidly approaching a few deadlines in my research, so I’ve been pretty swamped (probably will be for a while actually).

You should be able to play a Sound multiple times simultaneously. When you call play(), it creates a reference to the sound data and passes it off to the mixer. Can I ask how you came to this conclusion? If it really isn’t working, it’s possible there is a bug.

If I recall, the reason I had it at 25ms was to reduce latency. If it buffers up too much audio data there can be some audible lag between the time a sound is 'play()'ed and the time that it actually plays. It might be a good idea to up the default a little bit, but I’m reluctant to increase it to 100ms. That said, I’m glad it works so well for you though. I also think it’s really cool that you’re using TinySound and have taken enough of an interest to dig in the code!

Ok, my fault, I just tried it again and it works nice. I dunno why I assumed that. Maybe because I just started using music.

About the delay, 50 ms were also fine for removing the crackling. I didn’t noticed any lag (core i5 laptop and core i7 desktop, both java 1.7). Maybe just happens with slower computers or other JVM. Why does it happens? because the computer takes a lot of time filling the whole buffer for the first time? Interesting. I will take a look again at the code.

BTW, as I saw in your page, you are doing your PhD, right? good luck!! I wish you to publish a lot of papers :stuck_out_tongue:

You’re right, it could just happen in slower computers, but it would generally happen if there were ever a fairly large gap between buffering updates. It’s all mixed and sent to one SourceDataLine, so if you buffer 100ms on the line, the next audio played won’t actually hit the speakers until that buffer is complete. I can’t recall now if I have designed it so that it recovers from long buffers, which would make a 100ms buffer less of an issue.

Thanks! I am working on a PhD, and I guess only time will tell if I can really stick with it. The whole process can be quite a long haul.

[quote]When Java code is executed, it is not immediately compiled.
Instead, when a loop is encountered for the first
time, a few iterations are executed in interpreted mode,
and then only, the code is compiled. This is the HotSpot
feature of the Java Virtual Machine (JVM), which has
been introduced since the release 1.4.

While the speed of compiled Java code is fast enough
for real-time computations [4], the first few iterations that
are executed in interpreted mode can be too slow. As a
result, the computation time can take much more than the
duration of one audio buffer in the first iteration.
[/quote]
From “REAL-TIME, LOW LATENCY AUDIO PROCESSING IN JAVA”
Nicolas Juillerat, Stefan Mueller Arisona, Simon Schubiger-Banz
ETH Zurich
Computer Systems Institute

http://quod.lib.umich.edu/cgi/p/pod/dod-idx?c=icmc;idno=bbp2372.2007.131

A work-around solution is to kick the mixer into gear with a silent tone at an interval of time sufficiently before real-time response is needed, then leave the mixer running for the rest of the game.

P.S. Thanks for making the code for TinySound available on GitHub. I see that what we are doing is pretty similar, in terms of obtaining a SourceDataLine, and that you are getting similar feedback re crackle. Now I can stop stressing about this issue and get on with coding.

Hello,

I’m not new to Java but I have shied away from using audio in my games as I could never find a ‘fully’ reliably and undisputed way of playing and looping sound, efficiently. However, I have recently started writing my own engine (more as a personal project, but I will release it so that it can be used for Ludum Dare) and have finally reached the point of needing audio support. I have been searching around for a suitable method and managed to find ‘java.applet.AudioClip’ that works fairly well, but my question is: What does TinySound do that makes it superior to ‘java.applet.AudioClip’ or what are the shortcomings of ‘java.applet.AudioClip’?

As an aside it would also be useful to hear anyone’s suggestions for how I could approach sound in my engine or whether TinySound would be okay for me to use, from a licence perspective in my engine.

TinySound seems like it could be the best option (if I can use it) but the main thing I’m wondering is whether ‘java.applet.AudioClip’ will do?

Thanks,
Sam (a Java sound ‘noob’)

The thing that drove me from AudioClip was the fact that you have no control over the volume. But if it loops, then it is pretty easy to use and costs very little. You might not have a lot of control over things like starting or stopping in the middle of the sound. I don’t know. I stopped looking at it as soon as I saw there was no volume controls.

Of course there is javax.audio.sampled.Clip, for the same functionality, but with more control (volume, start & stop points).

But, if you are doing something like laser zaps that overlap, you will probably find TinySound a better choice, and well vetted.

Thanks for the help. I think if I was just working on a one off project I would definitely use TinySound, but for an engine it’s probably best to just try and write my own system based on javax.audio.sampled.

Cheers,
Sam

What you’ve got with TinySound as opposed to java.applet.AudioClip or javax.audio.sampled.Clip is a system that opens a single line to the soundcard, keeps it open, and mixes sounds together in software. The way javax.audio.sampled.Clip is implemented means that it opens a separate line to the soundcard every time you want to play a sound. This is OK for occasional sounds in a desktop application, but for anything where you want multiple sounds or rapid triggering then the TinySound approach will work far better.

Why reinvent the wheel? Using the high-level stuff in javax.audio.sampled is not really suitable for games, and using the low-level stuff requires you to have a good grasp of audio coding or you can get into issues quite easily.

I meant to add a couple lines about the pluses of TinySound, but nsigma beat me to it by a few minutes!

I’ll just add that there are some systems (certain flavors of Linux) that seem to have trouble with playing back multiple lines. With javax.sound.sampled, one is usually creating Clips and SourceDataLines that output at the same time (overlapping). A mixer, like the one Kuusisto has built will funnel all the outputs into a single line, allowing the sound to play back correctly on more operating systems.

Hey kuusisto!

Just wanted to thank you for this library!
It’s really easy, like: “Write 3 Lines of Code and the sound will play!”

I love this Library, because i love simple things.

  • Longor1996

Thanks for the clarification everyone. What I’m going to do, based on the most recent replies is to just try to implement TinySound straight into my engine (http://www.mythalore.com/ for more information). I will keep the source intact (within the jar file) and make sure not to imply that the code used is mine. I will probably be making the engine open source from when I release it, near April. Lastly, kuusisto if you have any objections to me packaging the TinySound jar file with my engine please email me at sam@Mythalore.com, as soon as possible. Thank you.

The thread has been inactive for a while but I hope that I can resolve this issue. I’m loading oggs for music in my game and the issue is that loading a single ogg ups the memory usage from 30 to 245 megs. Attempting to load more causes OutOfMemoryError

import java.util.HashMap;

import kuusisto.tinysound.Music;
import kuusisto.tinysound.Sound;
import kuusisto.tinysound.TinySound;

public class Sounder {

	public static HashMap<String, Sound> sounds = new HashMap<String, Sound>();
	public static HashMap<String, Music> musics = new HashMap<String, Music>();
	public static boolean dead;
	public static final int MUSIC_AMOUNT = 3;
	
	public static void init() {
		TinySound.init();
		TinySound.setGlobalVolume(0.1f);
		sounds.put("pickup", TinySound.loadSound("pickup.wav"));
		musics.put("music0", TinySound.loadMusic("music00.ogg"));
		musics.put("music1", TinySound.loadMusic("music01.ogg"));
		musics.put("music2", TinySound.loadMusic("music02.ogg"));
	}
	
	public static void kill() {
		dead = true;
	}
	
	public static void startSoundtrack() {
		Thread stThread = new Thread(new Runnable() {

			@Override
			public void run() {
				int currentSong = Main.rng.nextInt(MUSIC_AMOUNT);
				
				while (!dead) {
					playMusic("music" + currentSong, 1);
					try {
						Thread.sleep((int)(1000 * 7.2f * 60));
					} catch (InterruptedException e) {}
					
					currentSong = Main.rng.nextInt(MUSIC_AMOUNT);
					System.out.println("Switched song");
				}
			}
		});
		
		stThread.start();
	}

	public static void playSound(String sound, float volume) {
		Sound snd = sounds.get(sound);
		if (snd != null)
			snd.play(volume);
	}
	
	public static void playMusic(String sound, float volume) {
		Music snd = musics.get(sound);
		if (snd != null)
			snd.play(false, volume);
	}
}

I decided on posting this issue on this board as well;

I’m using TinySound for playing wavs and oggs. The thread has been inactive for a while but I hope that I can resolve this issue. I’m loading oggs for music in my game and the issue is that loading a single ogg ups the memory usage from 30 to 245 megs. Attempting to load more causes OutOfMemoryError

import java.util.HashMap;

import kuusisto.tinysound.Music;
import kuusisto.tinysound.Sound;
import kuusisto.tinysound.TinySound;

public class Sounder {

	public static HashMap<String, Sound> sounds = new HashMap<String, Sound>();
	public static HashMap<String, Music> musics = new HashMap<String, Music>();
	public static boolean dead;
	public static final int MUSIC_AMOUNT = 3;
	
	public static void init() {
		TinySound.init();
		TinySound.setGlobalVolume(0.1f);
		sounds.put("pickup", TinySound.loadSound("pickup.wav"));
		musics.put("music0", TinySound.loadMusic("music00.ogg"));
		musics.put("music1", TinySound.loadMusic("music01.ogg"));
		musics.put("music2", TinySound.loadMusic("music02.ogg"));
	}
	
	public static void kill() {
		dead = true;
	}
	
	public static void startSoundtrack() {
		Thread stThread = new Thread(new Runnable() {

			@Override
			public void run() {
				int currentSong = Main.rng.nextInt(MUSIC_AMOUNT);
				
				while (!dead) {
					playMusic("music" + currentSong, 1);
					try {
						Thread.sleep((int)(1000 * 7.2f * 60));
					} catch (InterruptedException e) {}
					
					currentSong = Main.rng.nextInt(MUSIC_AMOUNT);
					System.out.println("Switched song");
				}
			}
		});
		
		stThread.start();
	}

	public static void playSound(String sound, float volume) {
		Sound snd = sounds.get(sound);
		if (snd != null)
			snd.play(volume);
	}
	
	public static void playMusic(String sound, float volume) {
		Music snd = musics.get(sound);
		if (snd != null)
			snd.play(false, volume);
	}
}

If I recall, music is loaded into memory where sounds are streamed. Loading a whole song into memory is not bad as long as you only have 1 playing. I recommend playing the songs as sound files instead of music as they should be streamed. Most games stream music. I think SC2 supports up to 128 channels which mean 128 sounds playing at once. Or you can turn it down to 16.

That is not the issue, the issue is songs taking way too much space. 3 6-minute songs take up 400 megs (noticed when I increased heap space) and I find it ridiculous

If it’s what’s normal what can I do to stream music. I’m using LWJGL so I could use OpenAL but setting it up on my own will take very long time and I can’t really find any frameworks to help me with ogg