aboutsummaryrefslogblamecommitdiffstats
path: root/alsa-record-sound/record.c
blob: ed9c2e1bebd945002086017ad3fb5e8166ea184f (plain) (tree)









































































































































































































































































                                                                                                                                                                  
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include <alsa/asoundlib.h>

#include "debug.h"

#define AERR(S) if (err<0) {printf("error line %d:"S" %s\n", __LINE__, snd_strerror(err));};

typedef struct audio_params
{
	snd_pcm_format_t format;
	unsigned int channels;
	unsigned int rate;
} audio_params;

int main(int argc, char **argv)
{
	/* variables */
	int i;
	int err;
	size_t n;
	snd_pcm_uframes_t chunk_size;
	size_t chunk_bytes;
	snd_output_t *log;
	const snd_pcm_channel_area_t *areas;
	snd_pcm_uframes_t mmap_offset, mmap_size = chunk_size;
	long long max_file_size = 0;
	FILE *fout=NULL;

	/* audio params */
	char audio_name[] = "hw:0";
	int  open_mode = 0;
	audio_params params;
	unsigned int rate;
	unsigned int buffer_time;
	unsigned int period_time;
	snd_pcm_uframes_t period_frames = 0;
	snd_pcm_uframes_t buffer_frames = 0;
	int monotonic = 0;
	int can_pause = 0;
	int avail_min = -1;
	int start_delay = 0;
	int stop_delay = 0;
	snd_pcm_uframes_t start_threshold, stop_threshold;
	size_t significant_bits_per_sample, bits_per_sample, bits_per_frame;
	u_char *audiobuf = NULL;
	uint64_t fdcount=0;

	/* audio variables */
	snd_pcm_t *handle;
	snd_pcm_hw_params_t *hwparams;
	snd_pcm_sw_params_t *swparams;
	snd_pcm_uframes_t buffer_size;
	snd_pcm_access_mask_t *mask;

	/* set recording params */
	params.channels = 2;
	params.rate = 44100;
	params.format = SND_PCM_FORMAT_S16_LE;

	/* set device */
	err = snd_pcm_open(&handle, audio_name, SND_PCM_STREAM_CAPTURE, open_mode);
	AERR("Cant open");

	/* alloc hw params */
	err = snd_pcm_hw_params_malloc(&hwparams);
	AERR("hw params alloc");

	err = snd_pcm_hw_params_any(handle, hwparams);
	AERR("hw params initialisation");

	/* alooc sw params */
	err = snd_pcm_sw_params_malloc(&swparams);
	AERR("sw params alloca");

	/* mmaped buffer */
	mask = alloca(snd_pcm_access_mask_sizeof());
	snd_pcm_access_mask_none(mask);
	snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
	snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
	snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
	err = snd_pcm_hw_params_set_access_mask(handle, hwparams, mask);
	AERR("Cannot set access mask");
	
	/* set channels */
	err = snd_pcm_hw_params_set_channels(handle, hwparams, params.channels );
	AERR("Cannot set channels");PNL();
	
	/* set and check rate */
	rate = params.rate;
	err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &params.rate, 0);
	if ((float)rate*1.05 < params.rate || (float)rate*0.95>params.rate)
	{
		printf("Inaccurare rate setting\n");
	}

	/*set buffer time and perioid max*/
	rate = params.rate;
	if (buffer_time == 0 && buffer_frames == 0)
	{
		err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0);
		AERR("Cannt get buffer time max");
		if (buffer_time > 500000)
			buffer_time = 500000;
	}
	if (period_time == 0 && period_frames == 0)
	{
		if (buffer_time > 0)
			period_time = buffer_time / 4;
		else
			period_frames = buffer_frames / 4;
	}
	if (period_time > 0)
	{
		err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0);
	} else {
		err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_frames, 0);
	}
	AERR("Cant set period");PNL();
	if (buffer_time > 0)
	{
		err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0);
	} else {
		err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &buffer_frames);
	}
	AERR("Cant set buffer time/size");PNL();
	
	monotonic = snd_pcm_hw_params_is_monotonic(hwparams);
	can_pause = snd_pcm_hw_params_can_pause(hwparams);

	err = snd_pcm_hw_params(handle, hwparams);
	if (err < 0)
	{
		printf("Unable to install hw params:\n");
		snd_pcm_hw_params_dump(hwparams, log);
		exit(EXIT_FAILURE);
	}

	snd_pcm_hw_params_get_period_size(hwparams, &chunk_size, 0);
	snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
	if (chunk_size == buffer_size)
	{
		printf("Can't use period equal to buffer size (%lu == %lu)\n",
		      chunk_size, buffer_size);
		exit(EXIT_FAILURE);
	}

	snd_pcm_sw_params_current(handle, swparams);
	if (avail_min < 0)
	{
		n = chunk_size;
	} else {
		n = (double)rate * avail_min * 1000000;
	}
	err = snd_pcm_sw_params_set_avail_min(handle, swparams, n);
	AERR("Couldnt set avaliable min");

	/* round up to closest transfer boundry */
	n = buffer_size;
	if (start_delay <= 0) {
		start_threshold = n + (double) rate * start_delay / 1000000;
	} else
		start_threshold = (double) rate * start_delay / 1000000;
	if (start_threshold < 1)
		start_threshold = 1;
	if (start_threshold > n)
		start_threshold = n;
	err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold);
	assert(err >= 0);
	if (stop_delay <= 0) 
		stop_threshold = buffer_size + (double) rate * stop_delay / 1000000;
	else
		stop_threshold = (double) rate * stop_delay / 1000000;
	err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold);
	assert(err >= 0);

	err = snd_pcm_sw_params(handle, swparams);
	AERR("Unable to install sw params\n");PNL();

	/*do we neet setup_chmap?*/

	//snd_pcm_dump(handle, log);PNL();

	bits_per_sample = snd_pcm_format_physical_width(params.format);
	printf("!bits_per_sample=%d\n",bits_per_sample);
	significant_bits_per_sample = snd_pcm_format_width(params.format);
	bits_per_frame = bits_per_sample * params.channels;
	chunk_bytes = chunk_size * bits_per_frame / 8;
	printf("INFO: audio buffer size %d bytes\n",chunk_bytes);
	audiobuf = realloc(audiobuf, chunk_bytes);
	if (audiobuf == NULL) {
		printf("not enough memory\n");
		exit(EXIT_FAILURE);
	}
	// fprintf(stderr, "real chunk_size = %i, frags = %i, total = %i\n", chunk_size, setup.buf.block.frags, setup.buf.block.frags * chunk_size);

	/* show mmap buffer arragment */

	
	err = snd_pcm_mmap_begin(handle, &areas, &mmap_offset, &mmap_size);
	if (err < 0) {
		printf("snd_pcm_mmap_begin problem: %s\n", snd_strerror(err));
		exit(EXIT_FAILURE);
	}
	for (i = 0; i < params.channels; i++)
		fprintf(stderr, "mmap_area[%i] = %p,%u,%u (%u)\n", i, areas[i].addr, areas[i].first, areas[i].step, snd_pcm_format_physical_width(params.format));
	/* not required, but for sure */
	snd_pcm_mmap_commit(handle, mmap_offset, 0);

	buffer_frames = buffer_size;	/* for position test */

	/* get size of record for current config */

	max_file_size = snd_pcm_format_size(params.format, params.rate*params.channels);
	printf("INFO: Date %d byters per record\n", max_file_size);

	/* open file where to record */
	fout = fopen("foo_r2.wav","w");
	if (fout==NULL)
	{
		printf("Couldnt open record file");
		exit(0);
	}

	/* recording loop */
	{
		size_t rest=0;
		size_t r=0;
		size_t idx=0;
		u_char *data = audiobuf;
		size_t c = chunk_bytes;
		size_t f = c * 8 / bits_per_frame;
		rest = f*100;
		idx = 0;
		//fdcount = 0;
		while (rest > 1)
		{
			r = snd_pcm_mmap_readi(handle,data, f);
			if ((r == -EAGAIN) || ((r >= 0) && (r < rest)))
			{
				//snd_pcm_wait(handle, 100);
			} else
			{
				printf("Buffer reading problem %d\n",r);
				//break;
			}
			rest -= r;
			err = fwrite(audiobuf,1,r*bits_per_frame/8,fout);
			if (err != r*bits_per_frame/8)
				printf("Not all data saved %d\n",err);
		}
	}
	fclose(fout);

	/* free audio resources */
	snd_pcm_close(handle);
	free(audiobuf);
	//snd_output_close(log);
	snd_config_update_free_global();
	

	return 0;
}