Open charlesmulder opened 1 week ago
Generally speaking, pops when looping occur because the loop transition does not match the zero-crossing area of the wave.
But in your case, I tested your code, and itself warns about Short Writes to the buffer.
Declaring the buffer as:
unsigned char buffer[32];
instead of:
unsigned char buffer[24*1024];
... to match your requested period size results in a continuous tone for me.
Ah Geraldo. Thank you so, so much. I'll do some more reading on frames, period and buffer to try and make sense of it. Will post my understanding here. Would really appreciate if you could check it over. Thanks again.
Ok. Tested with buffer size of 32 and the pop is indeed gone, but the tone is wrong.
pops when looping occur because the loop transition does not match the zero-crossing area of the wave
This was my thinking also.
I'll do some calcs and post them here.
I have been unable to set period size and buffer size using the default device. Switched to using more specific device identifier like hw:1,0. Started getting some useful errors.
Looks like the soundcard doesn't support unsigned 8 bit format or single channel.
So, I guess I need to adapt the code to suit the soundcard or maybe try using the plughw device? Not sure, will experiment a bit.
What is strange to me is that the command says it's playing Unsigned 8 bit rate, Mono
, but the HW Params
doesn't list the format or channels as a valid option?
$ aplay -v -D hw:1,0 /dev/zero --dump-hw-params
Playing raw data '/dev/zero' : Unsigned 8 bit, Rate 8000 Hz, Mono
HW Params of device "hw:1,0":
--------------------
ACCESS: MMAP_INTERLEAVED RW_INTERLEAVED
FORMAT: S16_LE S32_LE
SUBFORMAT: STD MSBITS_MAX
SAMPLE_BITS: [16 32]
FRAME_BITS: [32 128]
CHANNELS: [2 4]
RATE: [44100 192000]
PERIOD_TIME: (41 11888617)
PERIOD_SIZE: [8 524288]
PERIOD_BYTES: [128 2097152]
PERIODS: [2 32]
BUFFER_TIME: (83 23777234)
BUFFER_SIZE: [16 1048576]
BUFFER_BYTES: [128 4194304]
TICK_TIME: ALL
--------------------
aplay: set_params:1387: Sample format non available
Available formats:
- S16_LE
- S32_LE
- ```
Here's an adapted version of your code, Charles:
#include <stdio.h>
#include <alsa/asoundlib.h>
#include <math.h>
#include <limits.h>
int sinuc( float );
#define TWOPI 2*M_PI
#define ALSA_INFO 1
static char *device = "hw:2"; /* playback device */
int buffer[44100];
int main(void) {
// ALSA playback related
int err;
snd_pcm_t *playback_handle; // pcm
snd_pcm_hw_params_t *hw_params;
unsigned int val, val2;
int dir;
snd_pcm_uframes_t period_size;
snd_pcm_format_t format;
// Wavetable related
unsigned int N = 2004;
float f = 440; // A4 note
//float f = 261.626; // C4 note
unsigned int fs = 44100;
// playback
if ((err = snd_pcm_open(&playback_handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
// ALSA configuration
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(&hw_params);
/* Fill it in with default values. */
snd_pcm_hw_params_any(playback_handle, hw_params);
/* Set the desired hardware parameters. */
/* Interleaved mode */
snd_pcm_hw_params_set_access(playback_handle, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED);
/* Unsigned 8-bit little-endian format */
snd_pcm_hw_params_set_format(playback_handle, hw_params,
SND_PCM_FORMAT_S32_LE);
/* Two channels (stereo) */
snd_pcm_hw_params_set_channels(playback_handle, hw_params, 1);
/* 44100 bits/second sampling rate (CD quality) */
snd_pcm_hw_params_set_rate_near(playback_handle,
hw_params, &fs, &dir);
/* Write the parameters to the driver */
err = snd_pcm_hw_params(playback_handle, hw_params);
if (err < 0) {
fprintf(stderr,
"unable to set hw parameters: %s\n",
snd_strerror(err));
exit(1);
}
// Wavetable init
int wavetable[N]; // wavetable buffer
float angle_inc = TWOPI/(float)N; // sine wave angle increment
float index_inc = N*f/(float)fs; // wavetable index increment
// Populate wavetable with a sine wave
for( int n = 0; n < N; n++ ) {
wavetable[n] = sinuc( angle_inc * n ); // 0 - 255 range
}
// ALSA Sample Buffer
// period = 940 frames
// buffer = 15052 frames
float n = 0.0;
for (int i = 0; i < sizeof(buffer)/sizeof(int); i++) {
buffer[i] = wavetable[(int)n];
n = n+index_inc;
if ( (int)n == N ) {
n = 0;
}
else if ( (int) n > N) {
n = 0.005;
}
}
int idx_zerocross = 0;
int zerocross_diff = 0;
for (int counter_b = (sizeof(buffer) / sizeof(int)) - 1; counter_b > 1; counter_b--) {
if (buffer[counter_b] == 0 && (counter_b) % 22 == 0) {
idx_zerocross = counter_b;
zerocross_diff = (sizeof(buffer) / sizeof(int)) + 1 - idx_zerocross;
printf("IDX ZEROCROSS: %i\n", counter_b);
printf("DIFF: %i\n", zerocross_diff);
break;
}
}
/* Set period size to 32 frames. */
snd_pcm_uframes_t period_frames = (idx_zerocross) / 22;
snd_pcm_hw_params_set_period_size(playback_handle, hw_params, period_frames, dir);
if ((err = snd_pcm_prepare (playback_handle)) < 0) {
fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
snd_strerror (err));
exit (1);
}
int iterate_wt = 0;
while(1) {
int ret = snd_pcm_wait(playback_handle, 5);
if (ret == 0)
continue;
if ((iterate_wt*period_frames) + period_frames <= idx_zerocross) {
period_size = snd_pcm_writei(playback_handle, buffer+(((iterate_wt*period_frames)) * sizeof(int)), period_frames);
}
if ((iterate_wt*period_frames) + period_frames == idx_zerocross) {
iterate_wt = 0;
continue;
}
iterate_wt++;
if ((iterate_wt * period_frames) + period_frames >= idx_zerocross) {
iterate_wt = 0;
continue;
}
if (period_size < 0)
period_size = snd_pcm_recover(playback_handle, period_size, 0);
if (period_size < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(period_size));
break;
}
if (period_size > 0 && period_size < period_frames) {
printf("Short write (expected %li, wrote %li) iterate_wt = %i\n", period_frames, period_size, iterate_wt);
}
}
// pass the remaining samples, otherwise they're dropped in close
err = snd_pcm_drain(playback_handle);
if (err < 0)
printf("snd_pcm_drain failed: %s\n", snd_strerror(err));
//snd_pcm_hw_params_free(hw_params);
snd_pcm_close(playback_handle);
return 0;
}
/**
* Sine unsigned char.
* Scales sine output to a char
* Original range -1 to 1.
* New range 0 - 255.
*/
int sinuc( float angle ) {
return sinf( angle ) * INT_MAX;
}
Thank a lot Geraldo. I'll take a look. Been sick.
I managed to get it to work. Will post my version here after work.
Will be interesting to compare.
I'll be in touch.
Hi.
Apologies for asking for help here. I don't know where else to go.
I'm working on a wavetable project for college.
I've managed to create a wavetable containing a sine wave. The pitch can be altered via a frequency variable (f).
There is an audible popping sound when the sound loops. I have noticed that increasing the buffer size, postpones the pop.
I've been reading up on
buffer > period > frames
, but I don't know how to go about removing the pop.Would really appreciate some guidance.
Thanks C