SFX Audio on Android

28 June 2011

Once upon the time all game audio was synthesized. We were amazed with the chips that would come out from the ZX Spectrum or the commodore 64 but nowadays we can still make cool explosion sounds for small devices such as the android mobile phones.

I will not diverge into much details on audio synthetising since I am not an expert myself. If you want information about that you can look here:

So i implemented a simple SFX util class based on an old project (but very useful: http://www.drpetter.se/project_sfxr.html) to generate sounds for Android games. The code is the following:

package com.jetdrone.and2d.sfx; import java.util.Random; import android.util.FloatMath; public class SFXR {   // 0: square, 1: sawtooth, 2: sine, 3: noise   private int wave_type;   private float p_base_freq; // Start Frequency   private float p_freq_limit; // Min Frequency   private float p_freq_ramp; // Slide   private float p_freq_dramp; // Delta Slide   private float p_duty; // Square Duty   private float p_duty_ramp; // Duty Sweep   private float p_vib_strength; // Vibrato Depth   private float p_vib_speed; // Vibrato Speed   private float p_vib_delay; // ???   private float p_env_attack; // Attack Time   private float p_env_sustain; // Sustain Time   private float p_env_decay; // Decay Time   private float p_env_punch; // Sustain Punch   boolean filter_on; // ???   private float p_lpf_resonance; // LP Filter Resonance   private float p_lpf_freq; // LP Filter Cutoff   private float p_lpf_ramp; // LP Filter Cutoff Sweep   private float p_hpf_freq; // HP Filter Cutoff   private float p_hpf_ramp; // HP Filter Cutoff Sweep   private float p_pha_offset; // Phaser Offset   private float p_pha_ramp; // Phaser Sweep   private float p_repeat_speed; // Repeat Speed   private float p_arp_speed; // Change Speed   private float p_arp_mod; // Change Amount   private float master_vol = 0.05f;   private float sound_vol = 0.5f;   private boolean playing_sample = false;   private int phase;   private float fperiod;   private float fmaxperiod;   private float fslide;   private float fdslide;   private int period;   private float square_duty;   private float square_slide;   private int env_stage;   private int env_time;   private int[] env_length = new int[3];   private float env_vol;   private float fphase;   private float fdphase;   private int iphase;   private float[] phaser_buffer = new float[1024];   private int ipp;   private float[] noise_buffer = new float[32];   private float fltp;   private float fltdp;   private float fltw;   private float fltw_d;   private float fltdmp;   private float fltphp;   private float flthp;   private float flthp_d;   private float vib_phase;   private float vib_speed;   private float vib_amp;   private int rep_time;   private int rep_limit;   private int arp_time;   private int arp_limit;   private float arp_mod;   private final Random rnd = new Random();   public void init(long seed) {     rnd.setSeed(seed);     resetParams();     playing_sample = true;   }   public void init() {     resetParams();     playing_sample = true;   }      private final float frnd(float v) {     return rnd.nextFloat() * v;   }      private static final float fpow(float a, float b) {     return (float) Math.pow(a, b);   }      private static final float fsin(float a) {     return FloatMath.sin(a);   }   private final int rnd(int v) {     return rnd.nextInt(v+1);   }   public void resetParams() {     wave_type = 0;     p_base_freq = 0.3f;     p_freq_limit = 0.0f;     p_freq_ramp = 0.0f;     p_freq_dramp = 0.0f;     p_duty = 0.0f;     p_duty_ramp = 0.0f;     p_vib_strength = 0.0f;     p_vib_speed = 0.0f;     p_vib_delay = 0.0f;     p_env_attack = 0.0f;     p_env_sustain = 0.3f;     p_env_decay = 0.4f;     p_env_punch = 0.0f;     filter_on = false;     p_lpf_resonance = 0.0f;     p_lpf_freq = 1.0f;     p_lpf_ramp = 0.0f;     p_hpf_freq = 0.0f;     p_hpf_ramp = 0.0f;     p_pha_offset = 0.0f;     p_pha_ramp = 0.0f;     p_repeat_speed = 0.0f;     p_arp_speed = 0.0f;     p_arp_mod = 0.0f;   }   public void resetSample(boolean restart) {     if (!restart)       phase = 0;     fperiod = 100.0f / (p_base_freq * p_base_freq + 0.001f);     period = (int) fperiod;     fmaxperiod = 100.0f / (p_freq_limit * p_freq_limit + 0.001f);     fslide = 1.0f - fpow(p_freq_ramp, 3.0f) * 0.01f;     fdslide = -fpow(p_freq_dramp, 3.0f) * 0.000001f;     square_duty = 0.5f - p_duty * 0.5f;     square_slide = -p_duty_ramp * 0.00005f;     if (p_arp_mod >= 0.0f)       arp_mod = 1.0f - fpow(p_arp_mod, 2.0f) * 0.9f;     else       arp_mod = 1.0f + fpow(p_arp_mod, 2.0f) * 10.0f;     arp_time = 0;     arp_limit = (int) (fpow(1.0f - p_arp_speed, 2.0f) * 20000 + 32);     if (p_arp_speed == 1.0f)       arp_limit = 0;     if (!restart) {       // reset filter       fltp = 0.0f;       fltdp = 0.0f;       fltw = fpow(p_lpf_freq, 3.0f) * 0.1f;       fltw_d = 1.0f + p_lpf_ramp * 0.0001f;       fltdmp = 5.0f / (1.0f + fpow(p_lpf_resonance, 2.0f) * 20.0f)           * (0.01f + fltw);       if (fltdmp > 0.8f)         fltdmp = 0.8f;       fltphp = 0.0f;       flthp = fpow(p_hpf_freq, 2.0f) * 0.1f;       flthp_d = 1.0f + p_hpf_ramp * 0.0003f;       // reset vibrato       vib_phase = 0.0f;       vib_speed = fpow(p_vib_speed, 2.0f) * 0.01f;       vib_amp = p_vib_strength * 0.5f;       // reset envelope       env_vol = 0.0f;       env_stage = 0;       env_time = 0;       env_length[0] = (int) (p_env_attack * p_env_attack * 100000.0f);       env_length[1] = (int) (p_env_sustain * p_env_sustain * 100000.0f);       env_length[2] = (int) (p_env_decay * p_env_decay * 100000.0f);       fphase = fpow(p_pha_offset, 2.0f) * 1020.0f;       if (p_pha_offset < 0.0f)         fphase = -fphase;       fdphase = fpow(p_pha_ramp, 2.0f) * 1.0f;       if (p_pha_ramp < 0.0f)         fdphase = -fdphase;       iphase = Math.abs((int) fphase);       ipp = 0;       for (int i = 0; i < 1024; i++)         phaser_buffer[i] = 0.0f;       for (int i = 0; i < 32; i++)         noise_buffer[i] = frnd(2.0f) - 1.0f;       rep_time = 0;       rep_limit = (int) (fpow(1.0f - p_repeat_speed, 2.0f) * 20000 + 32);       if (p_repeat_speed == 0.0f)         rep_limit = 0;     }   }   public float synthSample() {     if (!playing_sample)       return 0.0f;     rep_time++;     if (rep_limit != 0 && rep_time >= rep_limit) {       rep_time = 0;       resetSample(true);     }     // frequency envelopes/arpeggios     arp_time++;     if (arp_limit != 0 && arp_time >= arp_limit) {       arp_limit = 0;       fperiod *= arp_mod;     }     fslide += fdslide;     fperiod *= fslide;     if (fperiod > fmaxperiod) {       fperiod = fmaxperiod;       if (p_freq_limit > 0.0f)         playing_sample = false;     }     float rfperiod = fperiod;     if (vib_amp > 0.0f) {       vib_phase += vib_speed;       rfperiod = fperiod * (1.0f + fsin(vib_phase) * vib_amp);     }     period = (int) rfperiod;     if (period < 8)       period = 8;     square_duty += square_slide;     if (square_duty < 0.0f)       square_duty = 0.0f;     if (square_duty > 0.5f)       square_duty = 0.5f;     // volume envelope     env_time++;     if (env_time > env_length[env_stage]) {       env_time = 0;       env_stage++;       if (env_stage == 3)         playing_sample = false;     }     if (env_stage == 0) {       env_vol = (float) env_time / env_length[0];     }     if (env_stage == 1) {       env_vol = 1.0f           + fpow(1.0f - (float) env_time / env_length[1], 1.0f)           * 2.0f * p_env_punch;     }     if (env_stage == 2) {       env_vol = 1.0f - (float) env_time / env_length[2];     }     // phaser step     fphase += fdphase;     iphase = Math.abs((int) fphase);     if (iphase > 1023)       iphase = 1023;     if (flthp_d != 0.0f) {       flthp *= flthp_d;       if (flthp < 0.00001f)         flthp = 0.00001f;       if (flthp > 0.1f)         flthp = 0.1f;     }     float ssample = 0.0f;     for (int si = 0; si < 8; si++) // 8x supersampling     {       float sample = 0.0f;       phase++;       if (phase >= period) {         // phase=0;         phase %= period;         if (wave_type == 3) {           for (int j = 0; j < 32; j++) {             noise_buffer[j] = frnd(2.0f) - 1.0f;           }         }       }       // base waveform       float fp = (float) phase / period;       switch (wave_type) {       case 0: // square         if (fp < square_duty)           sample = 0.5f;         else           sample = -0.5f;         break;       case 1: // sawtooth         sample = 1.0f - fp * 2;         break;       case 2: // sine         sample = fsin(fp * 2 * (float) Math.PI);         break;       case 3: // noise         sample = noise_buffer[phase * 32 / period];         break;       }       // lp filter       float pp = fltp;       fltw *= fltw_d;       if (fltw < 0.0f)         fltw = 0.0f;       if (fltw > 0.1f)         fltw = 0.1f;       if (p_lpf_freq != 1.0f) {         fltdp += (sample - fltp) * fltw;         fltdp -= fltdp * fltdmp;       } else {         fltp = sample;         fltdp = 0.0f;       }       fltp += fltdp;       // hp filter       fltphp += fltp - pp;       fltphp -= fltphp * flthp;       sample = fltphp;       // phaser       phaser_buffer[ipp & 1023] = sample;       sample += phaser_buffer[(ipp - iphase + 1024) & 1023];       ipp = (ipp + 1) & 1023;       // final accumulation and envelope application       ssample += sample * env_vol;     }     ssample = ssample / 8 * master_vol;     ssample *= 2.0f * sound_vol;     if (ssample > 1.0f)       ssample = 1.0f;     if (ssample < -1.0f)       ssample = -1.0f;     return ssample;     // }   }   public void random(int i) {     switch (i) {     case 0: // pickup/coin       resetParams();       p_base_freq = 0.4f + frnd(0.5f);       p_env_attack = 0.0f;       p_env_sustain = frnd(0.1f);       p_env_decay = 0.1f + frnd(0.4f);       p_env_punch = 0.3f + frnd(0.3f);       if (rnd(1) > 0) {         p_arp_speed = 0.5f + frnd(0.2f);         p_arp_mod = 0.2f + frnd(0.4f);       }       break;     case 1: // laser/shoot       resetParams();       wave_type = rnd(2);       if (wave_type == 2 && rnd(1) > 0)         wave_type = rnd(1);       p_base_freq = 0.5f + frnd(0.5f);       p_freq_limit = p_base_freq - 0.2f - frnd(0.6f);       if (p_freq_limit < 0.2f)         p_freq_limit = 0.2f;       p_freq_ramp = -0.15f - frnd(0.2f);       if (rnd(2) == 0) {         p_base_freq = 0.3f + frnd(0.6f);         p_freq_limit = frnd(0.1f);         p_freq_ramp = -0.35f - frnd(0.3f);       }       if (rnd(1) > 0) {         p_duty = frnd(0.5f);         p_duty_ramp = frnd(0.2f);       } else {         p_duty = 0.4f + frnd(0.5f);         p_duty_ramp = -frnd(0.7f);       }       p_env_attack = 0.0f;       p_env_sustain = 0.1f + frnd(0.2f);       p_env_decay = frnd(0.4f);       if (rnd(1) > 0)         p_env_punch = frnd(0.3f);       if (rnd(2) == 0) {         p_pha_offset = frnd(0.2f);         p_pha_ramp = -frnd(0.2f);       }       if (rnd(1) > 0)         p_hpf_freq = frnd(0.3f);       break;     case 2: // explosion       resetParams();       wave_type = 3;       if (rnd(1) > 0) {         p_base_freq = 0.1f + frnd(0.4f);         p_freq_ramp = -0.1f + frnd(0.4f);       } else {         p_base_freq = 0.2f + frnd(0.7f);         p_freq_ramp = -0.2f - frnd(0.2f);       }       p_base_freq *= p_base_freq;       if (rnd(4) == 0)         p_freq_ramp = 0.0f;       if (rnd(2) == 0)         p_repeat_speed = 0.3f + frnd(0.5f);       p_env_attack = 0.0f;       p_env_sustain = 0.1f + frnd(0.3f);       p_env_decay = frnd(0.5f);       if (rnd(1) == 0) {         p_pha_offset = -0.3f + frnd(0.9f);         p_pha_ramp = -frnd(0.3f);       }       p_env_punch = 0.2f + frnd(0.6f);       if (rnd(1) > 0) {         p_vib_strength = frnd(0.7f);         p_vib_speed = frnd(0.6f);       }       if (rnd(2) == 0) {         p_arp_speed = 0.6f + frnd(0.3f);         p_arp_mod = 0.8f - frnd(1.6f);       }       break;     case 3: // powerup       resetParams();       if (rnd(1) > 0)         wave_type = 1;       else         p_duty = frnd(0.6f);       if (rnd(1) > 0) {         p_base_freq = 0.2f + frnd(0.3f);         p_freq_ramp = 0.1f + frnd(0.4f);         p_repeat_speed = 0.4f + frnd(0.4f);       } else {         p_base_freq = 0.2f + frnd(0.3f);         p_freq_ramp = 0.05f + frnd(0.2f);         if (rnd(1) > 0) {           p_vib_strength = frnd(0.7f);           p_vib_speed = frnd(0.6f);         }       }       p_env_attack = 0.0f;       p_env_sustain = frnd(0.4f);       p_env_decay = 0.1f + frnd(0.4f);       break;     case 4: // hit/hurt       resetParams();       wave_type = rnd(2);       if (wave_type == 2)         wave_type = 3;       if (wave_type == 0)         p_duty = frnd(0.6f);       p_base_freq = 0.2f + frnd(0.6f);       p_freq_ramp = -0.3f - frnd(0.4f);       p_env_attack = 0.0f;       p_env_sustain = frnd(0.1f);       p_env_decay = 0.1f + frnd(0.2f);       if (rnd(1) > 0)         p_hpf_freq = frnd(0.3f);       break;     case 5: // jump       resetParams();       wave_type = 0;       p_duty = frnd(0.6f);       p_base_freq = 0.3f + frnd(0.3f);       p_freq_ramp = 0.1f + frnd(0.2f);       p_env_attack = 0.0f;       p_env_sustain = 0.1f + frnd(0.3f);       p_env_decay = 0.1f + frnd(0.2f);       if (rnd(1) > 0)         p_hpf_freq = frnd(0.3f);       if (rnd(1) > 0)         p_lpf_freq = 1.0f - frnd(0.6f);       break;     case 6: // blip/select       resetParams();       wave_type = rnd(1);       if (wave_type == 0)         p_duty = frnd(0.6f);       p_base_freq = 0.2f + frnd(0.4f);       p_env_attack = 0.0f;       p_env_sustain = 0.1f + frnd(0.1f);       p_env_decay = frnd(0.2f);       p_hpf_freq = 0.1f;       break;     default:       break;     }   }   public void randomize() {     p_base_freq = fpow(frnd(2.0f) - 1.0f, 2.0f);     if (rnd(1) > 0)       p_base_freq = fpow(frnd(2.0f) - 1.0f, 3.0f) + 0.5f;     p_freq_limit = 0.0f;     p_freq_ramp = fpow(frnd(2.0f) - 1.0f, 5.0f);     if (p_base_freq > 0.7f && p_freq_ramp > 0.2f)       p_freq_ramp = -p_freq_ramp;     if (p_base_freq < 0.2f && p_freq_ramp < -0.05f)       p_freq_ramp = -p_freq_ramp;     p_freq_dramp = fpow(frnd(2.0f) - 1.0f, 3.0f);     p_duty = frnd(2.0f) - 1.0f;     p_duty_ramp = fpow(frnd(2.0f) - 1.0f, 3.0f);     p_vib_strength = fpow(frnd(2.0f) - 1.0f, 3.0f);     p_vib_speed = frnd(2.0f) - 1.0f;     p_vib_delay = frnd(2.0f) - 1.0f;     p_env_attack = fpow(frnd(2.0f) - 1.0f, 3.0f);     p_env_sustain = fpow(frnd(2.0f) - 1.0f, 2.0f);     p_env_decay = frnd(2.0f) - 1.0f;     p_env_punch = fpow(frnd(0.8f), 2.0f);     if (p_env_attack + p_env_sustain + p_env_decay < 0.2f) {       p_env_sustain += 0.2f + frnd(0.3f);       p_env_decay += 0.2f + frnd(0.3f);     }     p_lpf_resonance = frnd(2.0f) - 1.0f;     p_lpf_freq = 1.0f - fpow(frnd(1.0f), 3.0f);     p_lpf_ramp = fpow(frnd(2.0f) - 1.0f, 3.0f);     if (p_lpf_freq < 0.1f && p_lpf_ramp < -0.05f)       p_lpf_ramp = -p_lpf_ramp;     p_hpf_freq = fpow(frnd(1.0f), 5.0f);     p_hpf_ramp = fpow(frnd(2.0f) - 1.0f, 5.0f);     p_pha_offset = fpow(frnd(2.0f) - 1.0f, 3.0f);     p_pha_ramp = fpow(frnd(2.0f) - 1.0f, 3.0f);     p_repeat_speed = frnd(2.0f) - 1.0f;     p_arp_speed = frnd(2.0f) - 1.0f;     p_arp_mod = frnd(2.0f) - 1.0f;   }   public void mutate() {

 

    if (rnd(1) > 0)       p_base_freq += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_freq_ramp += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_freq_dramp += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_duty += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_duty_ramp += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_vib_strength += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_vib_speed += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_vib_delay += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_env_attack += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_env_sustain += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_env_decay += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_env_punch += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_lpf_resonance += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_lpf_freq += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_lpf_ramp += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_hpf_freq += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_hpf_ramp += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_pha_offset += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_pha_ramp += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_repeat_speed += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_arp_speed += frnd(0.1f) - 0.05f;     if (rnd(1) > 0)       p_arp_mod += frnd(0.1f) - 0.05f;   } }

