Procedural Sound

I’ve been looking online for an example off procedural sound. I see how you create the sampled array (google stack overflow how-to-generate-sound-effects-in-java, wont let me post links yet). Bot once I have that array, how do I play it?

Thanks in advance,
John

Simple code to get you started.


      try
      {
         AudioFormat af = new AudioFormat(sampleRate, 16, 1, true, true);
         DataLine.Info info = new DataLine.Info(SourceDataLine.class, af);
         SourceDataLine source = (SourceDataLine) AudioSystem.getLine(info);
         source.open(af);

         // increase master gain to 75%
         Type type = FloatControl.Type.MASTER_GAIN;
         FloatControl fc = (FloatControl) source.getControl(type);
         fc.setValue(fc.getMaximum() * 0.75f);

         source.start();

         while (this.isActive())
         {
              // stuff

              source.write(raw, 0, raw.length);
         }

         // adds 20 bytes :o(
         source.drain();
         source.stop();
         source.close();
      }
      catch (Exception e)
      {
         // System.out.println(e);
      }

Alternatively you can create a Clip, which is convenient for background sound

Try compiling this :slight_smile:


/*
 * SoundTemplate4k.java
 *
 * Created on 29 December 2010
 */

/**
 *
 * @author  Alan Waddington
 *
 */
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.AWTEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;

public class SoundTemplate4k extends Applet implements Runnable {

    private final static int SCREENWIDTH  = 600; // Screen width
    private final static int SCREENHEIGHT = 400; // Screen height

    private final static float RATE       = 16000f;// Music sample rate
    private final static float AMPLITUDE  = 2048f; // Music amplitude

    private byte musicLoop[];           // Array of 16 bit music sound samples
    private Clip music;                 // Clip to play the above music track

    // The following should be pasted in from Java4kSoundEditor
    private final static int maxNote = 730;
    private final static int length = 1018;
    private final static int tempo = 210;
    private final static String notes = "[42,#OB+^*(Un6*xG]]#io[%$\\_H]*eG:+#E7i^ W}T7 A'T7 UTR^ o^3^*)(R^ L@%7*/(E7 6@[7 /tx%$DUC^ 0y ,-)(R^ b|l]*0EX[*kKS^ (K;+#L7\\7 L3s]#/(E7 ='a^ #Un6*R<<^   8%$A[2^*)(R^ S}a^ #Un6*`#5^   8%$Mxw\\ o;'_ o;'_ rS9^*4L/,-qWf*#?rb<\"E$U%$:cP^*P<<^ .YK_ Aq]^*^#5^  # _ /yD^ 5q]^*IEv^ 1yD^ ll@^*3;2^*%8~] F7i^ qX2N!T#OM!LrXq#*LhR&po{]*+8~] qKF7 QzN]#uVT%$'8~] 7:>] .YK_ Bq]^*rDB7*grZ7 o+q7 g_/&$,q]^*-w%_ ^>[] 7Pp^ <r37*Bj\"^#0@h^ =\"+^*DUC^ 6Z{]*`#5^ oxx<\"_mw\\ 44v\\*jF5,#OB+^*(Un6*xG]]#io[%$\\_H]*eG:+#E7i^ W}T7 A'T7 UTR^ o^3^*)(R^ L@%7*/(E7 6@[7 /tx%$DUC^ 0y ,-)(R^ b|l]*0EX[*kKS^ (K;+#L7\\7 L3s]#/(E7 ='a^ #Un6*R<<^   8%$A[2^*)(R^ S}a^ #Un6*`#5^   8%$Mxw\\ o;'_ o;'_ rS9^*4L/,-qWf*#?rb<\"E$U%$:cP^*P<<^ .YK_ Aq]^*^#5^  # _ /yD^ 5q]^*IEv^ 1yD^ ll@^*3;2^*%8~] F7i^ qX2N!T#OM!LrXq#*LhR&po{]*+8~] qKF7 QzN]#uVT%$'8~] 7:>] .YK_ Bq]^*rDB7*grZ7 o+q7 g_/&$,q]^*-w%_ ^>[] 7Pp^ <r37*Bj\"^#0@h^ =\"+^*DUC^ 6Z{]*`#5^ {x^M!tbw\\ 3hE, *HKi -w%_ *$J]*eG:+#E7i^ W}T7 A'T7 UTR^ >X3,#OB+^*(Un6*xG]]#io[%$\\_H]*F7i^ ,HK] L7\\7 L3s]#/(E7 ='a^ s:S\\ fWi^ r*'] kKS^ >An]*\\_H]*l`%7*/(E7 6@[7 /tx%$DUC^ ,xa^ 9M]]#qS:+#F7i^ =\"+^*DUC^ ,xa^ 8M]]#(a2,#]l@^*yl@^*3HK] |\" _ K;u,#LC`6*)1O]#`w^]*tKS^ )+e^*/w%_ vKS^ R?O^*xl@^*ji-^ I'=_ [?O^*xP&^ :Pp^ IG6^ (;2^*%EB7*bYzM!{GeL!*7|y&T2_+#Q}a^ :#`6*c2~] [TE7 p\"D^ O?O^*{*e^*!1-_ +SE] &#r7 v71^#grZ7 btK] I'=_ ]?O^*1\"+^*FUC^ :I(] WZ!&$)(R^ S}a^ 3=eL!Q}a^ $gA<,Ria<\"1RP\\*g3ln7eG:+#E7i^ W}T7 A'T7 UTR^ >X3,#OB+^*(Un6*xG]]#io[%$\\_H]*F7i^ ,HK] L7\\7 L3s]#/(E7 ='a^ s:S\\ fWi^ r*'] kKS^ >An]*\\_H]*l`%7*/(E7 6@[7 /tx%$DUC^ ,xa^ 9M]]#qS:+#F7i^ =\"+^*DUC^ ,xa^ 8M]]#(a2,#]l@^*yl@^*3HK] |\" _ K;u,#LC`6*)1O]#`w^]*tKS^ )+e^*/w%_ vKS^ R?O^*xl@^*ji-^ I'=_ [?O^*xP&^ :Pp^ IG6^ (;2^*%EB7*bYzM!{GeL!*7|y&T2_+#Q}a^ :#`6*c2~] [TE7 p\"D^ O?O^*{*e^*!1-_ +SE] &#r7 v71^#grZ7 btK] I'=_ ]?O^*1\"+^*FUC^ :I(] WZ!&$)(R^ S}a^ 3=eL!Q}a^ zq'M+^iGM!BZl!*SwR, )d;_ Pb|[*";

