Because I'm not a fan of 8-bit music (whatever conclusions you may have drawn from me posting the link above), I've been spending a lot of time working on compact softsynth. (Need to spend a bit more time on the actual game!) Here for anyone to copy, modify, etc. is a standalone class which generates a snare drum patch and plays it once.

Here's another micro-sound-system that just plays an ugly beep. However, I guess it's pretty much the smallest that it can get (I chose some other features instead of sound, though). Maybe it's useful for someone as a starting point!

@Grunntyour code looks clean and simple. however if I want to change the sound, which part should be modified? amp?

Nah, you will need a bit more creativity If you want a different sound you need to fill the audioData buffer with a different pattern, in the for-loop below the "// Generate a simplistic beep" comment. Most other solutions out there actually make it easy to generate different sounds, this one is just to show the most minimal possible version.

If you don't mind 8-bit sound, below is a class I wrote to test my sound generation. Note that it uses basic sine waves; I tried a few other wave forms, but either made mistakes with them or else they didn't sound very good. I coded it to use the same frequencies as the Western standard (A440), so each note maps directly to a standard piano key (including sharps/flats.) So Middle C is note -9. The generateNote() method generates a given note of a given duration and appends it to the byte array. It also extends the array as needed, so you can write very small code to generate a lot of notes very easily. Notice the two commented out examples, one showing a simple three note sequence, and the other showing a repeating pattern that increases frequency and then in tempo.

The default, uncommented code uses another method I wrote, parseMusicStream(), to parse a byte stream in a format I created for the Java4K competition. In it, each note is represented by 1 byte, with the top two bytes representing the duration (shifted up by one, it gives you a range of 1-4) and the bottom 6 bytes representing the note (giving you a range of 0 to 62, with 63 reserved for a Rest note.) Those bytes compress really well in the final jar, probably because of repeated notes and repeated sequences within the music (eg: a chorus.) I've got a second program that takes a comma-separated list of notes in {duration}{octave}{note} format and outputs the notes encoded in the previous byte format. So I can write music like this:

14C,13G,14Eb,24C,00R,14A#,00R,14C,

And have those notes encoded into just 8 bytes before compression. I'll paste the encoder code in another comment.

// Incoming durations are between 0 and 3// - The code shifts them to be between 1 and 4// - This multiple is how many seconds a duration of 1 represents.privatestaticfinaldoubleDURATION_MULTIPLE = 0.2;

/* e.g.: * Load the music from a file (each byte is a note: top two bits are the duration, bottom 6 bits are the note to play.) */InputStreamstream = this.getClass().getResourceAsStream("MusicBoxDancer.music");pcmMusic = parseMusicStream(stream);

/** * Generates a new note, and appends it to the original array. * * @param originalArray An array of bytes representing pcm music (can be empty/zero length) * @param note The note to generate. There are 12 notes per octave: -9 = middle C, -8 = C#/Db, -7 = D, etc. * @param duration How long to play the note, in seconds. * @return A new byte array containing the originalArray followed by the pcm bytes for the new note. */privatebyte[] generateNote(byte[] originalArray, intnote, doubleduration){

intdurationInSamples = (int) Math.ceil(duration * SAMPLE_RATE) + 1;

byte[] noteArray = newbyte[durationInSamples * 2];

doubletime = 0;inti = 0;

// Put in a pause if this note is the max valueif (note == 63 + NOTE_SHIFT) {i = durationInSamples;

/** * Parses the bytes of an InputStream and returns a byte array containing the represented * notes in pcm format. * * - The InputStream must be in "ca.townsends.music" format, where each byte represents a note and its duration. * - For each byte, the top 2 bits are the duration, the bottom 6 bits are the note to play. * - The duration is multiplied by the duration multiple and shifted up by 0.1. So a duration of 0 = 0.1, 1 = 0.1 + (duration * multiple), etc. * - The note is shifted up by NOTE_SHIFT, which lets you adjust the note range up or down as desired. For example, * to shift the music being parsed up by an octave, just increase NOTE_SHIFT by 12 (the number of notes in an octave.) * * See ca.townsends.games.templates.EncodeMusic for a "music" encoder. * * @param stream An InputStream in "music" format. * * @return A byte array with the parsed music in pcm format. */privatebyte[] parseMusicStream (InputStreamstream) {byte[] music = newbyte[0];

As promised, my EncodeMusic class, which generates the "music" format that Sound4K plays. I've tried to comment it so that you can understand what's going on, but you're welcome ask questions or just use it without understanding.

/** * Takes a csv-like file of notes in {duration}{octave}{note} format, and outputs one byte per note. The input * ignores all whitespace, including linefeeds and carriage returns, so you can group music as you please. For example, * the first few stanzas of the song "Music Box Dancer" are: * * 13C,13E,13G,13E,14C,13G,13E,13G, * 13C,13E,13G,13E,14C,13G,13E,13G, * 13C,13E,13G,13E,14C,13G,13E,13G, * 13C,13E,13G,13E,14C,13G,13E,13G, * * 14C,13G,14C,14E,14C,14E,14G,14C, * 15C,15B,15A,34G,20R, * 14G,14F,14D,14B,13G,14C,14D,14F, * 14E,14C,15A,34G,20R, * 14C,13G,14C,14E,14C,14E,14G,14C, * 15C,15B,15A,34G,20R, * * Note that the input accepts notes in any mixed-case, and recognizes both sharps * and flats (e.g.: "A", "C#" and "Db" are all valid notes within an octave.) "R" is used for a Rest note. * * I've named the output format "ca.townsends.music", with a ".music" extension, but that's purely arbitrary. * * * @author Rick Townsend * @version 1.1 * 2012-02-25 */publicclassEncodeMusic {

// If we get an IO exception at any time, there's no point in continuing.try {// You could change these to be parameters from the args[] array, instead of hard-coded.inStream = EncodeMusic.class.getResourceAsStream("MusicBoxDancer.csv");outStream = newFileOutputStream("src/ca/townsends/games/templates/outputMusic.music");byte[] bytes = newbyte[4096 * 4]; // 4K notes is a very, very long song... But if you overrun it, just make this bigger.

// Grab all the bytes from the fileinStream.read(bytes);

// Dump it to a String so we can parse it more easilyStringdata = (newString(bytes)).toUpperCase();

// Skip any empty values (ie, where the input was only whitespace between two commas, or simply ",,")if (token.length() == 0) continue;

// Grab the duration, octave and noteintinDuration = Integer.parseInt(token.substring(0, 1));intinOctave = Integer.parseInt(token.substring(1, 2));StringinNote = token.substring(2); // This grabs all text from the third character onward, so it will get both single ("C") and double ("C#) character notes

//System.out.println(inNote);// Grab the notes value from the mapintnoteVal = noteMap.get(inNote);

java-gaming.org is not responsible for the content posted by its members, including references to external websites,
and other references that may or may not have a relation with our primarily
gaming and game production oriented community.
inquiries and complaints can be sent via email to the info‑account of the
company managing the website of java‑gaming.org