Hi. I hope this is the correct place to ask for help relating to ALSA's C lib? I'm working on a wavetable project for college. Please see attached. 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 -- Sent with Tuta; enjoy secure & ad-free emails: https://tuta.com
#include <stdio.h> #include <alsa/asoundlib.h> #include <math.h> unsigned char sinuc( float ); #define TWOPI 2*M_PI #define ALSA_INFO 1 static char *device = "default"; /* playback device */ unsigned char buffer[24*1024]; /* some random data */ /** * = ALSA Frames * Frame is container for sending simultaneous samples. * - Mono frame contains 1 sample. * - Stereo frame contains 2 samples. * * = ALSA Ring Buffer * Store outgoing (playback) and incoming (capture, record) samples. * == Two pointers * - current processed sample by hardware * - last processed sample by application. */ int main(void) { // ALSA playback related int err; snd_pcm_t *playback_handle; // pcm snd_pcm_hw_params_t *hw_params; unsigned int sbits, subformat, tick_time, access, channels, buffer_time, buffer_size, periods, period_time, rate, rate_num, rate_den, val, val2; // @todo more descriptive vars int dir; snd_pcm_uframes_t period_size; snd_pcm_format_t format; // Wavetable related unsigned int N = 1024; 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_U8); /* 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); /* Set period size to 32 frames. */ period_size = 32; snd_pcm_hw_params_set_period_size_near(playback_handle, hw_params, &period_size, &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); } /* Display information about the PCM interface */ if( ALSA_INFO == 1) { printf("PCM playback_handle name = '%s'\n", snd_pcm_name(playback_handle)); printf("PCM state = %s\n", snd_pcm_state_name(snd_pcm_state(playback_handle))); snd_pcm_hw_params_get_access(hw_params, &access); printf("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)access)); snd_pcm_hw_params_get_format(hw_params, &format); printf("format = '%s' (%s)\n", snd_pcm_format_name((snd_pcm_format_t)format), snd_pcm_format_description( (snd_pcm_format_t)format)); snd_pcm_hw_params_get_subformat(hw_params, (snd_pcm_subformat_t *)&subformat); printf("subformat = '%s' (%s)\n", snd_pcm_subformat_name((snd_pcm_subformat_t)subformat), snd_pcm_subformat_description( (snd_pcm_subformat_t)subformat) ); snd_pcm_hw_params_get_channels(hw_params, &channels); printf("channels = %d\n", channels); snd_pcm_hw_params_get_rate(hw_params, &rate, NULL); printf("rate = %d bps\n", rate); snd_pcm_hw_params_get_period_time(hw_params, &period_time, NULL); printf("period time = %d us\n", period_time); snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir); printf("period size = %d frames\n", (int)period_size); snd_pcm_hw_params_get_buffer_time(hw_params, &buffer_time, &dir); printf("buffer time = %d us\n", buffer_time); snd_pcm_hw_params_get_buffer_size(hw_params, (snd_pcm_uframes_t *) &buffer_size); printf("buffer size = %d frames\n", buffer_size); snd_pcm_hw_params_get_periods(hw_params, &periods, &dir); printf("periods per buffer = %d frames\n", periods); // rate numerator and denominator snd_pcm_hw_params_get_rate_numden(hw_params, &rate_num, &rate_den); printf("exact rate = %d/%d bps\n", rate_num, rate_den); sbits = snd_pcm_hw_params_get_sbits(hw_params); printf("significant bits = %d\n", sbits); } // Wavetable init unsigned char wavetable[N]; // wavetable buffer float angle_inc = TWOPI/(float)N; // sine wave angle increment float index_inc = N*f/(float)fs; // wavetable index increment //printf("angle inc: %.4f\n", angle_inc); //printf("index inc: %.4f\n", index_inc); // Populate wavetable with a sine wave for( int n = 0; n < N; n++ ) { //wavetable[n] = sin( angle_inc * n ); // 0 - 1 range wavetable[n] = sinuc( angle_inc * n ); // 0 - 255 range //printf("%d\n", wavetable[n]); } // ALSA Sample Buffer // period = 940 frames // buffer = 15052 frames float n = 0; for (int i = 0; i < sizeof(buffer)/sizeof(char); i++) { buffer[i] = wavetable[(int)n]; //printf("%d\n",buffer[i]); n = n+index_inc; if( (int)n >= N ) { n = 0; } } if ((err = snd_pcm_prepare (playback_handle)) < 0) { fprintf (stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror (err)); exit (1); } //for (int i = 0; i < 16; i++) { while(1) { period_size = snd_pcm_writei(playback_handle, buffer, sizeof(buffer)); 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 < (long)sizeof(buffer)) printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), period_size); } // 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. */ unsigned char sinuc( float angle ) { return (sinf( angle ) * 255 + 255) / 2; }