    private final static int    TRIANGLEHARMONICS = 7;    // Number of harmonics
    private final static double TRIANGLEAMPLITUDE = 0.5;  // Triangle Volume
    private final static double SINAMPLITUDE      = 0.2;  // Sinewave Volume
    private final static int    ATTACK            = 1000; // Note attack
    private final static int    DECAY             = 1000; // Note decay

    @Override
    public void start() {
        enableEvents(AWTEvent.KEY_EVENT_MASK);
        new Thread(this).start();
    }

    public void run() {

        //
        //  Initialise and start background music
        //

        // Create array of music frequencies
        int i, j, k;
        double[] freq = new double[100];
        double f = 16.351562;        // Frequency of C0 (C4 is middle C)
        for (i=0; i<7*12; i++) {
            freq[i] = f;
            f *= 1.0594630944;       // Evenly tempered scale (12th root 2)
        }

        // Create an array of bytes to store the background music
        musicLoop = new byte[2*(int)( length*15f/tempo*RATE )];

        // Read each note and render it into the loop
        int semiQuaver = 0;             // Start time for next note
        int byte0, byte1, twoNotes=0;
        for (i=0; i<maxNote; i++) {
            if ((i & 1)==0) {
                // Decode 5 chars to 1 int
                twoNotes = 0;
                for (j=4;j>=0;j--)
                    twoNotes = twoNotes*95+(int)notes.charAt(5*(i>>1)+j)-0x20;
            }
            byte0 = twoNotes & 0xff;        // Frequency
            byte1 = twoNotes>>8 & 0xff;     // Delay & Duration
            twoNotes=twoNotes>>16;
            double frequency = freq[byte0 & 0x7f];
            if (byte0 >127) // If bit7 is set, there is a delay before note
                semiQuaver += (byte1>>4 & 0x0f) + 1;    // Note start time
            int duration = (byte1 & 0x0f) + 1;          // Note duration
            // Calculate start and stop times in terms of samples
            int start = (int)( (semiQuaver)*15f/tempo*RATE );
            int stop  = (int)( (semiQuaver+duration)*15f/tempo*RATE );

            // Calculate each sample and render it into the music loop
            double sample;
            for (j=start; j<stop; j++) {
                double time = (j-start)/RATE;   // Time (seconds)
                // Triangle fundamental + sinusoidal 4th and 6th harmonics

                sample = 0.0;
                for (k=1; k<=TRIANGLEHARMONICS; k+=2) {
                    f = frequency*k;
                    if (f<RATE/2.0) // Nyquist frequency
                        sample += Math.sin(Math.PI*f*time*2.0)/(k*k);
                }
                sample = sample*TRIANGLEAMPLITUDE
                       + (Math.sin(Math.PI*frequency*time*8.0)
                       +  Math.sin(Math.PI*frequency*time*12.0))
                       *  SINAMPLITUDE;

                // Apply attack and decay to avoid clicks
                if (j-start<ATTACK) sample *= (j-start)/(float)ATTACK;
                if (stop-j<DECAY) sample *= (stop-j)/(float)DECAY;

                // Add the 16 bit sample to loop
                int data = (musicLoop[2*j]<<8)+(musicLoop[2*j+1] & 0xff);
                data += (int)(AMPLITUDE*sample);
                musicLoop[2*j+1] = (byte)(data & 0xff);
                musicLoop[2*j] = (byte)((data>>8) & 0xff);
            }
        }
        try {
            // Initialise Sound System
            AudioFormat audioFormat = new AudioFormat(RATE, 16, 1, true, true);
            DataLine.Info info = new DataLine.Info(Clip.class, audioFormat);
            music = (Clip)AudioSystem.getLine(info);
            music.open(audioFormat, musicLoop, 0, musicLoop.length);
            music.loop(Clip.LOOP_CONTINUOUSLY);
        } catch (Exception e) {}

        //
        // End of Music Section
        //

        BufferedImage screen = new BufferedImage(
                SCREENWIDTH, SCREENHEIGHT, BufferedImage.TYPE_INT_RGB);

        while(!isActive()) Thread.yield();
        Graphics g = getGraphics();
        requestFocus();
        do {

            if (g!=null) {
                g.drawImage(screen, 0,0,null);
            }
            Thread.yield();
        } while (isActive());
        music.stop(); // Required to stop music when applet is not in foreground
    }

    /** Process Key Event */
    @Override
    protected void processKeyEvent(KeyEvent e) {
        // Key Handler ...
    }

}


I tried this last night and looks good (SoundTemplate4K). Is there any explanation anywhere on how to create new songs… The notes string/code looks too complex for my simple ‘lazy’ mind to decipher :slight_smile:
Also, have you tried this within a game, ie… Does the sound cause the game/graphics to stutter once you implement your game logic.

/* Music Data

  • Each note is a short
  • Bits 0-6: Note in semitones in range C0-B7 (C4 is middle C)
  • Bit 7: This note starts at the same time as the previous note if clear
  • Bits 8-11: Length of this note (1-8) in semiquavers [Value stored 0-7]
  • Bits 12-15: Delay in semi-quavers (1-8) from start of previous note to
  • start of this note [Value stored 0-7]
  • Two shorts are packed into each integer, the first note in the low short
    */

Each integer is then encoded into 5 chars and added to the string. Don’t forget to escape ‘’ and ‘"’ for pasting into your source code.


                    n = integer representing two notes
                    for (int k=0;k<5;k++) {
                        char ch = (char)(n - 95*(int)(n/95)+ 0x20);
                        if (ch=='"' || ch=='\\')
                            s += '\\';
                        s += ch;
                        n /=95;
                    }

I wrote a program to convert MIDI files, but it is a bit quirky, so not really releaseable.

Thank you all, this will undoubtedly come in handy.

Thanks Alan, Very helpful. Your template doesn’t appear to use double buffering (I think appel pointed this out last year too), but was easily fixed, other than this looks good for music. Will see!