SFXR

Post screenshots, binaries and projectfiles of the projects you have made with ZGE that you want to share!

Moderator: Moderators

User avatar
Kjell
Posts: 1910
Joined: Sat Feb 23, 2008 11:15 pm

SFXR

Post by Kjell »

8)

Port of DrPetter's sfxr for use with the Sample Component.

Image

This library is meant as a replacement to exporting / importing a .wav file from sfxr into ZGE, not as real-time synthesizer!

Instructions

- Load a .sfs file using FileAction.
- Call SampleLength() and set your Sample accordingly.
- Call ResetSample() to initialize the Synthesizer.
- Call SynthSample() to render the sample to the buffer*
- Use a SampleExpression to copy the buffer to a Sample.

Attached file contains the library plus a demo scene which loads + renders Sample.sfs when pressing "R" and plays the sample when pressing "S".

*The quality argument of SynthSample has to be in the 0-3 range ( 0 being lowest, 3 be highest ).

K
Attachments
SFXR.zip
(3.82 KiB) Downloaded 1105 times
User avatar
jph_wacheski
Posts: 1005
Joined: Sat Feb 16, 2008 8:10 pm
Location: Canada
Contact:

Post by jph_wacheski »

Wow, nice one!

This is quite cool,.

what is the noteNr to use if i want to pitch the sounds in playback, I cant seem to find the base note?
iterationGAMES.com
User avatar
Kjell
Posts: 1910
Joined: Sat Feb 23, 2008 11:15 pm

Post by Kjell »

:)

Thanks jph. Ville will try to debug the script in order to see how we can speed it up some more. Let me know when you come across any problems* Anyway, the Base Note is around 148.7656.

