package com.Eiyeron.SFXRPP.SFXREngine; import java.util.Random; /** * @author Eiyeron * @version 1.00 | Finished * SFXRSynth is the SFXR++ Sound Synth. It's used to generate sound from presets and give SFXRSound as results. */ public class SFXRSynth { private SFXRPreset snd; private boolean playing_sample = false; private int phase; private double fperiod; private double fmaxperiod; private double fslide; private double fdslide; private int period; private double square_duty; private double square_slide; private int env_stage; private int env_time; private int[] env_length = new int[3]; private double env_vol; private double fphase; private double fdphase; private int iphase; private double[] phaser_buffer = new double[1024]; private int ipp; private double[] noise_buffer = new double[32]; private double fltp; private double fltdp; private double fltw; private double fltw_d; private double fltdmp; private double fltphp; private double flthp; private double flthp_d; private double vib_phase; private double vib_speed; private double vib_amp; private int rep_time; private int rep_limit; private int arp_time; private int arp_limit; private double arp_mod; private Random rnd; /** * @param snd Sound to synth. don't synth directly. */ public SFXRSynth(SFXRPreset snd) { this.snd = snd; rnd = new Random(snd.seed); resetSample(false); playing_sample = true; } private double frnd(double v) { return rnd.nextDouble() * v; } private void resetSample(boolean restart) { if (!restart) phase = 0; fperiod = 100.0 / (snd.p_base_freq * snd.p_base_freq + 0.001); period = (int) fperiod; fmaxperiod = 100.0 / (snd.p_freq_limit * snd.p_freq_limit + 0.001); fslide = 1.0 - Math.pow((double) snd.p_freq_ramp, 3.0) * 0.01; fdslide = -Math.pow((double) snd.p_freq_dramp, 3.0) * 0.000001; square_duty = 0.5f - snd.p_duty * 0.5f; square_slide = -snd.p_duty_ramp * 0.00005f; if (snd.p_arp_mod >= 0.0f) arp_mod = 1.0 - Math.pow((double) snd.p_arp_mod, 2.0) * 0.9; else arp_mod = 1.0 + Math.pow((double) snd.p_arp_mod, 2.0) * 10.0; arp_time = 0; arp_limit = (int) (Math.pow(1.0f - snd.p_arp_speed, 2.0f) * 20000 + 32); if (snd.p_arp_speed == 1.0f) arp_limit = 0; if (!restart) { // reset filter fltp = 0.0f; fltdp = 0.0f; fltw = Math.pow(snd.p_lpf_freq, 3.0f) * 0.1f; fltw_d = 1.0f + snd.p_lpf_ramp * 0.0001f; fltdmp = 5.0f / (1.0f + Math.pow(snd.p_lpf_resonance, 2.0f) * 20.0f) * (0.01f + fltw); if (fltdmp > 0.8f) fltdmp = 0.8f; fltphp = 0.0f; flthp = Math.pow(snd.p_hpf_freq, 2.0f) * 0.1f; flthp_d = 1.0 + snd.p_hpf_ramp * 0.0003f; // reset vibrato vib_phase = 0.0f; vib_speed = Math.pow(snd.p_vib_speed, 2.0f) * 0.01f; vib_amp = snd.p_vib_strength * 0.5f; // reset envelope env_vol = 0.0f; env_stage = 0; env_time = 0; env_length[0] = (int) (snd.p_env_attack * snd.p_env_attack * 100000.0f); env_length[1] = (int) (snd.p_env_sustain * snd.p_env_sustain * 100000.0f); env_length[2] = (int) (snd.p_env_decay * snd.p_env_decay * 100000.0f); fphase = Math.pow(snd.p_pha_offset, 2.0f) * 1020.0f; if (snd.p_pha_offset < 0.0f) fphase = -fphase; fdphase = Math.pow(snd.p_pha_ramp, 2.0f) * 1.0f; if (snd.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) (Math.pow(1.0f - snd.p_repeat_speed, 2.0f) * 20000 + 32); if (snd.p_repeat_speed == 0.0f) rep_limit = 0; } } private double synthSample() { if (!playing_sample) return 0.0; rep_time++; if (rep_limit != 0 && rep_time >= rep_limit) { rep_time = 0; // println(this + " resetting sample " + rep_limit + " " + // rep_time); 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 (snd.p_freq_limit > 0.0f) playing_sample = false; } double rfperiod = fperiod; if (vib_amp > 0.0f) { vib_phase += vib_speed; rfperiod = fperiod * (1.0 + Math.sin(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_stage > env_length.length || env_time > env_length[env_stage]) { env_time = 0; env_stage++; // System.out.println("Change of state"); if (env_stage == 3) { playing_sample = false; // System.out.println("End of sound"); } } // println(this + " env_length = " + Arrays.toString(env_length) + // " env_stage: " // + env_stage + " " + playing_sample); if (env_stage == 0) { env_vol = (double) env_time / env_length[0]; } if (env_stage == 1) { env_vol = 1.0f + Math.pow(1.0f - (double) env_time / env_length[1], 1.0f) * 2.0f * snd.p_env_punch; } if (env_stage == 2) { env_vol = 1.0f - (double) 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; } double ssample = 0.0f; for (int si = 0; si < 8; si++) // 8x supersampling { double sample = 0.0f; phase++; if (phase >= period) { // phase=0; phase %= period; if (snd.wave_type == WaveForm.NOISE) { for (int j = 0; j < 32; j++) { noise_buffer[j] = frnd(2.0f) - 1.0f; } } } // base waveform double fp = (double) phase / period; switch (snd.wave_type) { case SQUARE: if (fp < square_duty) sample = 0.5f; else sample = -0.5f; break; case SAWTOOTH: // sawtooth sample = 1.0f - fp * 2; break; case SINE: // sine sample = (double) Math.sin(fp * 2 * Math.PI); break; case NOISE: // noise sample = noise_buffer[phase * 32 / period]; break; case TRIANGLE: sample = Math.abs(1 - fp * 2) - 1; break; case TAN: sample = Math.tan(Math.PI * fp); break; case WHISTLE: sample = 0.75 * Math.sin(fp * 2 * Math.PI) + 0.25 * Math.sin(fp * 2 * 20 * Math.PI); break; case BREAKER: sample = Math.abs(1 - fp * fp * 2) - 1; break; } // lp filter double pp = fltp; fltw *= fltw_d; if (fltw < 0.0f) fltw = 0.0f; if (fltw > 0.1f) fltw = 0.1f; if (snd.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; // println(this + " " + ssample + " in supersampling nr " + si // + " wave_type: " + wave_type + " sample: " + sample // + " env_vol: " + env_vol); // final accumulation and envelope application ssample += sample * env_vol; } ssample = ssample / 8 * snd.master_vol; ssample *= 2.0f * snd.sound_vol; if (ssample > 1.0f) ssample = 1.0f; if (ssample < -1.0f) ssample = -1.0f; return ssample; // } } /** * @return SFXRSound of the SFXRPreset given */ public SFXRSound synthSound() { int length = this.env_length[0] + this.env_length[1] + this.env_length[2]; byte[] barray = new byte[length]; double[] darray = new double[length]; for (int i = 0; i < barray.length; i++) { length = i; double synth = this.synthSample(); barray[i] = (byte) (synth * 127f); darray[i] = synth; } return new SFXRSound(barray, 44100); } }