#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, ¶ms.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;
}