Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Finding zero crossings in a sample (to eliminate clicking)

This thread is locked; no one can reply to it. rss feed Print
Finding zero crossings in a sample (to eliminate clicking)
000000
Member #23,519
October 2022

When I stop a sample (or a sample instance), I often get a click because the waveform doesn't end at a zero crossing. Likewise, when I try to apply effects such as a fade-out, I get a lot of clicks because the gain changes don't match the zero crossings. How can I tell if I am at a zero crossing or do something at the next zero crossing? I couldn't find anything like this in the docs, so maybe there is another approach?

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

If you're applying a fade, you shouldn't get any clicks at all, as the values tend toward "zero" for your configuration. Are you using stereo or mono? Try showing some code. Are you using the audio stream routines, or the gain adjustment functions?

000000
Member #23,519
October 2022

Hey, I'm using the gain adjustment functions. I implemented the fade out with steps and everything is in mono. The code below is a breakdown of how my program works and produces an endless loop of fading beeps with clicks as can be heard and seen in the attachments.

#SelectExpand
1#include <ctype.h> 2#include <unistd.h> 3#include <stdlib.h> 4#include <allegro5/allegro.h> 5#include <allegro5/allegro_audio.h> 6#include <allegro5/allegro_acodec.h> 7 8ALLEGRO_SAMPLE *sample; 9ALLEGRO_SAMPLE_INSTANCE *sample_instance; 10 11void fade_out(int steps) 12{ 13 int i; 14 for(i=steps;i>0;i--) { 15 al_set_sample_instance_gain(sample_instance, (float)i/steps); 16 usleep(200000/steps); 17 } 18 al_set_sample_instance_gain(sample_instance, 0); 19} 20 21void play() 22{ 23 int steps = 8; 24 25 al_stop_sample_instance(sample_instance); 26 al_play_sample_instance(sample_instance); 27 al_run_detached_thread(fade_out, steps); 28} 29 30int main(int argc, char * argv[]) 31{ 32 al_install_audio(); 33 al_init_acodec_addon(); 34 al_reserve_samples(16); 35 36 sample = al_load_sample("sine.wav"); 37 sample_instance = al_create_sample_instance(sample); 38 al_attach_sample_instance_to_mixer(sample_instance, al_get_default_mixer()); 39 40 while(true) { 41 play(); 42 usleep(1000000); 43 } 44}

Dizzy Egg
Member #10,824
March 2009
avatar

I think this may just come down to timings and such, unfortunately I couldn't get your code to compile, so I had to change things up a bit, but this plays a smooth repeating "ping" using a sine wave:

#SelectExpand
1#include <ctype.h> 2#include <unistd.h> 3#include <stdlib.h> 4#include <allegro5/allegro.h> 5#include <allegro5/allegro_audio.h> 6#include <allegro5/allegro_acodec.h> 7 8 9ALLEGRO_SAMPLE *sample; 10ALLEGRO_SAMPLE_INSTANCE *sample_instance; 11 12 13 14void *fade_out(void *steps) 15{ 16 float fsteps = static_cast<int>(reinterpret_cast<intptr_t>(steps)); 17 18 for(float i=fsteps;i>0.0f;i-=0.2f) { 19 al_set_sample_instance_gain(sample_instance, i / fsteps); 20 al_rest(0.001); 21 } 22 al_set_sample_instance_gain(sample_instance, 0); 23 return NULL; 24} 25 26void play() 27{ 28 int steps = 8; 29 30 al_stop_sample_instance(sample_instance); 31 al_set_sample_instance_gain(sample_instance, 1.0); 32 al_play_sample_instance(sample_instance); 33 al_run_detached_thread(fade_out, reinterpret_cast<void *>(static_cast<intptr_t>(steps))); 34} 35 36int main(int argc, char * argv[]) 37{ 38 al_install_audio(); 39 al_init_acodec_addon(); 40 al_reserve_samples(16); 41 42 sample = al_load_sample("sine.wav"); 43 sample_instance = al_create_sample_instance(sample); 44 al_attach_sample_instance_to_mixer(sample_instance, al_get_default_mixer()); 45 46 while(true) { 47 play(); 48 al_rest(1.0); 49 } 50}

It may be that you're delaying too short a time or doing to big a jump (or both) in your fade loop...

I've attached a working example with sine.wav in the attachment.

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

000000
Member #23,519
October 2022

I could test the binary from Dizzy Egg and it seems to be pretty smooth as far as I can tell, but I couldn't compile it to tinker with the values. al_rest() always segfaults for me under Linux btw for some reason.

You shouldn't get any clicks at all, as the values tend toward "zero" for your configuration

With what configuration exactly?

I guess when I want to stop a sample without clicking, I should also do a short fade out then instead (or before) using al_stop_sample_instance()?

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Mark Oates
Member #1,146
March 2001
avatar

You could capture the output through a mixer with https://www.allegro.cc/manual/5/al_set_mixer_postprocess_callback, and create your own gain filter. You would capture the and compare a previous sample with the current sample, if there was a cross (negative sample value to positive, or vice-versa) you could interpolate the gain reduction at that sample point.

If you absolutely need for it to be exact, this would be the way to go. It would do exactly what you're looking for, but you would no longer be using the native gain functions and it would be more complicated.

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

000000
Member #23,519
October 2022

I haven't implemented Mark's suggestion yet, it's certainly more complex than I hoped. Fading out the sample beforehand doesn't fit my use case unfortunately. Anyway, thanks for the help so far!

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

@Mark Oates - you should link to the official docs, allegro.cc's docs are way outdated.

https://liballeg.org/a5docs/trunk/audio.html#al_set_mixer_postprocess_callback

It looks like you get 'buf' with the sample data, sample count, and the user data you passed into the function.

So take the format of the mixer, the buf data and apply a fade whenever you've reached a certain time in the sample. You'll have to measure the sample count or time of the sample before hand and when you're ready, simply apply a multiplication factor to your sound data, taking care of data types appropriately.

Go to: