Help to solve pop when sound loops

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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;
}


[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Pulse Audio]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux