|
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
|
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? My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
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. 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
|
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: 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.
---------------------------------------------------- |
Edgar Reynaldo
Major Reynaldo
May 2007
|
If you're fading out with steps, the fewer steps you use, the more it will click, due to the nature of the multiplication. You could try using more steps less often to lessen the impact. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
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. Edgar Reynaldo said: 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
|
For any configuration the values tend toward the middle value of the range, which is zero for signed values and mid for unsigned values. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Mark Oates
Member #1,146
March 2001
|
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. -- |
Edgar Reynaldo
Major Reynaldo
May 2007
|
Bump for status report. It would probably be easier to manually fade out each sample in your favorite sound editor than doing it programmatically for each sample. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
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
|
@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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
|