This class lets you generate sounds, but this is not enough since you also wants to listen to them. For this i created another helper class (AudioClip) that will let you play the sound on your Android device:

package com.jetdrone.and2d; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; public class AudioClip {      private AudioTrack track;   private int sampleRateInHz = 22050;   private short[] audioData;      public AudioClip(int sampleRateInHz) {     this.sampleRateInHz = sampleRateInHz;   }      public AudioClip(int sampleRateInHz, short[] audioData) {     this.sampleRateInHz = sampleRateInHz;     setData(audioData);   }      public void setData(short[] audioData) {          if(track != null) {       track.release();     }          final int bufSize = Math.max(sampleRateInHz, audioData.length);     this.audioData = new short[bufSize];          track = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, bufSize, AudioTrack.MODE_STATIC);          if(track.getState() == AudioTrack.STATE_NO_STATIC_DATA) {       System.arraycopy(audioData, 0, this.audioData, 0, audioData.length);       track.write(audioData, 0, audioData.length);     }   }      public void play() {     if(track != null) {       track.play();       track.stop();       track.reloadStaticData();     }   }      public void release() {     if(track != null) {       track.release();     }   }      @Override   public void finalize() throws Throwable {     release();     super.finalize();   } }

Now you can generate your sound in background and play the in the foreground, pretty neat! :)

Related tags: sfx audio android code 

Comments

Post a comment