*Other then some of the delta parameters not working at low values ( this is because ZGE doesn't support doubles ).

K
getter77
Posts: 5
Joined: Wed Apr 06, 2011 9:40 pm
Location: GA,USA

Post by getter77 »

Bumping with the notion, or at least the tip of the hat, that another project that takes this a tad further than the base one has come into being, bfxr.

http://www.bfxr.net/

Handy/potential for further/renewed doings?
Champion of Roguelikes, living voraciously, trying to wrap my head around game creation.
User avatar
Ats
Posts: 702
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: SFXR

Post by Ats »

I quickly updated Kjell's example, as ZGE isn't happy anymore with names being the same as components File:File, Sound:Sound and Sample:Sample. Weirdly enough, KeyPress:KeyPress isn't an issue.

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="SFXR" CustomScreenWidth="320" CustomScreenHeight="240" MouseVisible="255" FileVersion="2">
  <OnLoaded>
    <ZLibrary Comment="SFXR">
      <Source>
<![CDATA[//

float SampleLength()
{
  return (p_env_attack*p_env_attack+
          p_env_sustain*p_env_sustain+
          p_env_decay*p_env_decay)*100000;
}

//

void ResetSample(int restart)
{
  // Base

  if(!restart)phase = 0;

  period = 100/(p_base_freq*p_base_freq+0.001);
  fperiod = period;
  fmaxperiod = 100/(p_freq_limit*p_freq_limit+0.001);

  fslide = 1-pow(p_freq_ramp,3)*0.01;
  fdslide = pow(p_freq_dramp,3)*-0.000001;

  // Square

  square_duty = 0.5-p_duty*0.5;
  square_slide = p_duty_ramp*-0.00005;

  // Arpeggio

  arp_time = 0;
  arp_limit = p_arp_speed != 1 ? pow(1-p_arp_speed,2)*20000+32 : 0;
  arp_mod = p_arp_mod >= 0 ? 1-pow(p_arp_mod,2)*0.9 : 1+pow(p_arp_mod,2)*10;

  if(!restart)
  {
    sampling = 1;

    // Filter

		fltp = 0;
		fltdp = 0;
		fltw = pow(p_lpf_freq,3)*0.1;
		fltw_d = 1+p_lpf_ramp*0.0001;

		fltdmp = 5/(1+pow(p_lpf_resonance,2)*20)*(0.01+fltw);
		if(fltdmp > 0.8)fltdmp = 0.8;

		fltphp = 0;
		flthp = pow(p_hpf_freq,2)*0.1;
		flthp_d = 1+p_hpf_ramp*0.0003;

    // Vibrato

    vib_phase = 0;
    vib_speed = pow(p_vib_speed,2)*0.01;
    vib_amp = p_vib_strength*0.5;

    // Envelope

    env_stage = 0;
    env_time = 0;

    env_length[0] = p_env_attack*p_env_attack*100000;
    env_length[1] = p_env_sustain*p_env_sustain*100000;
    env_length[2] = p_env_decay*p_env_decay*100000;

    // Phaser

    fphase = pow(p_pha_offset,2)*1020;
    if(p_pha_offset < 0)fphase *= -1;

    fdphase = pow(p_pha_ramp,2);
    if(p_pha_ramp < 0)fdphase *= -1;

    iphase = fphase;
    ipp = 0;

    // Noise

    fnoise = rnd()*2-1;
    ppnoise = 0;

    // Repeat

    rep_time = 0;
    rep_limit = p_repeat_speed ? pow(1-p_repeat_speed,2)*20000+32 : 0;
  }
}

//

void SynthSample(int start, int end, int quality)
{
  // Repeat

  float l_rep_time = rep_time;
  float l_rep_limit = rep_limit;

  // Arpeggio

  float l_arp_time = arp_time;
  float l_arp_mod = arp_mod;
  float l_arp_limit = arp_limit;

  // Base

  int l_wave_type = wave_type;

  float l_phase = phase;
  float l_period = period;
  float l_fperiod = fperiod;
  float l_fmaxperiod = fmaxperiod;
  float l_fslide = fslide;
  float l_fdslide = fdslide;

  // Square

  float l_square_duty = square_duty;
  float l_square_slide = square_slide;

  // Vibrato

  float l_vib_phase = vib_phase;
  float l_vib_speed = vib_speed;
  float l_vib_amp = vib_amp;

  // Envelope

  float l_env_vol;
  float l_env_length;

  float l_env_stage = env_stage;
  float l_env_time = env_time;

  float l_env_punch = p_env_punch;

  float l_env_length_0 = env_length[0];
  float l_env_length_1 = env_length[1];
  float l_env_length_2 = env_length[2];

  // Noise

  float l_fnoise = fnoise;
  int l_ppnoise = ppnoise;

  // Phaser

  float l_fphase = fphase;
  float l_fdphase = fdphase;

  int l_iphase = iphase;
  int l_ipp = ipp;

  // Filter

  float l_lpf_freq = p_lpf_freq;

  float l_fltp = fltp;
  float l_fltdp = fltdp;
  float l_fltdmp = fltdmp;

  float l_flthp = flthp;
  float l_flthp_d = flthp_d;

  float l_fltw = fltw;
  float l_fltw_d = fltw_d;

  float l_fltphp = fltphp;

  // Supersampler

  float sp = pow(2,quality);
  float ss = 8/sp;

  // Synth

  int l_sampling = sampling;

  for(int i=start; i<end; i++)
  {
    if(!l_sampling)
    {
      buffer[i] = 0;
      continue;
    }

    // Repeat

    if(l_rep_limit)
    {
      l_rep_time++;

      if(l_rep_time >= l_rep_limit)
      {
        l_rep_time = 0;
        ResetSample(1);

        // Local

        l_period = period;
        l_fperiod = fperiod;
        l_fslide = fslide;

        l_square_duty = square_duty;

        l_arp_time = arp_time;
        l_arp_limit = arp_limit;
      }
    }

    // Arpeggio

    l_arp_time++;

    if(l_arp_limit != 0 && l_arp_time >= l_arp_limit)
		{
      l_arp_limit = 0;
      l_fperiod *= l_arp_mod;
		}

    // Base

    l_fslide += l_fdslide;
    l_fperiod *= l_fslide;

    if(l_fperiod > l_fmaxperiod)
    {
      l_fperiod = l_fmaxperiod;
      l_sampling = 0;
    }

    // Square

    if(l_wave_type == 0)
    {
      l_square_duty += l_square_slide;
      if(l_square_duty < 0)l_square_duty = 0;
      if(l_square_duty > 0.5)l_square_duty = 0.5;
    }

    // Vibrato

    float rfperiod = l_fperiod;

    if(l_vib_amp > 0)
    {
      l_vib_phase += l_vib_speed;
      rfperiod = l_fperiod*(1+sin(l_vib_phase)*l_vib_amp);
    }

    l_period = rfperiod;
    if(l_period < 8)l_period = 8;

    // Envelope

    l_env_time++;

    switch(l_env_stage)
    {
      case 0: l_env_length = l_env_length_0; break;
      case 1: l_env_length = l_env_length_1; break;
      case 2: l_env_length = l_env_length_2; break;
    }

    if(l_env_time > l_env_length)
    {
      l_env_time = 0;
      l_env_stage++;
    }

    float tl = l_env_time ? l_env_time/l_env_length : 0;

    switch(l_env_stage)
    {
      case 0: l_env_vol = tl; break;
      case 1: l_env_vol = 1+(1-tl)*2*l_env_punch; break;
      case 2: l_env_vol = 1-tl; break;
    }

    // Phaser

    if(l_fphase || l_fdphase)
    {
		  l_fphase += l_fdphase;
		  l_iphase = abs(l_fphase);
		  if(l_iphase > 1023)l_iphase = 1023;
    }

    // Filter

		if(l_flthp_d != 0)
		{
			l_flthp *= l_flthp_d;
			if(l_flthp < 0.00001)l_flthp = 0.00001;
			if(l_flthp > 0.1)l_flthp = 0.1;
		}

    // Supersampler

    float ssample = 0;

    for(int si=0; si<sp; si++)
    {
      float sample = 0;

      l_phase += ss;

      if(l_phase >= l_period)l_phase = l_phase-floor(l_phase/l_period)*l_period;

      float fp = l_phase/l_period;


      switch(l_wave_type)
      {
        case 0: sample = fp < l_square_duty ? -0.5 : 0.5; break;
        case 1: sample = 1-fp*2; break;
        case 2: sample = sin(fp*PI*2); break;
        case 3: int pnoise = fp*32;
                if(pnoise != l_ppnoise){l_fnoise = rnd()*2-1; l_ppnoise = pnoise;}
                sample = l_fnoise; break;
      }

      // Filter

      float pp = l_fltp;
      l_fltw *= l_fltw_d;

 			if(l_fltw < 0)l_fltw = 0;
			if(l_fltw > 0.1)l_fltw = 0.1;

			if(l_lpf_freq != 1)
			{
				l_fltdp += (sample-l_fltp)*l_fltw;
				l_fltdp -= l_fltdp*l_fltdmp;
			}
			else
			{
				l_fltp = sample;
				l_fltdp = 0;
			}

      l_fltp += l_fltdp;

      l_fltphp += l_fltp-pp;
			l_fltphp -= l_fltphp*l_flthp;
			sample = l_fltphp;

      // Phaser

      if(l_fphase || l_fdphase)
      {
        phaser_buffer[l_ipp&1023] = sample;
        sample += phaser_buffer[(l_ipp-l_iphase+1024)&1023];
        l_ipp = (l_ipp+1)&1023;
      }

      // Accumulate

      ssample += sample;
    }

    buffer[i] = ssample*l_env_vol*sound_vol*2/sp;
  }

  // Repeat

  rep_time = l_rep_time;

  // Arpeggio

  arp_time = l_arp_time;
  arp_limit = l_arp_limit;

  // Base

  phase = l_phase;
  period = l_period;
  fperiod = l_fperiod;
  fslide = l_fslide;

  // Square

  square_duty = l_square_duty;

  // Vibrato

  vib_phase = l_vib_phase;

  // Envelope

  env_time = l_env_time;
  env_stage = l_env_stage;

  // Noise

  fnoise = l_fnoise;
  ppnoise = l_ppnoise;

  // Phaser

  fphase = l_fphase;
  iphase = l_iphase;

  ipp = l_ipp;

  // Filter

  fltw = l_fltw;
  flthp = l_flthp;

  fltp = l_fltp;
  fltdp = l_fltdp;
  fltphp = l_fltphp;

  // Synth

  sampling = l_sampling;
}]]>
      </Source>
    </ZLibrary>
    <SetAppState State="Demo"/>
  </OnLoaded>
  <States>
    <AppState Name="Demo">
      <Definitions>
        <Sample Name="MySample" Length="0.5084">
          <Producers>
            <SampleExpression>
              <Expression>
<![CDATA[//

this.Sample = buffer[this.Time*44100];]]>
              </Expression>
            </SampleExpression>
          </Producers>
        </Sample>
        <Sound Name="MySound" Length="0.5" Volume="0.29" Mod0Active="1" Mod0Destination="1" Mod0Amount="1" Env0Active="1" Env0ReleaseTime="0.2" Sample="MySample" SampleRepeatPosition="-1" UseSampleHz="255"/>
        <Array Name="Key" Type="1" Dimensions="1" SizeDim1="2" SizeDim2="3"/>
      </Definitions>
      <OnUpdate>
        <Repeat Name="KeyRepeat" Count="2">
          <OnIteration>
            <ZExpression>
              <Expression>
<![CDATA[//

int K = KeyRepeat.Iteration;

//

Key[K,1] = Key[K,0];
Key[K,0] = 0;

//

KeyPress.CharCode = 82+K;]]>
              </Expression>
            </ZExpression>
            <KeyPress Name="KeyPress" CharCode="83">
              <OnPressed>
                <ZExpression>
                  <Expression>
<![CDATA[//

Key[KeyRepeat.Iteration,0] = 1;]]>
                  </Expression>
                </ZExpression>
              </OnPressed>
            </KeyPress>
            <ZExpression>
              <Expression>
<![CDATA[//

int K = KeyRepeat.Iteration;

//

Key[K,2] = Key[K,0] && !Key[K,1];]]>
              </Expression>
            </ZExpression>
          </OnIteration>
        </Repeat>
        <Condition>
          <Expression>
<![CDATA[//

return Key[0,2];]]>
          </Expression>
          <OnTrue>
            <FileAction File="MyFile"/>
            <ZExpression>
              <Expression>
<![CDATA[//

float S = SampleLength(); // Get the sample length
float L = S/44100; // Get the sample length in seconds

//

MySound.Length = L; // Set the Sound component length
MySample.Length = L; // Set the Sample component length

//

ResetSample(0); // Reset the synthesizer
SynthSample(0,S,3); // Synthesize the entire sample]]>
              </Expression>
            </ZExpression>
            <RefreshContent Component="MySample"/>
          </OnTrue>
        </Condition>
        <Condition>
          <Expression>
<![CDATA[//

return Key[1,2];]]>
          </Expression>
          <OnTrue>
            <PlaySound Sound="MySound"/>
          </OnTrue>
        </Condition>
      </OnUpdate>
    </AppState>
  </States>
  <Content>
    <Group Comment="SFXR">
      <Children>
        <Group Comment="File">
          <Children>
            <File Name="MyFile" FileName="Sample.sfs">
              <OnRead>
                <Repeat Name="FileRead" Count="33">
                  <OnIteration>
                    <FileMoveData Property="Data"/>
                    <ZExpression>
                      <Expression>
<![CDATA[//

switch(FileRead.Iteration)
{
  case  4: wave_type = Data; break;
  case  7: MyFile.Encoding = 1; break;
  case  8: sound_vol = Data; break;
  case  9: p_base_freq = Data; break;
  case 10: p_freq_limit = Data; break;
  case 11: p_freq_ramp = Data; break;
  case 12: p_freq_dramp = Data; break;
  case 13: p_duty = Data; break;
  case 14: p_duty_ramp = Data; break;
  case 15: p_vib_strength = Data; break;
  case 16: p_vib_speed = Data; break;
  case 18: p_env_attack = Data; break;
  case 19: p_env_sustain = Data; break;
  case 20: p_env_decay = Data; break;
  case 21: p_env_punch = Data; MyFile.Encoding = 0; break;
  case 22: MyFile.Encoding = 1; break;
  case 23: p_lpf_resonance = Data; break;
  case 24: p_lpf_freq = Data; break;
  case 25: p_lpf_ramp = Data; break;
  case 26: p_hpf_freq = Data; break;
  case 27: p_hpf_ramp = Data; break;
  case 28: p_pha_offset = Data; break;
  case 29: p_pha_ramp = Data; break;
  case 30: p_repeat_speed = Data; break;
  case 31: p_arp_speed = Data; break;
  case 32: p_arp_mod = Data; MyFile.Encoding = 0; break;
}]]>
                      </Expression>
                    </ZExpression>
                  </OnIteration>
                </Repeat>
              </OnRead>
            </File>
            <Variable Name="Data"/>
          </Children>
        </Group>
        <Group Comment="Sample">
          <Children>
            <Group Comment="Synth">
              <Children>
                <Variable Name="sampling" Type="1"/>
                <Array Name="buffer" SizeDim1="132300"/>
              </Children>
            </Group>
            <Group Comment="Base">
              <Children>
                <Variable Name="phase"/>
                <Variable Name="period"/>
                <Variable Name="fperiod"/>
                <Variable Name="fmaxperiod"/>
                <Variable Name="fslide"/>
                <Variable Name="fdslide"/>
              </Children>
            </Group>
            <Group Comment="Square">
              <Children>
                <Variable Name="square_duty"/>
                <Variable Name="square_slide"/>
              </Children>
            </Group>
            <Group Comment="Vibrato">
              <Children>
                <Variable Name="vib_phase"/>
                <Variable Name="vib_speed"/>
                <Variable Name="vib_amp"/>
              </Children>
            </Group>
            <Group Comment="Envelope">
              <Children>
                <Variable Name="env_stage"/>
                <Variable Name="env_time"/>
                <Array Name="env_length" SizeDim1="3"/>
              </Children>
            </Group>
            <Group Comment="Filter">
              <Children>
                <Variable Name="fltp"/>
                <Variable Name="fltdp"/>
                <Variable Name="fltw"/>
                <Variable Name="fltw_d"/>
                <Variable Name="fltdmp"/>
                <Variable Name="fltphp"/>
                <Variable Name="flthp"/>
                <Variable Name="flthp_d"/>
              </Children>
            </Group>
            <Group Comment="Noise">
              <Children>
                <Variable Name="fnoise"/>
                <Variable Name="ppnoise" Type="1"/>
              </Children>
            </Group>
            <Group Comment="Phaser">
              <Children>
                <Variable Name="fphase"/>
                <Variable Name="fdphase"/>
                <Variable Name="iphase" Type="1"/>
                <Variable Name="ipp" Type="1"/>
                <Array Name="phaser_buffer" SizeDim1="1024"/>
              </Children>
            </Group>
            <Group Comment="Repeat">
              <Children>
                <Variable Name="rep_time"/>
                <Variable Name="rep_limit"/>
              </Children>
            </Group>
            <Group Comment="Arpeggio">
              <Children>
                <Variable Name="arp_time"/>
                <Variable Name="arp_mod"/>
                <Variable Name="arp_limit"/>
              </Children>
            </Group>
          </Children>
        </Group>
        <Group Comment="Parameters">
          <Children>
            <Group Comment="Base">
              <Children>
                <Variable Name="wave_type" Type="1"/>
                <Variable Name="sound_vol"/>
                <Variable Name="p_base_freq"/>
                <Variable Name="p_freq_limit"/>
                <Variable Name="p_freq_ramp"/>
                <Variable Name="p_freq_dramp"/>
              </Children>
            </Group>
            <Group Comment="Square">
              <Children>
                <Variable Name="p_duty"/>
                <Variable Name="p_duty_ramp"/>
              </Children>
            </Group>
            <Group Comment="Vibrato">
              <Children>
                <Variable Name="p_vib_speed"/>
                <Variable Name="p_vib_strength"/>
              </Children>
            </Group>
            <Group Comment="Envelope">
              <Children>
                <Variable Name="p_env_attack"/>
                <Variable Name="p_env_sustain"/>
                <Variable Name="p_env_decay"/>
                <Variable Name="p_env_punch"/>
              </Children>
            </Group>
            <Group Comment="Filter">
              <Children>
                <Variable Name="p_lpf_freq"/>
                <Variable Name="p_lpf_ramp"/>
                <Variable Name="p_lpf_resonance"/>
                <Variable Name="p_hpf_freq"/>
                <Variable Name="p_hpf_ramp"/>
              </Children>
            </Group>
            <Group Comment="Phaser">
              <Children>
                <Variable Name="p_pha_offset"/>
                <Variable Name="p_pha_ramp"/>
              </Children>
            </Group>
            <Group Comment="Repeat">
              <Children>
                <Variable Name="p_repeat_speed"/>
              </Children>
            </Group>
            <Group Comment="Arpeggio">
              <Children>
                <Variable Name="p_arp_speed"/>
                <Variable Name="p_arp_mod"/>
              </Children>
            </Group>
          </Children>
        </Group>
      </Children>
    </Group>
  </Content>
</ZApplication>
And I'm already happy, as sfxr/jsfxr is simpler to use for me than the ZGE sound interface, as I understand how to make other sounds than "brrrrrr" and "piiiiii' :lol:

But now I'm wondering if sfxr could be wrapped to work as an external library (dll and so), just like ZGEBullet, so that we could synthesize sounds in real time. Therefore, I'm trying to compile it, for starters. Sourcecode on original website is missing a lot of files. So I'm trying to compile one of the many clones over github, but it is using SDL1.2 and 32bits, so I'm still navigating around that.

Do you think making such a library for ZGE is doable?
(named zsfxr, to stay in the trend)
Last edited by Ats on Wed May 01, 2024 5:27 pm, edited 1 time in total.
User avatar
Kjell
Posts: 1910
Joined: Sat Feb 23, 2008 11:15 pm

Re: SFXR

Post by Kjell »

Hi Ats,
Ats wrote: Wed May 01, 2024 5:03 pmWeirdly enough, KeyPress:KeyPress isn't an issue.
You can't use KeyPress as variable type in the scripting language .. while you can use File / Sample / Sound.
Ats wrote: Wed May 01, 2024 5:03 pmBut now I'm wondering if sfxr could be wrapped to work as an external library (dll and so), just like ZGEBullet, so that we could synthesize sounds in real time.
Sure .. although it would have been nice if ZGE exposed the audio interface ( DirectSound on Windows ) so you don't have to run a second interface :|

K
User avatar
Ats
Posts: 702
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: SFXR

Post by Ats »

Kjell wrote:it would have been nice if ZGE exposed the audio interface ( DirectSound on Windows ) so you don't have to run a second interface
Oh, ok, I get it.

Alternatively, how could I import several sfx sounds without having to loop on each file to read their data? Maybe I could simply copy/paste the serialization of the sound generated by jsfxr. But I'm wondering if it's even possible to play several different sounds at the same time...?
User avatar
Ats
Posts: 702
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: SFXR

Post by Ats »

Following Kjell's concerns about the amount of time the generation of the sound can take, I made an example that runs all the sounds presets of jsfxr.
  • 0 key = pickupCoin
  • 1 key = laserShoot
  • 2 key = explosion
  • 3 key = powerUp
  • 4 key = hitHurt
  • 5 key = jump
  • 6 key = blipSelect
  • 7 key = synth
  • 8 key = tone
  • 9 key = click
  • R key = random
  • Left click to play a sound
  • Right click to mutate the current sound
And yes, while it's holding better in release than in preview, it can definitely crash :lol:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZSFXR" CustomScreenWidth="320" CustomScreenHeight="240" MouseVisible="255" FileVersion="2" AndroidPackageName="org.zgameeditor.zsfxr" AndroidPortrait="2">
  <OnLoaded>
    <ZLibrary Comment="Math">
      <Source>
<![CDATA[int irnd(int i) { return round(rnd() * i); }
float frnd(float f) { return rnd() * f; }
float sqr(float x) { return x * x; }
float cube(float x) { return x * x * x; }
int sign(float x) { return x < 0 ? -1 : 1; }
float rndr(float from, float to) { return rnd() * (to - from) + from; }]]>
      </Source>
    </ZLibrary>
    <ZLibrary Comment="SFXR">
      <Source>
<![CDATA[//

float SampleLength()
{
  return (p_env_attack*p_env_attack+
          p_env_sustain*p_env_sustain+
          p_env_decay*p_env_decay)*100000;
}

//

void ResetSample(int restart)
{
  // Base

  if(!restart)phase = 0;

  period = 100/(p_base_freq*p_base_freq+0.001);
  fperiod = period;
  fmaxperiod = 100/(p_freq_limit*p_freq_limit+0.001);

  fslide = 1-pow(p_freq_ramp,3)*0.01;
  fdslide = pow(p_freq_dramp,3)*-0.000001;

  // Square

  square_duty = 0.5-p_duty*0.5;
  square_slide = p_duty_ramp*-0.00005;

  // Arpeggio

  arp_time = 0;
  arp_limit = p_arp_speed != 1 ? pow(1-p_arp_speed,2)*20000+32 : 0;
  arp_mod = p_arp_mod >= 0 ? 1-pow(p_arp_mod,2)*0.9 : 1+pow(p_arp_mod,2)*10;

  if(!restart)
  {
    sampling = 1;

    // Filter

    fltp = 0;
    fltdp = 0;
    fltw = pow(p_lpf_freq,3)*0.1;
    fltw_d = 1+p_lpf_ramp*0.0001;

    fltdmp = 5/(1+pow(p_lpf_resonance,2)*20)*(0.01+fltw);
    if(fltdmp > 0.8)fltdmp = 0.8;

    fltphp = 0;
    flthp = pow(p_hpf_freq,2)*0.1;
    flthp_d = 1+p_hpf_ramp*0.0003;

    // Vibrato

    vib_phase = 0;
    vib_speed = pow(p_vib_speed,2)*0.01;
    vib_amp = p_vib_strength*0.5;

    // Envelope

    env_stage = 0;
    env_time = 0;

    env_length[0] = p_env_attack*p_env_attack*100000;
    env_length[1] = p_env_sustain*p_env_sustain*100000;
    env_length[2] = p_env_decay*p_env_decay*100000;

    // Phaser

    fphase = pow(p_pha_offset,2)*1020;
    if(p_pha_offset < 0)fphase *= -1;

    fdphase = pow(p_pha_ramp,2);
    if(p_pha_ramp < 0)fdphase *= -1;

    iphase = fphase;
    ipp = 0;

    // Noise

    fnoise = rnd()*2-1;
    ppnoise = 0;

    // Repeat

    rep_time = 0;
    rep_limit = p_repeat_speed ? pow(1-p_repeat_speed,2)*20000+32 : 0;
  }
}

//

void SynthSample(int start, int end, int quality)
{
  // Repeat

  float l_rep_time = rep_time;
  float l_rep_limit = rep_limit;

  // Arpeggio

  float l_arp_time = arp_time;
  float l_arp_mod = arp_mod;
  float l_arp_limit = arp_limit;

  // Base

  int l_wave_type = wave_type;

  float l_phase = phase;
  float l_period = period;
  float l_fperiod = fperiod;
  float l_fmaxperiod = fmaxperiod;
  float l_fslide = fslide;
  float l_fdslide = fdslide;

  // Square

  float l_square_duty = square_duty;
  float l_square_slide = square_slide;

  // Vibrato

  float l_vib_phase = vib_phase;
  float l_vib_speed = vib_speed;
  float l_vib_amp = vib_amp;

  // Envelope

  float l_env_vol;
  float l_env_length;

  float l_env_stage = env_stage;
  float l_env_time = env_time;

  float l_env_punch = p_env_punch;

  float l_env_length_0 = env_length[0];
  float l_env_length_1 = env_length[1];
  float l_env_length_2 = env_length[2];

  // Noise

  float l_fnoise = fnoise;
  int l_ppnoise = ppnoise;

  // Phaser

  float l_fphase = fphase;
  float l_fdphase = fdphase;

  int l_iphase = iphase;
  int l_ipp = ipp;

  // Filter

  float l_lpf_freq = p_lpf_freq;

  float l_fltp = fltp;
  float l_fltdp = fltdp;
  float l_fltdmp = fltdmp;

  float l_flthp = flthp;
  float l_flthp_d = flthp_d;

  float l_fltw = fltw;
  float l_fltw_d = fltw_d;

  float l_fltphp = fltphp;

  // Supersampler

  float sp = pow(2,quality);
  float ss = 8/sp;

  // Synth

  int l_sampling = sampling;

  for(int i=start; i<end; i++)
  {
    if(!l_sampling)
    {
      buffer[i] = 0;
      continue;
    }

    // Repeat

    if(l_rep_limit)
    {
      l_rep_time++;

      if(l_rep_time >= l_rep_limit)
      {
        l_rep_time = 0;
        ResetSample(1);

        // Local

        l_period = period;
        l_fperiod = fperiod;
        l_fslide = fslide;

        l_square_duty = square_duty;

        l_arp_time = arp_time;
        l_arp_limit = arp_limit;
      }
    }

    // Arpeggio

    l_arp_time++;

    if(l_arp_limit != 0 && l_arp_time >= l_arp_limit)
    {
      l_arp_limit = 0;
      l_fperiod *= l_arp_mod;
    }

    // Base

    l_fslide += l_fdslide;
    l_fperiod *= l_fslide;

    if(l_fperiod > l_fmaxperiod)
    {
      l_fperiod = l_fmaxperiod;
      l_sampling = 0;
    }

    // Square

    if(l_wave_type == 0)
    {
      l_square_duty += l_square_slide;
      if(l_square_duty < 0)l_square_duty = 0;
      if(l_square_duty > 0.5)l_square_duty = 0.5;
    }

    // Vibrato

    float rfperiod = l_fperiod;

    if(l_vib_amp > 0)
    {
      l_vib_phase += l_vib_speed;
      rfperiod = l_fperiod*(1+sin(l_vib_phase)*l_vib_amp);
    }

    l_period = rfperiod;
    if(l_period < 8)l_period = 8;

    // Envelope

    l_env_time++;

    switch(l_env_stage)
    {
      case 0: l_env_length = l_env_length_0; break;
      case 1: l_env_length = l_env_length_1; break;
      case 2: l_env_length = l_env_length_2; break;
    }

    if(l_env_time > l_env_length)
    {
      l_env_time = 0;
      l_env_stage++;
    }

    float tl = l_env_time ? l_env_time/l_env_length : 0;

    switch(l_env_stage)
    {
      case 0: l_env_vol = tl; break;
      case 1: l_env_vol = 1+(1-tl)*2*l_env_punch; break;
      case 2: l_env_vol = 1-tl; break;
    }

    // Phaser

    if(l_fphase || l_fdphase)
    {
      l_fphase += l_fdphase;
      l_iphase = abs(l_fphase);
      if(l_iphase > 1023)l_iphase = 1023;
    }

    // Filter

    if(l_flthp_d != 0)
    {
      l_flthp *= l_flthp_d;
      if(l_flthp < 0.00001)l_flthp = 0.00001;
      if(l_flthp > 0.1)l_flthp = 0.1;
    }

    // Supersampler

    float ssample = 0;

    for(int si=0; si<sp; si++)
    {
      float sample = 0;

      l_phase += ss;

      if(l_phase >= l_period)l_phase = l_phase-floor(l_phase/l_period)*l_period;

      float fp = l_phase/l_period;


      switch(l_wave_type)
      {
        case 0: sample = fp < l_square_duty ? -0.5 : 0.5; break;
        case 1: sample = 1-fp*2; break;
        case 2: sample = sin(fp*PI*2); break;
        case 3: int pnoise = fp*32;
                if(pnoise != l_ppnoise){l_fnoise = rnd()*2-1; l_ppnoise = pnoise;}
                sample = l_fnoise; break;
      }

      // Filter

      float pp = l_fltp;
      l_fltw *= l_fltw_d;

      if(l_fltw < 0)l_fltw = 0;
      if(l_fltw > 0.1)l_fltw = 0.1;

      if(l_lpf_freq != 1)
      {
        l_fltdp += (sample-l_fltp)*l_fltw;
        l_fltdp -= l_fltdp*l_fltdmp;
      }
      else
      {
        l_fltp = sample;
        l_fltdp = 0;
      }

      l_fltp += l_fltdp;

      l_fltphp += l_fltp-pp;
      l_fltphp -= l_fltphp*l_flthp;
      sample = l_fltphp;

      // Phaser

      if(l_fphase || l_fdphase)
      {
        phaser_buffer[l_ipp&1023] = sample;
        sample += phaser_buffer[(l_ipp-l_iphase+1024)&1023];
        l_ipp = (l_ipp+1)&1023;
      }

      // Accumulate

      ssample += sample;
    }

    buffer[i] = ssample*l_env_vol*sound_vol*2/sp;
  }

  // Repeat

  rep_time = l_rep_time;

  // Arpeggio

  arp_time = l_arp_time;
  arp_limit = l_arp_limit;

  // Base

  phase = l_phase;
  period = l_period;
  fperiod = l_fperiod;
  fslide = l_fslide;

  // Square

  square_duty = l_square_duty;

  // Vibrato

  vib_phase = l_vib_phase;

  // Envelope

  env_time = l_env_time;
  env_stage = l_env_stage;

  // Noise

  fnoise = l_fnoise;
  ppnoise = l_ppnoise;

  // Phaser

  fphase = l_fphase;
  iphase = l_iphase;

  ipp = l_ipp;

  // Filter

  fltw = l_fltw;
  flthp = l_flthp;

  fltp = l_fltp;
  fltdp = l_fltdp;
  fltphp = l_fltphp;

  // Synth

  sampling = l_sampling;
}]]>
      </Source>
    </ZLibrary>
    <ZLibrary Comment="SFXR Samples" HasInitializer="1">
      <Source>
<![CDATA[// Wave shapes
byte SQUARE = 0;
byte SAWTOOTH = 1;
byte SINE = 2;
byte NOISE = 3;

void setcurrentSound(int i)
{
  currentSound = i;
  switch (currentSound)
  {
    case 0: text.Text = "PICKUP COIN"; break;
    case 1: text.Text = "LASER SHOOT"; break;
    case 2: text.Text = "EXPLOSION"; break;
    case 3: text.Text = "POWERUP"; break;
    case 4: text.Text = "HIT HURT"; break;
    case 5: text.Text = "JUMP"; break;
    case 6: text.Text = "BLIP SELECT"; break;
    case 7: text.Text = "SYNTH"; break;
    case 8: text.Text = "TONE"; break;
    case 9: text.Text = "CLICK"; break;
    case 10: text.Text = "RANDOM"; break;
  }
}

void initSound()
{
  // Wave shape
  wave_type = SQUARE;

  // Envelope
  p_env_attack = 0;    // Attack time
  p_env_sustain = 0.3; // Sustain time
  p_env_punch = 0;     // Sustain punch
  p_env_decay = 0.4;   // Decay time

  // Tone
  p_base_freq = 0.3;  // Start frequency
  p_freq_limit = 0;   // Min frequency cutoff
  p_freq_ramp = 0;    // Slide (SIGNED)
  p_freq_dramp = 0;   // Delta slide (SIGNED)

  // Vibrato
  p_vib_strength = 0; // Vibrato depth
  p_vib_speed = 0;    // Vibrato speed

  // Tonal change
  p_arp_mod = 0;      // Change amount (SIGNED)
  p_arp_speed = 0;    // Change speed

  // Square wave duty (proportion of time signal is high vs. low)
  p_duty = 0;         // Square duty
  p_duty_ramp = 0;    // Duty sweep (SIGNED)

  // Repeat
  p_repeat_speed = 0; // Repeat speed

  // Flanger
  p_pha_offset = 0;   // Flanger offset (SIGNED)
  p_pha_ramp = 0;     // Flanger sweep (SIGNED)

  // Low-pass filter
  p_lpf_freq = 1;     // Low-pass filter cutoff
  p_lpf_ramp = 0;     // Low-pass filter cutoff sweep (SIGNED)
  p_lpf_resonance = 0;// Low-pass filter resonance

  // High-pass filter
  p_hpf_freq = 0;     // High-pass filter cutoff
  p_hpf_ramp = 0;     // High-pass filter cutoff sweep (SIGNED)

  // Sample parameters
  sound_vol = 0.5;
  // sample_rate = 44100;
  // sample_size = 8;
}


void pickupCoin() {
  wave_type = SAWTOOTH;
  p_base_freq = 0.4 + frnd(0.5);
  p_env_attack = 0;
  p_env_sustain = frnd(0.1);
  p_env_decay = 0.1 + frnd(0.4);
  p_env_punch = 0.3 + frnd(0.3);
  if (irnd(1)) {
    p_arp_speed = 0.5 + frnd(0.2);
    p_arp_mod = 0.2 + frnd(0.4);
  }
}

void laserShoot() {
  wave_type = irnd(2);
  if(wave_type == SINE && irnd(1))
    wave_type = irnd(1);
  if (irnd(2) == 0) {
    p_base_freq = 0.3 + frnd(0.6);
    p_freq_limit = frnd(0.1);
    p_freq_ramp = -0.35 - frnd(0.3);
  } else {
    p_base_freq = 0.5 + frnd(0.5);
    p_freq_limit = p_base_freq - 0.2 - frnd(0.6);
    if (p_freq_limit < 0.2) p_freq_limit = 0.2;
    p_freq_ramp = -0.15 - frnd(0.2);
  }
  if (wave_type == SAWTOOTH)
    p_duty = 1;
  if (irnd(1)) {
    p_duty = frnd(0.5);
    p_duty_ramp = frnd(0.2);
  } else {
    p_duty = 0.4 + frnd(0.5);
    p_duty_ramp = -frnd(0.7);
  }
  p_env_attack = 0;
  p_env_sustain = 0.1 + frnd(0.2);
  p_env_decay = frnd(0.4);
  if (irnd(1))
    p_env_punch = frnd(0.3);
  if (irnd(2) == 0) {
    p_pha_offset = frnd(0.2);
    p_pha_ramp = -frnd(0.2);
  }
  //if (irnd(1))
    p_hpf_freq = frnd(0.3);
}

void explosion() {
  wave_type = NOISE;
  if (irnd(1)) {
    p_base_freq = sqr(0.1 + frnd(0.4));
    p_freq_ramp = -0.1 + frnd(0.4);
  } else {
    p_base_freq = sqr(0.2 + frnd(0.7));
    p_freq_ramp = -0.2 - frnd(0.2);
  }
  if (irnd(4) == 0)
    p_freq_ramp = 0;
  if (irnd(2) == 0)
    p_repeat_speed = 0.3 + frnd(0.5);
  p_env_attack = 0;
  p_env_sustain = 0.1 + frnd(0.3);
  p_env_decay = frnd(0.5);
  if (irnd(1)) {
    p_pha_offset = -0.3 + frnd(0.9);
    p_pha_ramp = -frnd(0.3);
  }
  p_env_punch = 0.2 + frnd(0.6);
  if (irnd(1)) {
    p_vib_strength = frnd(0.7);
    p_vib_speed = frnd(0.6);
  }
  if (irnd(2) == 0) {
    p_arp_speed = 0.6 + frnd(0.3);
    p_arp_mod = 0.8 - frnd(1.6);
  }
}

void powerUp() {
  if (irnd(1)) {
    wave_type = SAWTOOTH;
    p_duty = 1;
  } else {
    p_duty = frnd(0.6);
  }
  p_base_freq = 0.2 + frnd(0.3);
  if (irnd(1)) {
    p_freq_ramp = 0.1 + frnd(0.4);
    p_repeat_speed = 0.4 + frnd(0.4);
  } else {
    p_freq_ramp = 0.05 + frnd(0.2);
    if (irnd(1)) {
      p_vib_strength = frnd(0.7);
      p_vib_speed = frnd(0.6);
    }
  }
  p_env_attack = 0;
  p_env_sustain = frnd(0.4);
  p_env_decay = 0.1 + frnd(0.4);
}

void hitHurt() {
  wave_type = irnd(2);
  if (wave_type == SINE)
    wave_type = NOISE;
  if (wave_type == SQUARE)
    p_duty = frnd(0.6);
  if (wave_type == SAWTOOTH)
    p_duty = 1;
  p_base_freq = 0.2 + frnd(0.6);
  p_freq_ramp = -0.3 - frnd(0.4);
  p_env_attack = 0;
  p_env_sustain = frnd(0.1);
  p_env_decay = 0.1 + frnd(0.2);
  if (irnd(1))
    p_hpf_freq = frnd(0.3);
}

void jump() {
  wave_type = SQUARE;
  p_duty = frnd(0.6);
  p_base_freq = 0.3 + frnd(0.3);
  p_freq_ramp = 0.1 + frnd(0.2);
  p_env_attack = 0;
  p_env_sustain = 0.1 + frnd(0.3);
  p_env_decay = 0.1 + frnd(0.2);
  if (irnd(1))
    p_hpf_freq = frnd(0.3);
  if (irnd(1))
    p_lpf_freq = 1 - frnd(0.6);
}

void blipSelect() {
  wave_type = irnd(1);
  if (wave_type == SQUARE)
    p_duty = frnd(0.6);
  else
    p_duty = 1;
  p_base_freq = 0.2 + frnd(0.4);
  p_env_attack = 0;
  p_env_sustain = 0.1 + frnd(0.1);
  p_env_decay = frnd(0.2);
  p_hpf_freq = 0.1;
}

void synth() {
  wave_type = irnd(1);

//  p_base_freq = [0.2723171360931539, 0.19255692561524382, 0.13615778746815113][];
  int bf = irnd(2);
  if (bf == 0) p_base_freq = 0.27231713;
  else if (bf == 1) p_base_freq = 0.19255692;
  else p_base_freq = 0.13615778;

  p_env_attack = irnd(4) > 3 ? frnd(0.5) : 0;
  p_env_sustain = frnd(1);
  p_env_punch = frnd(1);
  p_env_decay = frnd(0.9) + 0.1;

//  p_arp_mod = [0, 0, 0, 0, -0.3162, 0.7454, 0.7454][irnd(6)];
  int am = irnd(2);
  if (am == 4) p_arp_mod = -0.3162;
  else if (am == 5) p_arp_mod = 0.7454;
  else if (am == 6) p_arp_mod = 0.7454;
  else p_arp_mod = 0;

  p_arp_mod = 0.7454;
  p_arp_speed = frnd(0.5) + 0.4;
  p_duty = frnd(1);
  p_duty_ramp = irnd(2) == 2 ? frnd(1) : 0;

//  p_lpf_freq = [1, 0.9 * frnd(1) * frnd(1) + 0.1][irnd(1)];
  p_lpf_freq = irnd(1) ? 0.9 * frnd(1) * frnd(1) + 0.1 : 1;

  p_lpf_ramp = rndr(-1, 1);
  p_lpf_resonance = frnd(1);
  p_hpf_freq = irnd(3) == 3 ? frnd(1) : 0;
  p_hpf_ramp = irnd(3) == 3 ? frnd(1) : 0;
}

void tone() {
  wave_type = SINE;
  p_base_freq = 0.35173364; // 440 Hz
  p_env_attack = 0;
  p_env_sustain = 0.6641; // 1 sec
  p_env_decay = 0;
  p_env_punch = 0;
}

void click() {
  //const base = ["explosion", "hitHurt"][irnd(1)];
  //this[base]();
  if (irnd(1)) {
    p_freq_ramp = -0.5 + frnd(1.0);
  }
  if (irnd(1)) {
    p_env_sustain = (frnd(0.4) + 0.2) * p_env_sustain;
    p_env_decay = (frnd(0.4) + 0.2) * p_env_decay;
  }
  if (irnd(3) == 0) {
    p_env_attack = frnd(0.3);
  }
  p_base_freq = 1 - frnd(0.25);
  p_hpf_freq = 1 - frnd(0.1);
}

void random() {
  wave_type = irnd(3);
  if (irnd(1))
    p_base_freq = cube(frnd(2) - 1) + 0.5;
  else
    p_base_freq = sqr(frnd(1));
  p_freq_limit = 0;
  p_freq_ramp = pow(frnd(2) - 1, 5);
  if (p_base_freq > 0.7 && p_freq_ramp > 0.2)
    p_freq_ramp = -p_freq_ramp;
  if (p_base_freq < 0.2 && p_freq_ramp < -0.05)
    p_freq_ramp = -p_freq_ramp;
  p_freq_dramp = pow(frnd(2) - 1, 3);
  p_duty = frnd(2) - 1;
  p_duty_ramp = pow(frnd(2) - 1, 3);
  p_vib_strength = pow(frnd(2) - 1, 3);
  p_vib_speed = rndr(-1, 1);
  p_env_attack = cube(rndr(-1, 1));
  p_env_sustain = sqr(rndr(-1, 1));
  p_env_decay = rndr(-1, 1);
  p_env_punch = pow(frnd(0.8), 2);
  if (p_env_attack + p_env_sustain + p_env_decay < 0.2) {
    p_env_sustain += 0.2 + frnd(0.3);
    p_env_decay += 0.2 + frnd(0.3);
  }
  p_lpf_resonance = rndr(-1, 1);
  p_lpf_freq = 1 - pow(frnd(1), 3);
  p_lpf_ramp = pow(frnd(2) - 1, 3);
  if (p_lpf_freq < 0.1 && p_lpf_ramp < -0.05)
    p_lpf_ramp = -p_lpf_ramp;
  p_hpf_freq = pow(frnd(1), 5);
  p_hpf_ramp = pow(frnd(2) - 1, 5);
  p_pha_offset = pow(frnd(2) - 1, 3);
  p_pha_ramp = pow(frnd(2) - 1, 3);
  p_repeat_speed = frnd(2) - 1;
  p_arp_speed = frnd(2) - 1;
  p_arp_mod = frnd(2) - 1;
}

void mutate() {
  if (irnd(1)) p_base_freq += frnd(0.1) - 0.05;
  if (irnd(1)) p_freq_ramp += frnd(0.1) - 0.05;
  if (irnd(1)) p_freq_dramp += frnd(0.1) - 0.05;
  if (irnd(1)) p_duty += frnd(0.1) - 0.05;
  if (irnd(1)) p_duty_ramp += frnd(0.1) - 0.05;
  if (irnd(1)) p_vib_strength += frnd(0.1) - 0.05;
  if (irnd(1)) p_vib_speed += frnd(0.1) - 0.05;
  //if (irnd(1)) p_vib_delay += frnd(0.1) - 0.05;
  if (irnd(1)) p_env_attack += frnd(0.1) - 0.05;
  if (irnd(1)) p_env_sustain += frnd(0.1) - 0.05;
  if (irnd(1)) p_env_decay += frnd(0.1) - 0.05;
  if (irnd(1)) p_env_punch += frnd(0.1) - 0.05;
  if (irnd(1)) p_lpf_resonance += frnd(0.1) - 0.05;
  if (irnd(1)) p_lpf_freq += frnd(0.1) - 0.05;
  if (irnd(1)) p_lpf_ramp += frnd(0.1) - 0.05;
  if (irnd(1)) p_hpf_freq += frnd(0.1) - 0.05;
  if (irnd(1)) p_hpf_ramp += frnd(0.1) - 0.05;
  if (irnd(1)) p_pha_offset += frnd(0.1) - 0.05;
  if (irnd(1)) p_pha_ramp += frnd(0.1) - 0.05;
  if (irnd(1)) p_repeat_speed += frnd(0.1) - 0.05;
  if (irnd(1)) p_arp_speed += frnd(0.1) - 0.05;
  if (irnd(1)) p_arp_mod += frnd(0.1) - 0.05;
}]]>
      </Source>
    </ZLibrary>
    <SetAppState State="Demo"/>
  </OnLoaded>
  <States>
    <AppState Name="Demo">
      <Definitions>
        <Sample Name="MySample" Length="1.5812">
          <Producers>
            <SampleExpression>
              <Expression>
<![CDATA[//

this.Sample = buffer[MySampleIndex++];]]>
              </Expression>
            </SampleExpression>
          </Producers>
        </Sample>
        <Sound Name="MySound" Length="1.5812" Volume="0.29" Mod0Active="1" Mod0Destination="1" Mod0Amount="1" Env0Active="1" Env0ReleaseTime="0.2" Sample="MySample" SampleRepeatPosition="-1" UseSampleHz="255"/>
        <Variable Name="MySampleIndex" Type="1"/>
      </Definitions>
      <OnStart>
        <ZExpression Expression="setcurrentSound(irnd(9));"/>
      </OnStart>
      <OnUpdate>
        <KeyPress Comment="0 - pickupCoin" Keys="0">
          <OnPressed>
            <ZExpression Expression="setcurrentSound(0);"/>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="1 - laserShoot" Keys="1">
          <OnPressed>
            <ZExpression Expression="setcurrentSound(1);"/>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="2 - explosion" Keys="2">
          <OnPressed>
            <ZExpression Expression="setcurrentSound(2);"/>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="3 - powerUp" Keys="3">
          <OnPressed>
            <ZExpression Expression="setcurrentSound(3);"/>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="4 - hitHurt" Keys="4">
          <OnPressed>
            <ZExpression Expression="setcurrentSound(4);"/>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="5 - jump" Keys="5">
          <OnPressed>
            <ZExpression Expression="setcurrentSound(5);"/>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="6 - blipSelect" Keys="6">
          <OnPressed>
            <ZExpression Expression="setcurrentSound(6);"/>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="7 - synth" Keys="7">
          <OnPressed>
            <ZExpression Expression="setcurrentSound(7);"/>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="8 - tone" Keys="8">
          <OnPressed>
            <ZExpression Expression="setcurrentSound(8);"/>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="9 - click" Keys="9">
          <OnPressed>
            <ZExpression Expression="setcurrentSound(9);"/>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="R - random" Keys="R">
          <OnPressed>
            <ZExpression Expression="setcurrentSound(10);"/>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="left click play sound" Keys="{" RepeatDelay="0.2">
          <OnPressed>
            <ZExpression Comment="Sound library">
              <Expression>
<![CDATA[if (App.MousePosition.Y > 0.7)
{
  // Normalize the mouse_x position from the range (-1, 1) to the range (0, 1)
  float normalized_x = (App.MousePosition.X + 1) / 2;

  // Calculate the index in the range of the buttons (0-9)
  setCurrentSound(floor(normalized_x * 10));
}

sound_vol = 0.5;

initSound();

switch(currentSound)
{
    case 0: pickupCoin(); break;
    case 1: laserShoot(); break;
    case 2: explosion(); break;
    case 3: powerUp(); break;
    case 4: hitHurt(); break;
    case 5: jump(); break;
    case 6: blipSelect(); break;
    case 7: synth(); break;
    case 8: tone(); break;
    case 9: click(); break;
    case 10: random(); break; // !!! Can trigger an infinite loop in SynthSample()
}

playSound = 1;]]>
              </Expression>
            </ZExpression>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="right click mutate sound" Keys="}" RepeatDelay="0.2">
          <OnPressed>
            <ZExpression Comment="Sound library">
              <Expression>
<![CDATA[mutate();

playSound = 1;]]>
              </Expression>
            </ZExpression>
          </OnPressed>
        </KeyPress>
        <Condition Comment="playSound?" Expression="return playSound;">
          <OnTrue>
            <ZExpression>
              <Expression>
<![CDATA[playSound = 0;

//

float S = SampleLength(); // Get the sample length
float L = S/44100; // Get the sample length in seconds

//

MySound.Length = L; // Set the Sound component length
MySample.Length = L; // Set the Sample component length

//

buffer.SizeDim1 = S + 1; // Set buffer size
MySampleIndex = 0;

ResetSample(0); // Reset the synthesizer
SynthSample(0,S,2); // Synthesize the entire sample]]>
              </Expression>
            </ZExpression>
            <RefreshContent Component="MySample"/>
            <PlaySound Sound="MySound"/>
          </OnTrue>
        </Condition>
      </OnUpdate>
      <OnRender>
        <UseMaterial Material="FontMaterial4"/>
        <RenderText Name="text" Text="SYNTH"/>
        <RenderText Text="0" X="-0.9" Y="0.8"/>
        <RenderText Text="1" X="-0.7" Y="0.8"/>
        <RenderText Text="2" X="-0.5" Y="0.8"/>
        <RenderText Text="3" X="-0.3" Y="0.8"/>
        <RenderText Text="4" X="-0.1" Y="0.8"/>
        <RenderText Text="5" X="0.1" Y="0.8"/>
        <RenderText Text="6" X="0.3" Y="0.8"/>
        <RenderText Text="7" X="0.5" Y="0.8"/>
        <RenderText Text="8" X="0.7" Y="0.8"/>
        <RenderText Text="9" X="0.9" Y="0.8"/>
      </OnRender>
    </AppState>
  </States>
  <Content>
    <Variable Name="CurrentSound" Type="4"/>
    <Variable Name="playSound" Type="4"/>
    <Group Comment="SFXR">
      <Children>
        <Group Comment="Sample">
          <Children>
            <Group Comment="Synth">
              <Children>
                <Variable Name="sampling" Type="1"/>
                <Array Name="buffer" SizeDim1="69729"/>
              </Children>
            </Group>
            <Group Comment="Base">
              <Children>
                <Variable Name="phase"/>
                <Variable Name="period"/>
                <Variable Name="fperiod"/>
                <Variable Name="fmaxperiod"/>
                <Variable Name="fslide"/>
                <Variable Name="fdslide"/>
              </Children>
            </Group>
            <Group Comment="Square">
              <Children>
                <Variable Name="square_duty"/>
                <Variable Name="square_slide"/>
              </Children>
            </Group>
            <Group Comment="Vibrato">
              <Children>
                <Variable Name="vib_phase"/>
                <Variable Name="vib_speed"/>
                <Variable Name="vib_amp"/>
              </Children>
            </Group>
            <Group Comment="Envelope">
              <Children>
                <Variable Name="env_stage"/>
                <Variable Name="env_time"/>
                <Array Name="env_length" SizeDim1="3"/>
              </Children>
            </Group>
            <Group Comment="Filter">
              <Children>
                <Variable Name="fltp"/>
                <Variable Name="fltdp"/>
                <Variable Name="fltw"/>
                <Variable Name="fltw_d"/>
                <Variable Name="fltdmp"/>
                <Variable Name="fltphp"/>
                <Variable Name="flthp"/>
                <Variable Name="flthp_d"/>
              </Children>
            </Group>
            <Group Comment="Noise">
              <Children>
                <Variable Name="fnoise"/>
                <Variable Name="ppnoise" Type="1"/>
              </Children>
            </Group>
            <Group Comment="Phaser">
              <Children>
                <Variable Name="fphase"/>
                <Variable Name="fdphase"/>
                <Variable Name="iphase" Type="1"/>
                <Variable Name="ipp" Type="1"/>
                <Array Name="phaser_buffer" SizeDim1="1024"/>
              </Children>
            </Group>
            <Group Comment="Repeat">
              <Children>
                <Variable Name="rep_time"/>
                <Variable Name="rep_limit"/>
              </Children>
            </Group>
            <Group Comment="Arpeggio">
              <Children>
                <Variable Name="arp_time"/>
                <Variable Name="arp_mod"/>
                <Variable Name="arp_limit"/>
              </Children>
            </Group>
          </Children>
        </Group>
        <Group Comment="Parameters">
          <Children>
            <Group Comment="Base">
              <Children>
                <Variable Name="wave_type" Type="1"/>
                <Variable Name="sound_vol"/>
                <Variable Name="p_base_freq"/>
                <Variable Name="p_freq_limit"/>
                <Variable Name="p_freq_ramp"/>
                <Variable Name="p_freq_dramp"/>
              </Children>
            </Group>
            <Group Comment="Square">
              <Children>
                <Variable Name="p_duty"/>
                <Variable Name="p_duty_ramp"/>
              </Children>
            </Group>
            <Group Comment="Vibrato">
              <Children>
                <Variable Name="p_vib_speed"/>
                <Variable Name="p_vib_strength"/>
              </Children>
            </Group>
            <Group Comment="Envelope">
              <Children>
                <Variable Name="p_env_attack"/>
                <Variable Name="p_env_sustain"/>
                <Variable Name="p_env_decay"/>
                <Variable Name="p_env_punch"/>
              </Children>
            </Group>
            <Group Comment="Filter">
              <Children>
                <Variable Name="p_lpf_freq"/>
                <Variable Name="p_lpf_ramp"/>
                <Variable Name="p_lpf_resonance"/>
                <Variable Name="p_hpf_freq"/>
                <Variable Name="p_hpf_ramp"/>
              </Children>
            </Group>
            <Group Comment="Phaser">
              <Children>
                <Variable Name="p_pha_offset"/>
                <Variable Name="p_pha_ramp"/>
              </Children>
            </Group>
            <Group Comment="Repeat">
              <Children>
                <Variable Name="p_repeat_speed"/>
              </Children>
            </Group>
            <Group Comment="Arpeggio">
              <Children>
                <Variable Name="p_arp_speed"/>
                <Variable Name="p_arp_mod"/>
              </Children>
            </Group>
          </Children>
        </Group>
      </Children>
    </Group>
    <Group Name="Font4Group">
      <Children>
        <Bitmap Name="FontBitmap4" Width="512" Height="128">
          <Producers>
            <BitmapFromFile Transparency="1" DataWidth="512" DataHeight="128">
              <BitmapFile>
<![CDATA[789CED5D8195E32A0C743129C6CDA498349362D24CFEEE618F074960094876B39F793CBFBBB52D84908410982CCBC4C4C4C4C4C48175BDDEEF8FAF72BBDDBFCAF57A4BFF487F7C3C9EDFE5B9977FFF15CF7F9544E77259BFCAD73FBE4AFA7BBAE541A9463C60DC2D30C30C186DF9F7FAD70351412552A8051545E9082456B92DB2A5D5662699D7E90B222552CDFC0FA12F5A94FE1DE5AAA4A8CED72176D104CFBB45ABD909FA553749804D29248489090FBE2D77D743E1D96C652E2B6DA2A68700BFE9956AE407C60C01E42242E24A5508E75FF7BDA7D83CBF6A8B73D83DE521EA73A22EB7817E4514FAE1A8EB2B71E27C3D49DE0C6F3CEF8E1D0258874342989838857438F75B5B81C748AACBB7FCA607479D117F48FF2F1F682ED476BFC4B4A04E63EFD3561FDEA0B52D251ED2B4CE4F9F1D8E6708184E5F2B4083EBDBF8C995D04FE4AB45428743FEBFC78E4A25C4FFC484074760D9AD9F883F93A508D5753A5804D519F1DC3F2743186859C8543885A60DBC39FEFF6AC81125F6B5C20CAAB32834428D674C15FE5F415F8FEFA1F963C21660AB1A9DAFA711AAEDF52DC21FA79F30A2A81026262AE098ED4BC12BD7D307BEAE883F937FCEEEFABC2B32BD82B2783D59E2293FA16B322E67C603EE0E14DAD2E65FAF6C898211ADD03C609DA29926DCBBC9FF8BE86F7F54BD1392ADEE23E774065D2374CCDFC5187AC6AA6868FD6262A28E23E77FBB0E29883FE1138EE25EC44C81B4249E6B7E7A7814DBA98416F8B4DC42BE851B929CCC902292E4C818F752FED777BA75AFA3BF4DC7F2DE490919A760B19341C8C74F41372DE0FF9F23CD8A0515CA524E4C5490ECEE4BAFBFED4B5CC93987167F9381A4BF3341FF22669A44F0BB3A4B60AE8B9DAEB81D2D329B7C0BACAF25E26DEF722BBED9B698716EBBFA6E5199874D2C4EFA1091F5BC587FF1D3670A7EFADBDAAB5248FF323D5AC71585727449A39887FA9A3523F55449CDBE19B1763739D5D8C9FFC444056C205CA06C7098D15D28CB1EBF09CA6DC5DC3B6AEE8BF30F01705CBAAEA8811F25B83607CE4BEDF58CBC42C89C248FD217DD2DB5629764887FA683AD651EFAA999C633D634440359354121B446AF29F85F474FD515DBDC2677AAC64EFE27264A80817C29F5B72AEE5756B39E0D8DC9D805F1C6EB3E0719D87C0C55A2AE5882F79FFF177CFA79D8F6345A0CF0485A011C94E6E1D83319A19FB4026EC7D08DDDFD62F255A10F3F9FEE6EFED0473F89D756CEB3EF35D88B3285E81EDDC4AAA0E07F1D237549B1BF4BF0BB9889892180757041A4E1F43C7AF73B26C88994A0DF56100B8D95C0E6A9F2BAFC06AE1B185A9B4BB2BAFE4B2AA04477DDEB4E84A05243DAE8635548F720E2CF3AFD2443C400C7BBCFA79FFEF7F0AA55348F4F34E7592E45756E6881268D53FD140411591C3BAC2626C662F3DB97E55BABF76B6999CF0486003D875DF6F92F13EFB926236A585DAD60F3C0795DFE09FE262B7A37B436B7B9A9BC9908AA9D44742782874EFAC9FD7E47025647D4E983C8E6A5F7BB227D54A78F07D210C08FD5D79E8E444AFE96734AC5C024E5A0135FE2079315F57E5184333151C2E6BABF6C762F88559C14F03C13C1FE1358374A2DAB99A7B533827B199BF93C06ACBC16BFFBD5AC86D6E692A084E8A2E1A5268215D27EFAE81726C2F38B53FAE2F5AFFFB27CEAF4173A4AC2D48792F3D77D0A07DBF2F9D843EA7043AEE68894AC86B459DFC4440F109324C04642FE27793C26020381E58A5BFEDD447837BD8E87351B226BED5CA44EE16B466777624E018AB687462884EE2C9F06FFCFAD601EFAE9234BA395A4423F8BFFF718203D20F26375FA1B85BDFB444FF901D7DA9060D972A1397B6DB9FA23535A6808C6EEB9BC3BF106601B4C32D22D937CBD85167CB7142E11C104B972AB0E76D1783D5130D7FE30A64437A9A67F33FD508A000433F622FE5FBCDE105BE2549C83877D881C427F4BE0E744E0FF4FE96F6BB8FF9E49CBC1C20957E827A43142E8AABF5CE9243D67938FB509A578227A89268258F1EADC9A41CEC4C4406CAE3257BFB07F78E6C1F63FE70CFF2FE84783CF144C9A96C28FC169449D8369807E21942A75363079127EB121B64CE90E938721F42B4AE2A12F1CA95EE5F428E1BA5E317C37F6B26F50AECD22A9880742F2AC6B7503CF13136D38FCF31E2A37AC6D4157714506E030EDFD6E68EFDC423B18B32A68828C84B3E0A1F98AE56C0F7BDBF4812944CC36B91A517B344771381396CFEEFFFBE927C72B3A11FEFF94BEF8AC40FBF60A7DDDD2630808F6ACA7434572129A96CD259FEA8F4F2F7D001FAD9C733E87808997416AF823169F6F449ED228B07F66336DEB9613D8C8C144787D731B1D5443DA0A824C277B2B1DD4CFC5F97A164612037EF92C38A0CCE26108FDC3DF2A25F1D0C79A2627DF9CF419EC9CDB7AB69EFFC9F28121E22A26F1C0546C93E7B91C3CF12224D54584F6D8E373BF8B3E224FA223CFFF21FAA19D8D09C92A99088C82E328F140DB356A6E306141C7F97A8987907CD26C45931A453F396DB3135F4D1F58E96BB2E6FEAD8CECEC8DDB8837B4FD580838D3C9F95DC0C42BC0C94CAC67855CF44ABF17C344E0FFAFFB17A0E2961F307C8E24EFF47D13DFE212FDA03EE4BA13127D5DB5F3752DBA061EB47CC0C38BE823427835FD8435FF94B8B994327B88223AE943C3A36DAF370D29AF905427264E01FF9FBC62DAF91972D1B07A50482690E6F8D06DA61F5A7F84C716D7640E89557D17AD080D01D18169D95D07D77E8F446BA976E63C9A4658689065E11CFEBF9B3EE6172CDEC3FFBF92FE922FEE6B4D909DF8CC32275A67CC95FDA41EA69A55E8979E8F3A6AB175ADA4ED0D1B332626EAE0353B94AB7B8B385B8DA000FFCF6B760D6ACCB6CC446EFBFE4341FF46DB3B9BC5E247F254828180FFA72D3137EC6C89FB7FB38CA20FF78872ECEF7A31FD852264B38D7208C89DBF2E5A374C156AA68F5A421260E75FA1F936AD9EF89F808319ECE546A92BDBBAFF982FBF0B0A378ACFC5DD987BFCC7A1A6CFF38B52ED6F007C97E0C1F93AF65CE1DDE451433C88EE63210FA1BFEE87FC30FDC3FFBF923E07FF662FDFAC6F15E1CFCD7779860BFAA68239E99BEC8524B0E4F36893E02D3E379F98A803B96B76FB224922766CA6748AD07FFD2EEC6B3B1F2CBF7BCA184FBA057DB60293070E805F8DEBFE859A28CED731BAA134C4CF660F261E86D03F0EA97B3B7D53CD20704F7C62F60EEF4FD07743F4C590C14442425896E3CB5FD3AC4036BA836E62A2024E30B2C9DCCCE4679EFFD46F21919B88C3B471379B539773B66C0B4C9F0F175DF2D3E7CC5AEA93771E50F4EF082C64922556D963B0DFF02C3D2CF4E92B2834E4CF3511F03F843EBE2F66F1BE873E84CC4DF387C1ECFC8516ADB43F4DD41EA25FA1B0E4E35749CDB4623CF62F20CCEBF4FF13630113D3051B63B4F72B3DCF5B20A0E728994F2EE7544BF485FF17ABBDBAAEBAF7D6B6C956B988FDED05564DD1796C1CF4B50043DD078FA1DDE310FA9A32A657EFA72F14E014579A906AF9684D8BD2E769B2A0B3E4C15549CDB462E083B24A09497862A202A8229FDCDE70C55652A6DC4C4D5FB5F2C36A06D692D67339FE1C48395D938D9BF485003D480393AE6814FD95626610817F7E29FD077D4E021D88EE4F8693673A3CBE9BDDE4A7AF99BCD0F78FFA56FFB52DBF34315182887F1A0A02634116C34A6731D3AAEB3E431952050AE2CFF48FB1C42FFBC12F26FD860D84899AAE65147D8E0D402475F4ABE9EBCE6D887EE184852E2D7B7EAF93BEA9812B9D7F355C7FC0FFC4C428882160F997BDF15C11846B9D8469FBA9E92BCFA035DB982F77D6225A04FFDFCFBFBE22FED7F41B56AE11FF8308F20F43E8A71C05134192E7D5F475D31A425FACB680CE15FBA3F6441CDF0AFB7F4BFEF0FFE2D6906BDB16A389893A300438030C4E5D9ACFC3D0DAC099F68A55726EBFB92E5DEFB28F5F436832C0AAA6DF60DABAE1F03F43E86B973E96FF0A7DBD9BB4A147B47C502306E22EFA8A49E4971A764379F0B6ED6D13FF37F01060AF4C596BA0256AA0505F87D58B5F582076E693F1967F7B4F65E90DF127EFB870BD585E6236F7FF94E887BAACD4EA51F44B227A03FD52B786E89BBB176EF47D5C2FFD82E22D7964324A331F4D87F84D4C3871A11DFEA72EAE3E59BE39F661DA55EC8F4557E2FCDB7BEA26F6D8CF1765233D7FBDCC807EAB423FD45FA54A47D12F75D01BE8EB48A0313F66A9EE52F6CF31FA168799FF0F6E3C3B75FE0D8BEC131313131313131309D17C8E89520CBF98A19D632EDC9C49C89A16AC48075D4B7E267C6D42546E571BF397FD4CB91EE92DFC83B34D9BDB9BB1CDC5DCD1AFD917E05FE74942CC40AB05B5255FC072E6F142FA53938FBB8A0A9D21F229A52BEBFCF7674417F5DB707832C47FC5FF34748D86D9E91521AFFBC7471ECD3925A5356DE08AA4D9350DFEDFEC82A59CDA0D5956DB7A5CBF092FF4FD9A7F625E7235FDF26C183A6FF4791A3F10E2270A7F4EACDE0BE05FE74942FCDCF6A3D544A50B7D1D368A672652E2C7133688F6969A3C443E260FA7FEAD3F23BA88EFEB23EB7D420866BD8B75782C4CDB0FB3EB4B4E69A52CB45F734AD236DF1AB8F4B329E1FD86D2E06F134B4C04F24FADCB6E450A779C47AB57F1C1726BBD081292E49BE944BDEE65DFDADD2937E49F359D8AEAF66315CB46ADFCC3090CE15F3383203949BB5D4F4A45ED08827C0EFD7C9CEBE7691434443EDA584AEA0A66D2033D7601FF2FBAE6DEF4FB7452D9F2F9BB79CB0FEDDF4ADD2146318F9DC2584C812751EB5742FC57B031DCA73F8611ED4236868678612955D8C802804EFBDDE3B7E4C03BA92184AB0B9687F821125BF0FB9B0ED5ED07DC547F8F23481BC2FFA615AA4716CBBEC6142BFEC7BA6D746414393141B35F3E3A083427AD1CCDF65BC4857F1FD652DD50FF6AFF7FDF7F1FD0F0FFC1F369F5206B06E1C79CAE49DBCD81494B1B53E321D846ABDBB788D2B561FE985C161381FCC13FDF6DBC96BF325878A3E988BAC4FE96F4F79EC2565C9221747588ACE0FF854CC6CE1F850E083568BFEEB6B0394CBADBC0BFA9E457DA1F3546E6B9FE3FAC2F0EBE55B495AC1902F5CB67C54A0DD585FC98A8A8877F933E5C2B37B3797C0711080AF3F78381C2D4AC02ED58F4207524697B74467D2D92A4A1DD6088FF0AB6A90A79AA86F5A36DBD833D1EED6F4CFC0F298FC28E50A4C54656F43CF6B78F225BCAF55DF6735D465504F9EBFE1D3B7F6405182828C87F08FF891A1341903C58EC247F36E461FAA9D2D7FDF2B9EE3F76CF44841358F7338E46C907F4E1DF74D7C4FA37E71FF18FEEDF86F81F832C0BF99E9F7266F0D0A0F64F3904206610AAE559A54248E969DAEDBAA66B740B019E67222C7F79EB76B24BFF4823D02BB8A6860BF62ACFF72CE1817222958AEC82F242DEA12DF9BB7AF84E5C99FC47D71CF983A665378D8CE6D0F96382D9CBDC5ECD9E56804C439E87FF4F6685BB0DFC276A42AA493F9D5265DEFC5A0406588BFCB650791EFADF2F9FDBFE7926D7C24980DBFE9B9E263FCD8BBFBCFE22C4DBB27EFD944ACEFE470B30445FCB87FB77DDCF190EF99F92BD27A141029873650ED0B74BCDB3F2B8BDB53BA806FF8F8A9808E4BF7D4A76CDEE9E2A066B947C97361781812DBECA9F841E360F01CB1E9F68CAD2A7952B2ABD0BFE4B4FB68D59FCC1D482DFF7540C84F4FF5401A0CF5CA23E41F7F228FEE11F4AFA793AA48A779D9D02F9E8067AAC2011AFEB7FBF7CB024C14478AD0A1A2E38C13698D350A4D4C685221FAE3AECFF0B7D64D217D6E7A2FF3084FCD8D797D170A37F9FE7CE595AFD753B4873A5F571FD8CB3B05A9A52DDB85A2FDF06BB5FA17EA79281AEF2EB2CFFD44C41DF9382638D325F4FEC1DA6973FC3DAD8A051828D443395DBF5FC88CB9576F6261366F63884BBEC677909FE85CE34F30FD360CA51FDAFB7D45400E119EAFCC3488F2EE6F1EBF964CACDF62BF4D3E9243176B06AF9132C25F9C03D9624235C847E77A5DF5FEE914F1285503F2461AED79B56A1D4BF9E9DF04EF908E713DD2FADFB88F94F23AFB0AC4EFAD0CF15BF8468F9A84ABE3A8B190AB6BFE0F79584730B5ED9120527703E5C563AFCA7D4B962381314447E55DCF5A7E0104EF0EBDB84F479ECCF94B5EF467AF7ED1AAD33903A57133F7D974DB82401288F7EE05EDE15E607FC4356F538FF6FF63EDC82D33960044781134B46D4C97FA226E807FCBF7AD7EFFF2BBDEF31017611C7EB4F39BF6B96CF6D5F95D61A7EC5F759B9FE24EB5E1DB9650FB47FD04BCFA7D07D849049FBB7E6FCA169BFC94F1AFD6BA5A975DBE1990505F6FFB269AD454C4B13B664FB97CD5E96ECBA33E69ABC5BAFAFFBEFFF26FDE707422ABA4952BDFEA0FDF925E6FBF51313902FB2A9B0759C023E504A20FF3E4EF05F891CA2D8D289427ABEB9B907A6FE443F44DD9C0951807F86FE1FC2695DBF6322FE20196307BFEB7720D04F2EA1CE35257CF8FF56F9F0972642FDA00C267D78D77EE55C2CFB6D1856B6693EB762676F0B62E95668FA9660B8A0FCFB622D4067FF62FA20281CF27F1AF26FBEEA9072EBCA5C3F514E137AA517217F937E688ABACD762DAE16E47F54ED98807462CBC5E5F443C48FFC1B7B805D7F4C31EA6D003DFC27D390C41D6B731E6CE92925FF5014C7DF548AFCB9967FF3FE3DED813DEFEA0E7A440E28A864C89D148E99B865410DF211AB1525AB5FE9F76BF82EF4C4C97F1D86FDC64716C388F2EF0B84F1B6F87F6D44B43EA8A5E7D7103CAF85BC58DECFBFDA627A66A17B5B807A09C9E3042C7FE818DF0DC9071248EF8AC48EE61F2A34243E41D733FD5084CC86060A08D210BA30FD53E72CD61CEBFC274DF0727B594201185C135388CA9F33C92257094D0671CFE0A5BF976726D7C824026347F6AEDB7BEB4645D37A1C2282075890219FB4D9D521196DF8681D1AA8E97B1674FC106C20780B11D17D84F043F3DF30780923651DD05D1395CF8ADF4CB798C4FC5DDCB28700CB28A46FD91F46ED6926788C20DDE5BAFF96DF42E793F0DDD8EA1B8D6E4857DED5E60A107F3C9F4332931B0369FE95F31F1D5C9210322989F93BEEA67F07B72D55AABEEF1FCD35F49D07F7FD5C1DA6D0B3605DA7BFFD23289F2DC52A88B8FDBFC9C090D639A185001330E4F370C947AB04BF5BA93AB4FC710A6DBF0DFE59F7113C409203DF6A185F8411254195E4EF4F0E03C209B005556E559062063C2CCCF391EF9FB95CD647C11B3417FEBEE37BB936BFEB4CC11DA39BF2FF78460C2EA83DBA845482D9BA35B845410B81E7EF3D723E9DCC26FD0CD10CB56EEB23C5D5A8FC80A93F038A3BFE4F12D61486B4CE092D6458C12BE4C3559BAA1B5D3FAD40DB6F437E46F7112234991E79B6EC064CA3AD2904DD350DCA0F8534F917F4FDFC639657F2F00BCEC7E019C4882B983CFC0FDDF57431E27CE64D0F1C5BD7E70C44FD73051BD99C7E747CD14238D637738F14BD9E8EA49BFD46C8860C64D3CF9C42430854E1FF88F9C75E7D31FCE6A0F8DDE7BBE37F2D6458D070F988AA0DFAF1F3992BD0F6AB555AE7D0B413107D84205FB7B12139A08DE89EEFCF64FA0D935FED0178FD42D00F39379E0E8B4E7CF0F9788FE38FBC79A3B27A95392EE8E75E20FFEC95BD7886C84331A8E8D8E3E89ABCF661F99F9C324414A2AF85C0EB9BDC23D1729A49DBEC57BCF834BAEFDEF4B94189AB512982C3B48716FF0E846DFAAC280C699D13BA7658D070F9DCADFD81AF6BBEB65FADD2DA178907741F65F9F3DCBE1A9203DA1741C3B5641A9C4F85C92D74ECE01FD233F51FA24B7FF9BA5EF6B368FC9F9A62091B740EF9E32854AAC2938243F24ABC2B8600CC1144ED03FDBFA61FED622D04A13FBA0AE7F574320EFBE5171F8F9AF30F4D60450080EB40FFAF75A0FFEA4F62C076048521AD73420B1916345C3EA2EF34FDB1D31F6DBFBA6B922FAA3CA0FB08F3234CB7D9781BFCBF30A21BEFCFE9A6AFFB57E4CF997E34F8840FD77246D320D5A45A51E779A79FF1BDEF6B25C2BF355481152BF13A2B80796B60FED9A41F5A425AF35F2516F2115ACDF2F14CBEEE67712CEC97CB635C0807364C29F5D3D70AC0CD2F8D5FE6423013F1EF4048144AEAF71E6821C382B47C6A6A93CBCAEC358831556DC87F4F1D0F81C98350CED4959507304088562C96FF89FA37537FC4FC42D08F9E9F536132F54527FFDAC943FF319FC215B6E3A78FA41CD311E32FDFF5CB673B5988DE8536627DE1B1CF19F9DAB08A64828D05D77084AC84000AA247209FE8393F2524224C7CAC0743C25F34E174607202B60FE20F6BEE79EAFCB59E3BD5436BE07DE806180F920A9916A4E553511B212BF87653BD4BF487FB7F611AE85F3C23F8143A8FBED64AB8E43F6D89D635F87F6144C7FE9CDDFF0BF9FBAB40EF082FC7F93D413F14DC6AF7C5FA0FD78DB23A761532AEFB962741E74EEB9BE2963F85953A57BC8EBA161CCDA45A81B6F80565C224EB1F5CB6651725017422778716513FFFA600D13BFD28C9FFB1E7423BE9C3B499F3E8C8A2F533D483A6009B5AD3080CB2DA82B47C3C6A031B876713CA090FA6E98FCDFF98FA238698C467A65A6A74D0EA7DA7FD39A2EB4393F7244F53448BD535371A5B3DF44D0FC0EB3BA2F628FF62DC17FA7FA36191CDC459CB4A5BAC9902E49FBA926F5DF7C3439CFCAFFB5E566DC50B8E66CA1F601E3A5D9049D9E90156DA58C514583DB83BF8CA1FE9F4F08FCDDE25017682D3A17C4596A69FFF75FF864BA8961F42ED436B1CA6068ECA2E3A0127A92D48CB271A5C41439CF407260FD1346120629395702F3C3AC09A0487ECFF4DE7E694CF4A9F4B301DFEBE409B004A5DF9C1B6D62EF867CCE6C4334EFE314DD6F439FFC03C8B6963A909C2F38B82202D75AEB8EB716BEC2A35F1A4E70B05D8260F7AB61885D9BADB59FCCC29D3C743F2C6167AB9AC426784943C8A54071FADA04B33D904E889A6CC29FA66FE59084EE16B705A8395CAF3AEA981EFF7FF42C2B0202D9F68F256536015D577C72E7F97EC97739BA2EF10FF73A45A79573CE3897CC4FC481B264FB21A5C2842FA927BB9EDB98B75FF3E37CA3F2F1456DC2F1CD4BA1F597CA519C75D2D389A4957F12EBA60F9A7BAFA19ED9F357D3193C2BB3A7DCD4EC664A36DB9F04AF32F41F9E0BCBA9112BDCFEFA22EF0CF1E495FB53A658DAAB608CA50225E6A78B1BABC52A81034D6947F2845CFF50A4B64CDACEBBF40A923A0FF1525D1BD13E27F08E06144134CF944E78C9AC295E25B7DB761FE5507C45E521EE1677407C9B768E2A97B1039F0BB5A4AD38A6A9ABFB0DF9227B4FD67C1C5690E57CABF09FD37FC4FCE3FB74EF72C0F5EF09C66399552E94531468BBB988F57E8DF691B8CA07C57BBBF1EFBA638FB9526E7CFF32F2D16C41815E76FBE0BCB154DA89436E76F0E31A5121802A8D2441CF9E18A163538FF07FDFE9A1060D4FF6BF580099C6A38072151898598AC000E818570A5F5C15305AB0022D56D34E9DF472F7FAFEBF1A366A6FF11CDAFBB08917534F9AF479E75E70682DC849292ACBCEB5ED5527A9EA70C9C0766FE33B296D296E88BF42018C3B6FFFE2B682E7B7E6014718C685A8520D2217521C81C4570A5799C36013C30B02EEEE5751F4490EFEDA42F923068DAA856207EB8D26A0827B2421E06EE858573A1EF6B068A9D4D20C46405E83B963FFC7FBF7CE0EE041D937EC3FCEB14EC12FBFD038FBCD0C92157D3F92F344BEA547E3873A63C90FF751FD60FC93F0FFF3FAA60605AF6F86A0CD97C55480003DF90BAE0DF068A05C1A4C93F6B4E7F4974B47C46F5857602977D963AA4091C3F883EADC8B084C7FEE1BF96CF409D31F91F02530837DABFD7291FAD18184486D0F700B16BB3CCCD69D72885BCD0262893FF350FB05B7486C266268B61A5B398839718342FFBA27FF31559AF95F2AB9D64397B7629AF6DDDF2A5ED9E4A91DFB8FCF31543A89D4E9CD77C6ADF53A31EE8973CD7D149DF4CC25CF218A3873EE70F936BC2AD86F81366053A60F2B66FE7E864586AECE8F85F0801BA34443EACEDCB3E940FA4EF0427094DA90A4E70E5CC9BA0C973E17E13BEEDDB4E4CAC2AC7E2BC5672861569F8AF2C58CDB6C8BA9FF552119C0245433A13A16221A322FC049EA3690718AA17EB8F3D74383BE7A103D3130996284A49E0353F06A4877E3D91D52BB79D3DAD930DF9671D56413E0DD912578DA3F3FF5A0877DADF2E6E35F87FC12D9CFC10FA7E9416B016DA692698E1BCB7260857DC8694EFF2DB2F8600A75EF1C865D26F58ED6288E58C12CFECFF4F97452AAB78BA2DF5E5759388B99416EA443F59D14CF1DFA5FAFB4DA5CD3FE69A69C8197AD65EEBDD54573F1E02E4FA94BBB32AF4F99953998B752B7E7811FB73D4D2B3138FA7DDA285F2C35155D1D4F45B21262BB817367B0C934F61257114FD10B49A41B06BBEADD1135CB575AB36BA28FF35E5B7D4BEC43F47D415252CB1EDEFAFD55C198FFBEDD2FA42F310D01C4606ECDA1C0B68FF897F4B8C7EA68DFF0B9651CA034D5DA3EAE187980834F478BD5D4E99EB8AC45CBEA4F621610A5B60F988397B33DBE65B21263DFCB3723EE8F78306C8C752B051F41BC06A268219218ABA1E3674ABB8D5363D5C2BDBF6D42EA00A1D50A828613D28ED99864F4C4C4C4CFC5534CC73FDF1528DFE4EBC1E48EB1AB9D285BECF0A4CE1F35ADE303E724C2ED29B9ED904BF556CDAC3B5E0F86AFA6D30BBEF74FE52D24641E70DFD0B860533AFAE77E2D7E25354A2619EEB77B64B7E7EE69021404C091BB8326B79A9906FF9D9832BED1070B277EEA269D43679F08FEC6DF47B505A4B321FBED0E7F0A6F33787929E45C053E89E3DCD8F4DFC6D7C8A4A24EB78DC4797E7E1FFBF1DC868FAC8DF2647D14387BDDF8B84AC8580602031304CE685F46C36BEBC807E2792F075BFE827D7FC6BE2539E872C249D4277E25A5D1F9FF8F3D8B4EE7645F99D2AA14D6F4CD9E3FF2DC21F4D1FFE6188FFBC16B69C8DC2E67E73FFB0D2FEF38192C16A116A3FE659AFA1DF0F1D84DCD4F6576C936EE8719ECBBCA28B135742A37EA1B14FBC0D9BA25E5794DFA912C9117DDBF56DE455EE4F184DFF9E7FFF328426F24BC385BC79E0BC2ECE5F8D158E089EB7B8FD65F4FB910641D1BFC2FF2361D5A333F7A1DB3201CD58257FA5F129B9E2093F924A5CD7CB51FA76F2BF0849DF789E32A6E0FCE7E7E6FA86D3BFD3F72FA3C8BE284D9766435C11AF5F7CE734860A87F737226FFF22FA43A027CBC25163B76A27E7FE1D71BCD8E4DD3AA844E46FFE47E48A27FC482AB15E169437AC3336C0B966FA357FF9D6EA7F57FF92EB82DFFFA2D7718571AD6A63B973F576297F1AE359B62E71357C9A060F8C5A100C98EBDAFE656BB3093CBE60FC15CF8CA23F04DB3666AA02F3C70507803C6C2D12C133FE5BEADFD4D87A164868CB49A7EC72CB2A8A7CBEFD29B9E2093F588113422AF136383D0F27B2FC7B8116DA709851D80BDE6ADBC0B3589FC644F72C69968667EA3086B20C6F74BE68F3CE25383A51B09C6D0A7F20FD21D8AA23FABCDD148C49360A3B97EAFD8B969A9C3091AF622AADA78496483E25573CE1C7664ACFECB3B25F18FF9F02BEEBBA5EB66B702203979211C1D5B1D97B2CC4193B294DC75C5DAD0F997B9024C0B58C0A060E8F97370109842479297677F6E694FE106826E1A257FC3E54CE008FF262E1FE42C7149BFD9B086AFE7940311435720D2D347C4AAE78C20F9D91F89DF99F53C077A1340C6418013325DFCBF07CB2077CEC5E89A55175E95A066EA1D942DF9CFF35FFBEACA77575FA43A0750CFE3F5990A81D36551F8356FAA51E83423E0538E68C968A468B9EFED79BFF11B9E289FF21E0BBBED4325DDB7CD79D7F2293A8A5EB36557FBBCE23F521F8193B59831366FAA336A2A4DC911029BE2FDBD238F9DD50EBEAF43D70A6D0B90ADE3F0C077EDC7D06967175FF82263F96B8126AD07CD5E34B05DBAAF46567E6F24B73C5134E5432BA3FCD5A18B00B9447EB174088F3991ACA2B36969FE2B2FFB880C9CCA85AB6EF8F72190EF1FFC7BA46CEBFF8BEACB975A7F49D443C4380291FA44FB3BBE504BE8626AE3782C20397D4D25CF0152B4D5D1CFE895CF144427D47C74F7317037C5742A7EF3A766B5CE4ADB14340744D99F9E1310E2C9D6E95A96D34DA9D30D37FD0EF1B5EF7A3B3EB3B4C4AD509617EBBA0FCFB32D1BA81F43DD8CC41F578F1799A1FADF4C16FA2D0ECFF973DB4BEE59F03A3F9424AE71B3E85ACACFEF573F80772C5130925AB39B5BB5FB8E9171ABE2DC35D6F9D87C0F06E8D430EB940FA6363FF26D52DBD40CC7CFF7BE7013EB9D9F9A34629C3DDFF9F0F0175E78C1FB847204D3F020E06F881AFFFFAE9EB6EC25D675F5CF69F9F33BADB2A893D8401E00A3244078955E07AE71E3B8228BB72AC2FE70AC0D44E5B8A87B5FE4CFC0F01FD3115BB6277EF5F063D4532016DA43D34E1276D0F306208607FDB58E8FCD22E3AA536EEFE5F3BF0CEC25BAA5E4DDF830B7EDBDA5DD8455FE8A757598032FDE238BC0E74B07861D6EEFC8D03408FC203D777263E0E50C2A8CE4723AB37600B6C3060916FEC01964832CAA2968E51E6701725FAF5EB5EFB318EB4D1A95CE9F725C752E628E2D5F43DD87808562112205B8776732EC822D8C0636BFC04866D802322A134E691149DE73FFC159CA86B594B4381C71BB04D64602683F2330B7D7AA689F70F012932B4297BCA3EC61DC3DFD0C2F9ED0BFFD4D708CAECE55E4DDF035E04B157379EB216BD013E7BB183F9BB75AC103F0327EC6FE011C9EC25B48187E72FF3FC87BF01A9EA91B2BEEC20B206C045A7B6E03A907872B0823E0BA481322FDAB65D1F8F233FD343C7BCC211257B7F05E58457D3F7801741EC2180C6C474E5FC12BED410BD534AF8F012067B7EE61FEE5D2B095CB1BF81186441243444DEE6F90F7F1142E5841256AC0CB9919F6EC137D2488456B479800AE01C74153C0A44C96EC27C4A0F732F2CA1EA7A531BF5DDC36B9D91150BC14C04F1617ACC6C7B7253A17321F4D0FC6AFAFD6019A2B0033439AFF32CFAC2A400E2783795861CAC56123D7FA9E03ECF7FF8A3806E979C43C5307F49FC9FF8145B5342D37FD1643DAE898D19E2DA96083AA60F3E2F2DEA858B80FF3CF88990156E8AE9DCE8FC37DD6A0C4C2117ADE5F06AFAFDD0C322D85BF8CBACBC159E24395AC76D67820BFD3EA9D93B4EA44E670A2107BE0D19F3FC873F8ABA73109A032DFD25FE3F710BFF8CE27C5D84D9A5DC269BA12EFDCBC126301EE91A31C6E907EEF1AD1DB77D7B270A0C1CAE43DC1DB50DE0D5F4FB6176BAF87E4D488F1FA8C3D45E505BACAEC1C0E7970F8F9B37DA8CEA7CFD31CF7FF8BF42045D6DFA3310A568166682AB93209A86174B4D83DDE9BA6EB4923BB0B11892CC1AE1FFB17C89183E9A03119E47ECCF8402F0DD51E6FF6AFA9DD06E39B18779A2F9C08DE62F15E21C5F899E45FF9AF2C1ACC7390468FD09C56F5AB16FF3FC87FF0DD824B57F78272A090DB014E58DE9D42920B4C6F29F28A38680CBFE03E29CF410E5B67FF5A39F69B0CD3B9DA5CC225DA8F74515A322C057D3EF8129F9241C2409313BD6CF889E62C0E79BDA8B14131ED6CFF0AAC7A927D74A1ECADFF20E404E85456439F19110537261A46F66060E76DD3F2035AFD748EA80A733C2CA2A39E752ED62F16E719E2D5318D7EA6D047DF16483E7E4354D5C457E43DC4543B2B638561C3899FF06FA52D4F16571F3CA0AC68BAAFC00F7B8E86EAE575464AA10BE50E627452B0CBD7A660FF0BBA1FD1B5A6EBF61749EF0A0621D458FF49003BD36819F5200E836E6CEA5E22428561BD9184B52AAD72BEC37B47D853D92B375ECB8D8ED44A58A351D684BAAC5940F37563B9CBA6B6569BC87BE73B4D5CEDF2C3CF82680CFBA3E8007FCC57C3EF1269C73852B5E9E300D198D152FFE92FD7B132F45C53A8ABE886CA464053F980004936891797552BBD376A651571E1C61B943AE7ADA9EFA911FD30EEA5CA4F9671DBC0FEA15F2C18AC642FEF975F46FFB2FB0744A1E0BB5AB95CFD1326CB856F239ECB7875CCD5A26FE18B47FE8BF22A2F8A94661088061EAE225F5CFE22A741A0A67478503E929A0C3FCDFF6DD26ECA3C2FEFF2985803114E1E8D8C243E42BBA00F4B57C3ABBD59C5B5D2EC7EF3636D3AFEF62C5C83EA42DA11CE9C4E762A0FE1F8AEA5B757A2978CBE5E59FA7C2F51259DB5AF773B1349DB6ABC8FF20D0EA21CB9338C17F9A88F1C30DA9392D01D405E73C4A3E9052E6FF77CF399C3E46C31E829C4529C9F0929F1618AA42BF6882A7ED9DF26998244E7C22B47FE8BCBEF45B9B104A9C44E7B6A35AC4E95666A6C7D0C442A1F980704A0D7B33F4C70BA8EE457D2DD8E6ACE358FA9D8E0E7DEA0C7890C3714E9039FBEAA4CF695B571B2C344C12273E11031335BC463084603F2ACB79313AC18D25F5955CA68C87EBEB2CA76B9425CE4B0BF70D62346518DD36E397926052EF571942FFBA6FD7F4BEA88E7A8B8E4A9820971A62D65E09FB3532B60B3A53D7B7B981E77F823B1F08D36FBFBFC9F927884D0ECD8ACDEB1AA71B4B4ADE43877057DACD6E6ECFB0ED94BC503D2C2C1978A8EDA60C0DFF1FDC7E53DFEC643AD5E1F479D9D4F5BAF2FC6D79CE9576E608E72F6A6FA65FD299E210A0F6F535D43BF159A8070951FB9A6B461313131313BF0788A944A4F7D37C4DFCDFB1EEFBFF3107141157949A88CD38429B0AEFC40709EA433DDB4A5FBED43FB1693304811B7D47C9C447356762220A5E088609687388D21413F3ABFAC478AA7D1D9FE5193ED1B3999A5F72FE50E09E466D26307F9F62E277402EEEDC6FBA34C7FF263591EA7F51BB3E1A9FE23F191FE7D93C9ACF65C804F63E7F9F62E2D7E0588DAD6A3EBEB0F323B9AF139B7AFEC64D1A3F0BDE03F0599EE1B33C9B53F3CDE8A561A31D709FBF4F31F13B706CB4B87FDB6DE59AC2D110F1F4CA29E5DFF385CE8F43E7CF3FEBC3E70FF26C7ECD2FEAEDA3516F1FF3F729267E018ECC274DD84BA561212F79300FF1B91690C0F9F3146A7E96FFFF14CF16D2FC9ADE36852E9BB65F76662EF3F729267E00691AFB3549FFD264792DEC8508D12FEDCD366B6CF80CE4EFE1E3F2E7023A25F20B3DDBBA9F7F6BEA7C49F3BFBF6437F5F6190E5DB64587E7FCBC65E2C7C0DE984BFDBBE65015F5AF3245BDD30A964FCB9F6BFC7ECFB6D24F75080D843337351FCBF1C65BFBD1D34E1EE6E7ED133F8B753F96E7BA5EBE9DCC7E65B57C45DA417C052C6AEFDF59F7E9F8A0FCB989DFEFD94A9A8FABA9F9F25BE3FCAD5F38CC4D4C5490BC4AE667BE9CFFEE995FED81D77486EDBFEA040FB7FFF7396F9F923FFF5C989AAF95D09439CF67C52B21FF7FDF7F7D9509FEB62CD9C41FC6A67E97E54B7571BDEFBF6BF30606707A95E0E1B3D21DC3F111F9F38F86D6FC34E1127A581902B659AAEF7913F7FCD757918F1ADAD0898922B67D261467225BFB361E5023B3F159E98EE1E8CF9F1B6733E647A871E489F8D379E4E0EB1AFE3694349FFFC27FD7144CBD0DCD5BF12DFC9605DD7F1A7B684327268A400C938000E69DB9770EA5041B6FE3E1B7A13F7F5E3943551FE8C49B5B3CE7CDBEAEE16F4345F3F147DC3287009EBAE2C9D0BC35D5A84F2AEE68D6C4440008051181A4FF46BFF0EA41FAB849B33127C23D483D2B7D4B7FF92BC77494341F031FB7BA14996F4B574424B43B2E55AA853CBAAD131336B6A02E57F5F727DE53A5CCC64C8476E2882D1FCF91D7BFE2A02A9ABF7D76678E7DF91020C492B64C84FCFF36B2309D19FF4F04D19CAE491358B6F11FF9CE748BB8C810FE4C9EF9A7B005B7D64FC2F5963FE1FFEB9ABF9D585218010F224F7937743AD6A1F67919DFDA89BF8B9E9321B5EEFDC839935BC4350D611C526C9986D1B1D7BFD12FA79ABF0D018541702342F3825442A76341ED8590C7B776E22FA2FF6460EC30C1B5E178CF7E6C07840A3FF327E2CC9F82F858B5B2985BDA208405E23FB9FFC7A3F9EBFE73307A044C4260A1A56BE874ACF4301F3731D39E131E8C3A199893EDF7FD30F337FF54136A6436FE4C9EF9A710DACF5371FEE65B3FDDB801706A3E8600B3B0D04024E4FFBF4A92EA7118C5FF78DBDBC429C69E0C0C2B8012C26F8C65BB82E3582D3284E9FF275E0ABFE62393C6FAC90328FF25744C5FB25F6CC74519DDD689BF83B12703A7C04FA85FDB219F6D405C2A7898F99F899722A4F93C03E2C21F53F0A4C9C903C21EEC3EFD9F1F7B32718ADBD09381318165F5E329C658E605905B384E53D977FEFF9975C689DF89A8E6C3BDD7AFA1EFD639BDC665745B27FE0EEE434F06466E536820277B5F74FEA7B9CE88B9F68BEA9D9800A29A6FEAAA7E37BAFF5F1FC1F182B64EFC11DC479F0CCC4E985591E370BD3FA48167939459EFFCF877E20D08687EAEB4E2795CB18EE06480334E5C5EDAEA898FC663F4C9C06905CAD4C3954FE927E71FCDCCF08B4CCDAC3119E0FBF7A04EFC0F71AEF956F0837D3EE2F9E8A415F385B4EF08D7D7B577E2D3A113E3FDD132E69ED82B58B936ACCC26F339A58CBDAC33049A780F429A5FB99AA7439C22CD1752D55C5ED4D8893F00FEDE1CD148E79E31EC0F871FAE94869D99E99553CACC434F732626FCF06B7ED122D4B9104E20959A147EC6FF13A7D0D9C8217B86B5FB2D5D9BE3FF0ACDE9FC277E10620858AA8ACA57CE6A36D4AB4F7B98F9FF899F023BE1CA63CDF97FF39648A24EE73FF123E0E527CF6EBA211BE4CC6FAEA7FF9FF8418875D8D22130219A95236578F5ED452D9A98F0406BFEE91119A1DD3E1AA5D39606366A62228A3411A81F021322583F526686FD13BF07D07C63D3723E04D43DFF7FE3CA631F]]>
              </BitmapFile>
            </BitmapFromFile>
          </Producers>
        </Bitmap>
        <Font Name="Font4" Bitmap="FontBitmap4" FirstChar="33" CharPixelWidth="31" CharPixelHeight="31" BorderPixels="1"/>
        <Material Name="FontMaterial4" Blend="1" Font="Font4"/>
      </Children>
    </Group> <!-- Font4Group -->

  </Content>
</ZApplication>

Edit:

I modified my example so that we can chose which sound to play using numbers 0 to 9, and R for random. This way, it is easier to discover what kind of sound crashes the program while holding the mouse click and enjoying the blip-blops.
Here are the results so far:
  • Power up can crash in preview
  • Explosions can crash in preview
  • Synth has high chance to crash
  • Tone can crash in preview
  • Random has high chance to enter an ugly infinite loop
Since pickup, laser, explosion, powerup, hit and blip seems to run pretty solid in release, I'm going to try those on Android and see how it holds.
Last edited by Ats on Thu May 02, 2024 9:44 pm, edited 4 times in total.
User avatar
Kjell
Posts: 1910
Joined: Sat Feb 23, 2008 11:15 pm

Re: SFXR

Post by Kjell »

Hi Ats,

The only "crashes" / errors i'm seeing on my end are "array access out of range" errors. You need to make sure your buffer array is large enough to fit the sound you're generating. But perhaps you're getting different errors?

K
User avatar
Ats
Posts: 702
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: SFXR

Post by Ats »

Hahaha, I've fallen into a new loophole.
All right. So from what I understand, the buffer size should be equal to the sample lenght, right?

Code: Select all

float S = SampleLength(); // Get the sample length
float L = S/44100; // Get the sample length in seconds

//

MySound.Length = L; // Set the Sound component length
MySample.Length = L; // Set the Sample component length
buffer.SizeDim1 = S; // Set buffer size

//

ResetSample(0); // Reset the synthesizer
SynthSample(0,S,3); // Synthesize the entire sample
mmm... Nope, this is still crashing. But buffer.SizeDim1 = S + 4; works fine :lol:
Kjell wrote:But perhaps you're getting different errors?
With the Random and Synth sounds, I can easily get this error:

Code: Select all

Callstack:
SynthSample
ZExpression
Infinite loop?
I didn't manage to obtain that bug when reducing the SynthSample quality to 2.
User avatar
Kjell
Posts: 1910
Joined: Sat Feb 23, 2008 11:15 pm

Re: SFXR

Post by Kjell »

Hi Ats,
Ats wrote: Thu May 02, 2024 1:04 pmNope, this is still crashing. But buffer.SizeDim1 = S + 4; works fine :lol:
Ah .. in that case it's probably a rounding-error caused by the SampleExpression. Try the following:

- Add a MySampleIndex ( type = int ) variable
- Change the SampleExpression to "this.Sample = buffer[MySampleIndex++];"
- Make sure to set MySampleIndex to 0 every time prior to using CallComponent(MySample)
Kjell wrote:I didn't manage to obtain that bug when reducing the SynthSample quality to 2.
That's because SFXR uses doubles ( 64-bit floating point ) for some variables, which is something ZGE doesn't support.

Edit: My bad .. it's been a while since i looked at the SFXR source ( 10+ years ) :) I double-checked and there isn't anything that could cause a infinite loop. However at quality=3 the super-sampling loop runs 8 times per sample, which could end up tripping the GuardLimit in ZGE if a sound is long enough.

K
User avatar
Ats
Posts: 702
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: SFXR

Post by Ats »

With the new method, I still had to add a little +1 to prevent crashing. But it's running just fine :wink:

Code: Select all

buffer.SizeDim1 = S + 1; // Set buffer size
MySampleIndex = 0;
Kjell wrote:That's because SFXR uses doubles ( 64-bit floating point ) for some variables, which is something ZGE doesn't support.
So I trimmed the extremely precise numbers jsfxr is giving, just in case: 0.023714355139274713 -> 0.02371435
But I've yet to find which line is producing the error in the SynthSample function, for(int si=0; si<sp; si++) loop, just to try to prevent the problem.


I updated the entire project code in my previous post to include onscreen buttons, and here's the android APK - 243 KB - I love how small it is when compared to the size of an apk generated by any other engine!
And... Yeah, we can clearly feel the sound generation lag :lol:

Since I can't manage to create cool sounds using the classic ZGE interface, I'm considering a different approach. Maybe I can generate multiple sfxr variations of each sound type (laser, explosion, beeps) at the start of my game and save them as separate samples. This way, the sounds should play smoothly during the game. Am I right?
Attachments
ZSFXR.0.1.0.apk
(243.14 KiB) Downloaded 187 times
User avatar
Ats
Posts: 702
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: SFXR

Post by Ats »

So I tried to initialize a bunch of sounds when starting the app, but there's something wrong with it and I don't know where...
So I'm wondering if I can really pass a Sample as a variable in a function, then do a RefreshContent on that variable, or if I messed up somewhere else with the calculation of the buffers. Or maybe I need a Sound object per Sample? I don't know... :?

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZSFXR" CustomScreenWidth="320" CustomScreenHeight="240" MouseVisible="255" FileVersion="2" AndroidPackageName="org.zgameeditor.zsfxr" AndroidPortrait="2">
  <OnLoaded>
    <ZLibrary Comment="Math">
      <Source>
<![CDATA[int irnd(int i) { return round(rnd() * i); }
float frnd(float f) { return rnd() * f; }
float sqr(float x) { return x * x; }
float cube(float x) { return x * x * x; }
int sign(float x) { return x < 0 ? -1 : 1; }
float rndr(float from, float to) { return rnd() * (to - from) + from; }]]>
      </Source>
    </ZLibrary>
    <ZLibrary Comment="SFXR">
      <Source>
<![CDATA[//

float SampleLength()
{
  return (p_env_attack*p_env_attack+
          p_env_sustain*p_env_sustain+
          p_env_decay*p_env_decay)*100000;
}

//

void ResetSample(int restart)
{
  // Base

  if(!restart)phase = 0;

  period = 100/(p_base_freq*p_base_freq+0.001);
  fperiod = period;
  fmaxperiod = 100/(p_freq_limit*p_freq_limit+0.001);

  fslide = 1-pow(p_freq_ramp,3)*0.01;
  fdslide = pow(p_freq_dramp,3)*-0.000001;

  // Square

  square_duty = 0.5-p_duty*0.5;
  square_slide = p_duty_ramp*-0.00005;

  // Arpeggio

  arp_time = 0;
  arp_limit = p_arp_speed != 1 ? pow(1-p_arp_speed,2)*20000+32 : 0;
  arp_mod = p_arp_mod >= 0 ? 1-pow(p_arp_mod,2)*0.9 : 1+pow(p_arp_mod,2)*10;

  if(!restart)
  {
    sampling = 1;

    // Filter

    fltp = 0;
    fltdp = 0;
    fltw = pow(p_lpf_freq,3)*0.1;
    fltw_d = 1+p_lpf_ramp*0.0001;

    fltdmp = 5/(1+pow(p_lpf_resonance,2)*20)*(0.01+fltw);
    if(fltdmp > 0.8)fltdmp = 0.8;

    fltphp = 0;
    flthp = pow(p_hpf_freq,2)*0.1;
    flthp_d = 1+p_hpf_ramp*0.0003;

    // Vibrato

    vib_phase = 0;
    vib_speed = pow(p_vib_speed,2)*0.01;
    vib_amp = p_vib_strength*0.5;

    // Envelope

    env_stage = 0;
    env_time = 0;

    env_length[0] = p_env_attack*p_env_attack*100000;
    env_length[1] = p_env_sustain*p_env_sustain*100000;
    env_length[2] = p_env_decay*p_env_decay*100000;

    // Phaser

    fphase = pow(p_pha_offset,2)*1020;
    if(p_pha_offset < 0)fphase *= -1;

    fdphase = pow(p_pha_ramp,2);
    if(p_pha_ramp < 0)fdphase *= -1;

    iphase = fphase;
    ipp = 0;

    // Noise

    fnoise = rnd()*2-1;
    ppnoise = 0;

    // Repeat

    rep_time = 0;
    rep_limit = p_repeat_speed ? pow(1-p_repeat_speed,2)*20000+32 : 0;
  }
}

//

void SynthSample(int start, int end, int quality)
{
  // Repeat

  float l_rep_time = rep_time;
  float l_rep_limit = rep_limit;

  // Arpeggio

  float l_arp_time = arp_time;
  float l_arp_mod = arp_mod;
  float l_arp_limit = arp_limit;

  // Base

  int l_wave_type = wave_type;

  float l_phase = phase;
  float l_period = period;
  float l_fperiod = fperiod;
  float l_fmaxperiod = fmaxperiod;
  float l_fslide = fslide;
  float l_fdslide = fdslide;

  // Square

  float l_square_duty = square_duty;
  float l_square_slide = square_slide;

  // Vibrato

  float l_vib_phase = vib_phase;
  float l_vib_speed = vib_speed;
  float l_vib_amp = vib_amp;

  // Envelope

  float l_env_vol;
  float l_env_length;

  float l_env_stage = env_stage;
  float l_env_time = env_time;

  float l_env_punch = p_env_punch;

  float l_env_length_0 = env_length[0];
  float l_env_length_1 = env_length[1];
  float l_env_length_2 = env_length[2];

  // Noise

  float l_fnoise = fnoise;
  int l_ppnoise = ppnoise;

  // Phaser

  float l_fphase = fphase;
  float l_fdphase = fdphase;

  int l_iphase = iphase;
  int l_ipp = ipp;

  // Filter

  float l_lpf_freq = p_lpf_freq;

  float l_fltp = fltp;
  float l_fltdp = fltdp;
  float l_fltdmp = fltdmp;

  float l_flthp = flthp;
  float l_flthp_d = flthp_d;

  float l_fltw = fltw;
  float l_fltw_d = fltw_d;

  float l_fltphp = fltphp;

  // Supersampler

  float sp = pow(2,quality);
  float ss = 8/sp;

  // Synth

  int l_sampling = sampling;

  for(int i=start; i<end; i++)
  {
    if(!l_sampling)
    {
      buffer[i] = 0;
      continue;
    }

    // Repeat

    if(l_rep_limit)
    {
      l_rep_time++;

      if(l_rep_time >= l_rep_limit)
      {
        l_rep_time = 0;
        ResetSample(1);

        // Local

        l_period = period;
        l_fperiod = fperiod;
        l_fslide = fslide;

        l_square_duty = square_duty;

        l_arp_time = arp_time;
        l_arp_limit = arp_limit;
      }
    }

    // Arpeggio

    l_arp_time++;

    if(l_arp_limit != 0 && l_arp_time >= l_arp_limit)
    {
      l_arp_limit = 0;
      l_fperiod *= l_arp_mod;
    }

    // Base

    l_fslide += l_fdslide;
    l_fperiod *= l_fslide;

    if(l_fperiod > l_fmaxperiod)
    {
      l_fperiod = l_fmaxperiod;
      l_sampling = 0;
    }

    // Square

    if(l_wave_type == 0)
    {
      l_square_duty += l_square_slide;
      if(l_square_duty < 0)l_square_duty = 0;
      if(l_square_duty > 0.5)l_square_duty = 0.5;
    }

    // Vibrato

    float rfperiod = l_fperiod;

    if(l_vib_amp > 0)
    {
      l_vib_phase += l_vib_speed;
      rfperiod = l_fperiod*(1+sin(l_vib_phase)*l_vib_amp);
    }

    l_period = rfperiod;
    if(l_period < 8)l_period = 8;

    // Envelope

    l_env_time++;

    switch(l_env_stage)
    {
      case 0: l_env_length = l_env_length_0; break;
      case 1: l_env_length = l_env_length_1; break;
      case 2: l_env_length = l_env_length_2; break;
    }

    if(l_env_time > l_env_length)
    {
      l_env_time = 0;
      l_env_stage++;
    }

    float tl = l_env_time ? l_env_time/l_env_length : 0;

    switch(l_env_stage)
    {
      case 0: l_env_vol = tl; break;
      case 1: l_env_vol = 1+(1-tl)*2*l_env_punch; break;
      case 2: l_env_vol = 1-tl; break;
    }

    // Phaser

    if(l_fphase || l_fdphase)
    {
      l_fphase += l_fdphase;
      l_iphase = abs(l_fphase);
      if(l_iphase > 1023)l_iphase = 1023;
    }

    // Filter

    if(l_flthp_d != 0)
    {
      l_flthp *= l_flthp_d;
      if(l_flthp < 0.00001)l_flthp = 0.00001;
      if(l_flthp > 0.1)l_flthp = 0.1;
    }

    // Supersampler

    float ssample = 0;

    for(int si=0; si<sp; si++)
    {
      float sample = 0;

      l_phase += ss;

      if(l_phase >= l_period)l_phase = l_phase-floor(l_phase/l_period)*l_period;

      float fp = l_phase/l_period;


      switch(l_wave_type)
      {
        case 0: sample = fp < l_square_duty ? -0.5 : 0.5; break;
        case 1: sample = 1-fp*2; break;
        case 2: sample = sin(fp*PI*2); break;
        case 3: int pnoise = fp*32;
                if(pnoise != l_ppnoise){l_fnoise = rnd()*2-1; l_ppnoise = pnoise;}
                sample = l_fnoise; break;
      }

      // Filter

      float pp = l_fltp;
      l_fltw *= l_fltw_d;

      if(l_fltw < 0)l_fltw = 0;
      if(l_fltw > 0.1)l_fltw = 0.1;

      if(l_lpf_freq != 1)
      {
        l_fltdp += (sample-l_fltp)*l_fltw;
        l_fltdp -= l_fltdp*l_fltdmp;
      }
      else
      {
        l_fltp = sample;
        l_fltdp = 0;
      }

      l_fltp += l_fltdp;

      l_fltphp += l_fltp-pp;
      l_fltphp -= l_fltphp*l_flthp;
      sample = l_fltphp;

      // Phaser

      if(l_fphase || l_fdphase)
      {
        phaser_buffer[l_ipp&1023] = sample;
        sample += phaser_buffer[(l_ipp-l_iphase+1024)&1023];
        l_ipp = (l_ipp+1)&1023;
      }

      // Accumulate

      ssample += sample;
    }

    buffer[i] = ssample*l_env_vol*sound_vol*2/sp;
  }

  // Repeat

  rep_time = l_rep_time;

  // Arpeggio

  arp_time = l_arp_time;
  arp_limit = l_arp_limit;

  // Base

  phase = l_phase;
  period = l_period;
  fperiod = l_fperiod;
  fslide = l_fslide;

  // Square

  square_duty = l_square_duty;

  // Vibrato

  vib_phase = l_vib_phase;

  // Envelope

  env_time = l_env_time;
  env_stage = l_env_stage;

  // Noise

  fnoise = l_fnoise;
  ppnoise = l_ppnoise;

  // Phaser

  fphase = l_fphase;
  iphase = l_iphase;

  ipp = l_ipp;

  // Filter

  fltw = l_fltw;
  flthp = l_flthp;

  fltp = l_fltp;
  fltdp = l_fltdp;
  fltphp = l_fltphp;

  // Synth

  sampling = l_sampling;
}]]>
      </Source>
    </ZLibrary>
    <ZLibrary Comment="SFXR Samples" HasInitializer="1">
      <Source>
<![CDATA[// Wave shapes
byte SQUARE = 0;
byte SAWTOOTH = 1;
byte SINE = 2;
byte NOISE = 3;

void setcurrentSound(int i)
{
  CurrentSoundType = i;
  switch (CurrentSoundType)
  {
    case 0: text.Text = "PICKUP COIN"; break;
    case 1: text.Text = "LASER SHOOT"; break;
    case 2: text.Text = "EXPLOSION"; break;
    case 3: text.Text = "POWERUP"; break;
    case 4: text.Text = "HIT HURT"; break;
    case 5: text.Text = "JUMP"; break;
    case 6: text.Text = "BLIP SELECT"; break;
    case 7: text.Text = "SYNTH"; break;
    case 8: text.Text = "TONE"; break;
    case 9: text.Text = "CLICK"; break;
    case 10: text.Text = "RANDOM"; break;
  }
}

void initSound()
{
  // Wave shape
  wave_type = SQUARE;

  // Envelope
  p_env_attack = 0;    // Attack time
  p_env_sustain = 0.3; // Sustain time
  p_env_punch = 0;     // Sustain punch
  p_env_decay = 0.4;   // Decay time

  // Tone
  p_base_freq = 0.3;  // Start frequency
  p_freq_limit = 0;   // Min frequency cutoff
  p_freq_ramp = 0;    // Slide (SIGNED)
  p_freq_dramp = 0;   // Delta slide (SIGNED)

  // Vibrato
  p_vib_strength = 0; // Vibrato depth
  p_vib_speed = 0;    // Vibrato speed

  // Tonal change
  p_arp_mod = 0;      // Change amount (SIGNED)
  p_arp_speed = 0;    // Change speed

  // Square wave duty (proportion of time signal is high vs. low)
  p_duty = 0;         // Square duty
  p_duty_ramp = 0;    // Duty sweep (SIGNED)

  // Repeat
  p_repeat_speed = 0; // Repeat speed

  // Flanger
  p_pha_offset = 0;   // Flanger offset (SIGNED)
  p_pha_ramp = 0;     // Flanger sweep (SIGNED)

  // Low-pass filter
  p_lpf_freq = 1;     // Low-pass filter cutoff
  p_lpf_ramp = 0;     // Low-pass filter cutoff sweep (SIGNED)
  p_lpf_resonance = 0;// Low-pass filter resonance

  // High-pass filter
  p_hpf_freq = 0;     // High-pass filter cutoff
  p_hpf_ramp = 0;     // High-pass filter cutoff sweep (SIGNED)

  // Sample parameters
  sound_vol = 0.5;
  // sample_rate = 44100;
  // sample_size = 8;
}


void pickupCoin() {
  wave_type = SAWTOOTH;
  p_base_freq = 0.4 + frnd(0.5);
  p_env_attack = 0;
  p_env_sustain = frnd(0.1);
  p_env_decay = 0.1 + frnd(0.4);
  p_env_punch = 0.3 + frnd(0.3);
  if (irnd(1)) {
    p_arp_speed = 0.5 + frnd(0.2);
    p_arp_mod = 0.2 + frnd(0.4);
  }
}

void laserShoot() {
  wave_type = irnd(2);
  if(wave_type == SINE && irnd(1))
    wave_type = irnd(1);
  if (irnd(2) == 0) {
    p_base_freq = 0.3 + frnd(0.6);
    p_freq_limit = frnd(0.1);
    p_freq_ramp = -0.35 - frnd(0.3);
  } else {
    p_base_freq = 0.5 + frnd(0.5);
    p_freq_limit = p_base_freq - 0.2 - frnd(0.6);
    if (p_freq_limit < 0.2) p_freq_limit = 0.2;
    p_freq_ramp = -0.15 - frnd(0.2);
  }
  if (wave_type == SAWTOOTH)
    p_duty = 1;
  if (irnd(1)) {
    p_duty = frnd(0.5);
    p_duty_ramp = frnd(0.2);
  } else {
    p_duty = 0.4 + frnd(0.5);
    p_duty_ramp = -frnd(0.7);
  }
  p_env_attack = 0;
  p_env_sustain = 0.1 + frnd(0.2);
  p_env_decay = frnd(0.4);
  if (irnd(1))
    p_env_punch = frnd(0.3);
  if (irnd(2) == 0) {
    p_pha_offset = frnd(0.2);
    p_pha_ramp = -frnd(0.2);
  }
  //if (irnd(1))
    p_hpf_freq = frnd(0.3);
}

void explosion() {
  wave_type = NOISE;
  if (irnd(1)) {
    p_base_freq = sqr(0.1 + frnd(0.4));
    p_freq_ramp = -0.1 + frnd(0.4);
  } else {
    p_base_freq = sqr(0.2 + frnd(0.7));
    p_freq_ramp = -0.2 - frnd(0.2);
  }
  if (irnd(4) == 0)
    p_freq_ramp = 0;
  if (irnd(2) == 0)
    p_repeat_speed = 0.3 + frnd(0.5);
  p_env_attack = 0;
  p_env_sustain = 0.1 + frnd(0.3);
  p_env_decay = frnd(0.5);
  if (irnd(1)) {
    p_pha_offset = -0.3 + frnd(0.9);
    p_pha_ramp = -frnd(0.3);
  }
  p_env_punch = 0.2 + frnd(0.6);
  if (irnd(1)) {
    p_vib_strength = frnd(0.7);
    p_vib_speed = frnd(0.6);
  }
  if (irnd(2) == 0) {
    p_arp_speed = 0.6 + frnd(0.3);
    p_arp_mod = 0.8 - frnd(1.6);
  }
}

void powerUp() {
  if (irnd(1)) {
    wave_type = SAWTOOTH;
    p_duty = 1;
  } else {
    p_duty = frnd(0.6);
  }
  p_base_freq = 0.2 + frnd(0.3);
  if (irnd(1)) {
    p_freq_ramp = 0.1 + frnd(0.4);
    p_repeat_speed = 0.4 + frnd(0.4);
  } else {
    p_freq_ramp = 0.05 + frnd(0.2);
    if (irnd(1)) {
      p_vib_strength = frnd(0.7);
      p_vib_speed = frnd(0.6);
    }
  }
  p_env_attack = 0;
  p_env_sustain = frnd(0.4);
  p_env_decay = 0.1 + frnd(0.4);
}

void hitHurt() {
  wave_type = irnd(2);
  if (wave_type == SINE)
    wave_type = NOISE;
  if (wave_type == SQUARE)
    p_duty = frnd(0.6);
  if (wave_type == SAWTOOTH)
    p_duty = 1;
  p_base_freq = 0.2 + frnd(0.6);
  p_freq_ramp = -0.3 - frnd(0.4);
  p_env_attack = 0;
  p_env_sustain = frnd(0.1);
  p_env_decay = 0.1 + frnd(0.2);
  if (irnd(1))
    p_hpf_freq = frnd(0.3);
}

void jump() {
  wave_type = SQUARE;
  p_duty = frnd(0.6);
  p_base_freq = 0.3 + frnd(0.3);
  p_freq_ramp = 0.1 + frnd(0.2);
  p_env_attack = 0;
  p_env_sustain = 0.1 + frnd(0.3);
  p_env_decay = 0.1 + frnd(0.2);
  if (irnd(1))
    p_hpf_freq = frnd(0.3);
  if (irnd(1))
    p_lpf_freq = 1 - frnd(0.6);
}

void blipSelect() {
  wave_type = irnd(1);
  if (wave_type == SQUARE)
    p_duty = frnd(0.6);
  else
    p_duty = 1;
  p_base_freq = 0.2 + frnd(0.4);
  p_env_attack = 0;
  p_env_sustain = 0.1 + frnd(0.1);
  p_env_decay = frnd(0.2);
  p_hpf_freq = 0.1;
}

void synth() {
  wave_type = irnd(1);

//  p_base_freq = [0.2723171360931539, 0.19255692561524382, 0.13615778746815113][];
  int bf = irnd(2);
  if (bf == 0) p_base_freq = 0.27231713;
  else if (bf == 1) p_base_freq = 0.19255692;
  else p_base_freq = 0.13615778;

  p_env_attack = irnd(4) > 3 ? frnd(0.5) : 0;
  p_env_sustain = frnd(1);
  p_env_punch = frnd(1);
  p_env_decay = frnd(0.9) + 0.1;

//  p_arp_mod = [0, 0, 0, 0, -0.3162, 0.7454, 0.7454][irnd(6)];
  int am = irnd(2);
  if (am == 4) p_arp_mod = -0.3162;
  else if (am == 5) p_arp_mod = 0.7454;
  else if (am == 6) p_arp_mod = 0.7454;
  else p_arp_mod = 0;

  p_arp_mod = 0.7454;
  p_arp_speed = frnd(0.5) + 0.4;
  p_duty = frnd(1);
  p_duty_ramp = irnd(2) == 2 ? frnd(1) : 0;

//  p_lpf_freq = [1, 0.9 * frnd(1) * frnd(1) + 0.1][irnd(1)];
  p_lpf_freq = irnd(1) ? 0.9 * frnd(1) * frnd(1) + 0.1 : 1;

  p_lpf_ramp = rndr(-1, 1);
  p_lpf_resonance = frnd(1);
  p_hpf_freq = irnd(3) == 3 ? frnd(1) : 0;
  p_hpf_ramp = irnd(3) == 3 ? frnd(1) : 0;
}

void tone() {
  wave_type = SINE;
  p_base_freq = 0.35173364; // 440 Hz
  p_env_attack = 0;
  p_env_sustain = 0.6641; // 1 sec
  p_env_decay = 0;
  p_env_punch = 0;
}

void click() {
  //const base = ["explosion", "hitHurt"][irnd(1)];
  //this[base]();
  if (irnd(1)) {
    p_freq_ramp = -0.5 + frnd(1.0);
  }
  if (irnd(1)) {
    p_env_sustain = (frnd(0.4) + 0.2) * p_env_sustain;
    p_env_decay = (frnd(0.4) + 0.2) * p_env_decay;
  }
  if (irnd(3) == 0) {
    p_env_attack = frnd(0.3);
  }
  p_base_freq = 1 - frnd(0.25);
  p_hpf_freq = 1 - frnd(0.1);
}

void random() {
  wave_type = irnd(3);
  if (irnd(1))
    p_base_freq = cube(frnd(2) - 1) + 0.5;
  else
    p_base_freq = sqr(frnd(1));
  p_freq_limit = 0;
  p_freq_ramp = pow(frnd(2) - 1, 5);
  if (p_base_freq > 0.7 && p_freq_ramp > 0.2)
    p_freq_ramp = -p_freq_ramp;
  if (p_base_freq < 0.2 && p_freq_ramp < -0.05)
    p_freq_ramp = -p_freq_ramp;
  p_freq_dramp = pow(frnd(2) - 1, 3);
  p_duty = frnd(2) - 1;
  p_duty_ramp = pow(frnd(2) - 1, 3);
  p_vib_strength = pow(frnd(2) - 1, 3);
  p_vib_speed = rndr(-1, 1);
  p_env_attack = cube(rndr(-1, 1));
  p_env_sustain = sqr(rndr(-1, 1));
  p_env_decay = rndr(-1, 1);
  p_env_punch = pow(frnd(0.8), 2);
  if (p_env_attack + p_env_sustain + p_env_decay < 0.2) {
    p_env_sustain += 0.2 + frnd(0.3);
    p_env_decay += 0.2 + frnd(0.3);
  }
  p_lpf_resonance = rndr(-1, 1);
  p_lpf_freq = 1 - pow(frnd(1), 3);
  p_lpf_ramp = pow(frnd(2) - 1, 3);
  if (p_lpf_freq < 0.1 && p_lpf_ramp < -0.05)
    p_lpf_ramp = -p_lpf_ramp;
  p_hpf_freq = pow(frnd(1), 5);
  p_hpf_ramp = pow(frnd(2) - 1, 5);
  p_pha_offset = pow(frnd(2) - 1, 3);
  p_pha_ramp = pow(frnd(2) - 1, 3);
  p_repeat_speed = frnd(2) - 1;
  p_arp_speed = frnd(2) - 1;
  p_arp_mod = frnd(2) - 1;
}

void mutate() {
  if (irnd(1)) p_base_freq += frnd(0.1) - 0.05;
  if (irnd(1)) p_freq_ramp += frnd(0.1) - 0.05;
  if (irnd(1)) p_freq_dramp += frnd(0.1) - 0.05;
  if (irnd(1)) p_duty += frnd(0.1) - 0.05;
  if (irnd(1)) p_duty_ramp += frnd(0.1) - 0.05;
  if (irnd(1)) p_vib_strength += frnd(0.1) - 0.05;
  if (irnd(1)) p_vib_speed += frnd(0.1) - 0.05;
  //if (irnd(1)) p_vib_delay += frnd(0.1) - 0.05;
  if (irnd(1)) p_env_attack += frnd(0.1) - 0.05;
  if (irnd(1)) p_env_sustain += frnd(0.1) - 0.05;
  if (irnd(1)) p_env_decay += frnd(0.1) - 0.05;
  if (irnd(1)) p_env_punch += frnd(0.1) - 0.05;
  if (irnd(1)) p_lpf_resonance += frnd(0.1) - 0.05;
  if (irnd(1)) p_lpf_freq += frnd(0.1) - 0.05;
  if (irnd(1)) p_lpf_ramp += frnd(0.1) - 0.05;
  if (irnd(1)) p_hpf_freq += frnd(0.1) - 0.05;
  if (irnd(1)) p_hpf_ramp += frnd(0.1) - 0.05;
  if (irnd(1)) p_pha_offset += frnd(0.1) - 0.05;
  if (irnd(1)) p_pha_ramp += frnd(0.1) - 0.05;
  if (irnd(1)) p_repeat_speed += frnd(0.1) - 0.05;
  if (irnd(1)) p_arp_speed += frnd(0.1) - 0.05;
  if (irnd(1)) p_arp_mod += frnd(0.1) - 0.05;
}]]>
      </Source>
    </ZLibrary>
    <ZLibrary Comment="Init Sound Sample">
      <Source>
<![CDATA[void initSoundSample(Sample sample)
{
  // Init sfxr parameters for the sound type
  initSound();
  switch(CurrentSoundType)
  {
      case 0: pickupCoin(); break;
      case 1: laserShoot(); break;
      case 2: explosion(); break;
      case 3: powerUp(); break;
      case 4: hitHurt(); break;
      case 5: jump(); break;
      case 6: blipSelect(); break;
      case 7: synth(); break;
      case 8: tone(); break;
      case 9: click(); break;
      case 10: random(); break; // !!! Can trigger an infinite loop in SynthSample()
  }

  float S = SampleLength(); // Get the sample length
  float L = S / 44100; // Get the sample length in seconds

  buffer.SizeDim1 = S + 1; // Set buffer size
  MySampleIndex = 0;
  
  sample.Length = L;

  ResetSample(0); // Reset the synthesizer
  SynthSample(0,S,2); // Synthesize the entire sample in buffer

  @RefreshContent(Component:sample);
}

void attachSampleToSound(Sample sample)
{
  MySound.Sample = sample;
  MySound.Length = sample.Length; // Set the Sound component length
}]]>
      </Source>
    </ZLibrary>
    <SetAppState State="Initialization"/>
  </OnLoaded>
  <States>
    <AppState Name="Initialization">
      <OnStart>
        <ZExpression Expression="CurrentSoundType = -1;
setRandomSeed(getSystemTime());"/>
      </OnStart>
      <OnUpdate>
        <Condition Comment="Init over?" Expression="return CurrentSoundType == 1;">
          <OnTrue>
            <SetAppState State="Demo"/>
          </OnTrue>
          <OnFalse>
            <ZExpression>
              <Expression>
<![CDATA[// Display text one turn before init sound
int currentInit = CurrentSoundType + 1;
switch(currentInit)
{
    case 0: textInit.Text = "INIT PICKUPCOIN"; break;
    case 1: textInit.Text = "INIT LASERSHOT"; break;
    case 2: textInit.Text = "INIT EXPLOSION"; break;
}

if (CurrentSoundType > -1)
{
  for (int n = 0; n < 10; n ++)
  {
    trace("Init "+intToStr(CurrentSoundType)+" "+intToStr(n));

    // Set the Sample component length
    switch(CurrentSoundType)
    {
      case 0:
      switch(n)
      {
        case 0: initSoundSample(Sample_PickupCoin_0); break;
        case 1: initSoundSample(Sample_PickupCoin_1); break;
        case 2: initSoundSample(Sample_PickupCoin_2); break;
        case 3: initSoundSample(Sample_PickupCoin_3); break;
        case 4: initSoundSample(Sample_PickupCoin_4); break;
        case 5: initSoundSample(Sample_PickupCoin_5); break;
        case 6: initSoundSample(Sample_PickupCoin_6); break;
        case 7: initSoundSample(Sample_PickupCoin_7); break;
        case 8: initSoundSample(Sample_PickupCoin_8); break;
        case 9: initSoundSample(Sample_PickupCoin_9); break;
      }
      break;
    }
  }
}

CurrentSoundType ++;]]>
              </Expression>
            </ZExpression>
          </OnFalse>
        </Condition>
      </OnUpdate>
      <OnRender>
        <RenderText Name="textInit" Text="INIT LASERSHOT"/>
      </OnRender>
    </AppState>
    <AppState Name="Demo">
      <Definitions>
        <Sound Name="MySound" Length="0.5252" Volume="0.29" Mod0Active="1" Mod0Destination="1" Mod0Amount="1" Env0Active="1" Env0ReleaseTime="0.2" SampleRepeatPosition="-1" UseSampleHz="255"/>
        <Variable Name="MySampleIndex" Type="1"/>
      </Definitions>
      <OnStart>
        <ZExpression>
          <Expression>
<![CDATA[setcurrentSound(0);
CurrentSoundNumber = 0;]]>
          </Expression>
        </ZExpression>
      </OnStart>
      <OnUpdate>
        <KeyPress Comment="left click play sound" Keys="{" RepeatDelay="0.2">
          <OnPressed>
            <ZExpression Comment="Sound library">
              <Expression>
<![CDATA[sound_vol = 0.5;

playSound = 1;]]>
              </Expression>
            </ZExpression>
          </OnPressed>
        </KeyPress>
        <KeyPress Comment="right click mutate sound" Keys="}" RepeatDelay="0.2">
          <OnPressed>
            <ZExpression Comment="Sound library">
              <Expression>
<![CDATA[mutate();

playSound = 1;]]>
              </Expression>
            </ZExpression>
          </OnPressed>
        </KeyPress>
        <Condition Comment="playSound?" Expression="return playSound;">
          <OnTrue>
            <ZExpression>
              <Expression>
<![CDATA[playSound = 0;

trace("Play "+intToStr(CurrentSoundType)+" "+intToStr(CurrentSoundNumber));

switch(CurrentSoundType)
{
  case 0:
  switch(CurrentSoundNumber)
  {
    case 0: attachSampleToSound(Sample_PickupCoin_0); break;
    case 1: attachSampleToSound(Sample_PickupCoin_1); break;
    case 2: attachSampleToSound(Sample_PickupCoin_2); break;
    case 3: attachSampleToSound(Sample_PickupCoin_3); break;
    case 4: attachSampleToSound(Sample_PickupCoin_4); break;
    case 5: attachSampleToSound(Sample_PickupCoin_5); break;
    case 6: attachSampleToSound(Sample_PickupCoin_6); break;
    case 7: attachSampleToSound(Sample_PickupCoin_7); break;
    case 8: attachSampleToSound(Sample_PickupCoin_8); break;
    case 9: attachSampleToSound(Sample_PickupCoin_9); break;
  }
  break;
}

CurrentSoundNumber ++;
if (CurrentSoundNumber == 10) CurrentSoundNumber = 0;]]>
              </Expression>
            </ZExpression>
            <PlaySound Sound="MySound"/>
          </OnTrue>
        </Condition>
      </OnUpdate>
      <OnRender>
        <RenderText Name="text" Text="PICKUP COIN"/>
      </OnRender>
    </AppState>
  </States>
  <Content>
    <Variable Name="CurrentSoundType" Type="1"/>
    <Variable Name="CurrentSoundNumber" Type="1"/>
    <Variable Name="playSound" Type="4"/>
    <Group Comment="SFXR">
      <Children>
        <Group Comment="Sample">
          <Children>
            <Group Comment="Synth">
              <Children>
                <Variable Name="sampling" Type="1"/>
                <Array Name="buffer" SizeDim1="1364"/>
              </Children>
            </Group>
            <Group Comment="Base">
              <Children>
                <Variable Name="phase"/>
                <Variable Name="period"/>
                <Variable Name="fperiod"/>
                <Variable Name="fmaxperiod"/>
                <Variable Name="fslide"/>
                <Variable Name="fdslide"/>
              </Children>
            </Group>
            <Group Comment="Square">
              <Children>
                <Variable Name="square_duty"/>
                <Variable Name="square_slide"/>
              </Children>
            </Group>
            <Group Comment="Vibrato">
              <Children>
                <Variable Name="vib_phase"/>
                <Variable Name="vib_speed"/>
                <Variable Name="vib_amp"/>
              </Children>
            </Group>
            <Group Comment="Envelope">
              <Children>
                <Variable Name="env_stage"/>
                <Variable Name="env_time"/>
                <Array Name="env_length" SizeDim1="3"/>
              </Children>
            </Group>
            <Group Comment="Filter">
              <Children>
                <Variable Name="fltp"/>
                <Variable Name="fltdp"/>
                <Variable Name="fltw"/>
                <Variable Name="fltw_d"/>
                <Variable Name="fltdmp"/>
                <Variable Name="fltphp"/>
                <Variable Name="flthp"/>
                <Variable Name="flthp_d"/>
              </Children>
            </Group>
            <Group Comment="Noise">
              <Children>
                <Variable Name="fnoise"/>
                <Variable Name="ppnoise" Type="1"/>
              </Children>
            </Group>
            <Group Comment="Phaser">
              <Children>
                <Variable Name="fphase"/>
                <Variable Name="fdphase"/>
                <Variable Name="iphase" Type="1"/>
                <Variable Name="ipp" Type="1"/>
                <Array Name="phaser_buffer" SizeDim1="1024"/>
              </Children>
            </Group>
            <Group Comment="Repeat">
              <Children>
                <Variable Name="rep_time"/>
                <Variable Name="rep_limit"/>
              </Children>
            </Group>
            <Group Comment="Arpeggio">
              <Children>
                <Variable Name="arp_time"/>
                <Variable Name="arp_mod"/>
                <Variable Name="arp_limit"/>
              </Children>
            </Group>
          </Children>
        </Group>
        <Group Comment="Parameters">
          <Children>
            <Group Comment="Base">
              <Children>
                <Variable Name="wave_type" Type="1"/>
                <Variable Name="sound_vol"/>
                <Variable Name="p_base_freq"/>
                <Variable Name="p_freq_limit"/>
                <Variable Name="p_freq_ramp"/>
                <Variable Name="p_freq_dramp"/>
              </Children>
            </Group>
            <Group Comment="Square">
              <Children>
                <Variable Name="p_duty"/>
                <Variable Name="p_duty_ramp"/>
              </Children>
            </Group>
            <Group Comment="Vibrato">
              <Children>
                <Variable Name="p_vib_speed"/>
                <Variable Name="p_vib_strength"/>
              </Children>
            </Group>
            <Group Comment="Envelope">
              <Children>
                <Variable Name="p_env_attack"/>
                <Variable Name="p_env_sustain"/>
                <Variable Name="p_env_decay"/>
                <Variable Name="p_env_punch"/>
              </Children>
            </Group>
            <Group Comment="Filter">
              <Children>
                <Variable Name="p_lpf_freq"/>
                <Variable Name="p_lpf_ramp"/>
                <Variable Name="p_lpf_resonance"/>
                <Variable Name="p_hpf_freq"/>
                <Variable Name="p_hpf_ramp"/>
              </Children>
            </Group>
            <Group Comment="Phaser">
              <Children>
                <Variable Name="p_pha_offset"/>
                <Variable Name="p_pha_ramp"/>
              </Children>
            </Group>
            <Group Comment="Repeat">
              <Children>
                <Variable Name="p_repeat_speed"/>
              </Children>
            </Group>
            <Group Comment="Arpeggio">
              <Children>
                <Variable Name="p_arp_speed"/>
                <Variable Name="p_arp_mod"/>
              </Children>
            </Group>
          </Children>
        </Group>
      </Children>
    </Group>
    <Group Comment="Samples">
      <Children>
        <Group Comment="0 - Pickup Coin">
          <Children>
            <Sample Name="Sample_PickupCoin_0" Length="0.2419">
              <Producers>
                <SampleExpression>
                  <Expression>
<![CDATA[//

this.Sample = buffer[MySampleIndex++];]]>
                  </Expression>
                </SampleExpression>
              </Producers>
            </Sample>
            <Sample Name="Sample_PickupCoin_1" Length="0.0957">
              <Producers>
                <SampleExpression>
                  <Expression>
<![CDATA[//

this.Sample = buffer[MySampleIndex++];]]>
                  </Expression>
                </SampleExpression>
              </Producers>
            </Sample>
            <Sample Name="Sample_PickupCoin_2" Length="0.5112">
              <Producers>
                <SampleExpression>
                  <Expression>
<![CDATA[//

this.Sample = buffer[MySampleIndex++];]]>
                  </Expression>
                </SampleExpression>
              </Producers>
            </Sample>
            <Sample Name="Sample_PickupCoin_3" Length="0.5689">
              <Producers>
                <SampleExpression>
                  <Expression>
<![CDATA[//

this.Sample = buffer[MySampleIndex++];]]>
                  </Expression>
                </SampleExpression>
              </Producers>
            </Sample>
            <Sample Name="Sample_PickupCoin_4" Length="0.3633">
              <Producers>
                <SampleExpression>
                  <Expression>
<![CDATA[//

this.Sample = buffer[MySampleIndex++];]]>
                  </Expression>
                </SampleExpression>
              </Producers>
            </Sample>
            <Sample Name="Sample_PickupCoin_5" Length="0.1103">
              <Producers>
                <SampleExpression>
                  <Expression>
<![CDATA[//

this.Sample = buffer[MySampleIndex++];]]>
                  </Expression>
                </SampleExpression>
              </Producers>
            </Sample>
            <Sample Name="Sample_PickupCoin_6" Length="0.4319">
              <Producers>
                <SampleExpression>
                  <Expression>
<![CDATA[//

this.Sample = buffer[MySampleIndex++];]]>
                  </Expression>
                </SampleExpression>
              </Producers>
            </Sample>
            <Sample Name="Sample_PickupCoin_7" Length="0.2736">
              <Producers>
                <SampleExpression>
                  <Expression>
<![CDATA[//

this.Sample = buffer[MySampleIndex++];]]>
                  </Expression>
                </SampleExpression>
              </Producers>
            </Sample>
            <Sample Name="Sample_PickupCoin_8" Length="0.5705">
              <Producers>
                <SampleExpression>
                  <Expression>
<![CDATA[//

this.Sample = buffer[MySampleIndex++];]]>
                  </Expression>
                </SampleExpression>
              </Producers>
            </Sample>
            <Sample Name="Sample_PickupCoin_9" Length="0.0309">
              <Producers>
                <SampleExpression>
                  <Expression>
<![CDATA[//

this.Sample = buffer[MySampleIndex++];]]>
                  </Expression>
                </SampleExpression>
              </Producers>
            </Sample>
          </Children>
        </Group>
      </Children>
    </Group>
  </Content>
</ZApplication>
Last edited by Ats on Fri May 03, 2024 9:49 am, edited 3 times in total.
User avatar
VilleK
Site Admin
Posts: 2316
Joined: Mon Jan 15, 2007 4:50 pm
Location: Stockholm, Sweden
Contact:

Re: SFXR

Post by VilleK »

Ats wrote: Thu May 02, 2024 11:04 pm So I tried to initialize a bunch of sounds when starting the app, but there's something wrong with it and I don't know where...
I tried the project and got a crash which turned out to be a problem in ZGEs code that shows the callstack. I've fixed that bug and now it shows array access error.

Code: Select all

Callstack:
SampleExpression
Array access outside range: buffer 0 0 4571
User avatar
Ats
Posts: 702
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: SFXR

Post by Ats »

Thanks Ville. I think I'm getting pretty good at randomly finding bugs in ZGE :lol:

So, setting the buffer.SizeDim1 before the sample.Length seems to fix the crash that could occur during the initialization.
(I update the project above)

Code: Select all

buffer.SizeDim1 = S + 1; // Set buffer size
MySampleIndex = 0;
sample.Length = L;
Edit:
Nope. My fix didn't last long :lol:
But setting buffer.SizeDim1 = (insert big number here) at the start of the project's initialization is working. That's weird because there are no access to that array before initSoundSample function sets its size, depending on the SampleLength.

But I've yet to find the quirk with that same buffer when playing the sound.

Is it possible that all the Samples are affected by the last buffer modification when a single SampleExpression is refreshed, maybe?
this.Sample = buffer[MySampleIndex++];
Because initializing only one sound is working well.
Post Reply