IC-7410 Audio Interface and ALSA (3)

The original pcm.c has 925 lines to send a sine wave with various transfer methods. Here is a simplified version only to support asynchronous notification, and instead of playing a waveform, the audio signal is captured from the device.

capture

The source program is compiled, the audio signal from IC-7410 (hw:2,0) is captured and stored in a file, and the file is played back on a speaker (hw:1,3).

/* file name = pcm_capture3.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include "asoundlib.h"
#include <sys/time.h>
#include <math.h>

static char *device             = "hw:2,0";	/* playback device is IC-7410*/
static unsigned int rate        = 44100;	/* stream rate */
static unsigned int channels    = 2;		/* count of channels */
static int byte_per_sample      = 2;		/* 16 bit format */
static unsigned int buffer_time = 500000;	/* ring buffer length in us */
static unsigned int period_time = 100000;	/* period time in us */
static int resample             = 0;		/* enable alsa-lib resampling */
static int period_event         = 0;		/* produce poll event after each period */

static snd_pcm_sframes_t buffer_size;
static snd_pcm_sframes_t period_size;

static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params)
{
	unsigned int      rrate;
	snd_pcm_uframes_t size;
	int               err, dir;

	/* choose all parameters */
	err = snd_pcm_hw_params_any(handle, params);
	if (err < 0) {
		fprintf(stderr,"Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
		return err;
	}

	/* set hardware resampling */
	err = snd_pcm_hw_params_set_rate_resample(handle, params, resample);
	if (err < 0) {
		fprintf(stderr,"Resampling setup failed for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the interleaved read/write format */
	err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
	if (err < 0) {
		fprintf(stderr,"Access type not available for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the sample format */
	err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16);
	if (err < 0) {
		fprintf(stderr,"Sample format not available for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the count of channels */
	err = snd_pcm_hw_params_set_channels(handle, params, channels);
	if (err < 0) {
		fprintf(stderr,"Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
		return err;
	}

	/* set the stream rate */
	rrate = rate;
	err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
	if (err < 0) {
		fprintf(stderr,"Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
		return err;
	}
	if (rrate != rate) {
		fprintf(stderr,"Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
		return -EINVAL;
	}

	/* set the buffer time */
	err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
	if (err < 0) {
		fprintf(stderr,"Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
		return err;
	}
	err = snd_pcm_hw_params_get_buffer_size(params, &size);
	if (err < 0) {
		fprintf(stderr,"Unable to get buffer size for playback: %s\n", snd_strerror(err));
		return err;
	}
	buffer_size = size;

	/* set the period time */
	err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
	if (err < 0) {
		fprintf(stderr,"Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
		return err;
	}
	err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
	if (err < 0) {
		fprintf(stderr,"Unable to get period size for playback: %s\n", snd_strerror(err));
		return err;
	}
	period_size = size;

	/* write the parameters to device */
	err = snd_pcm_hw_params(handle, params);
	if (err < 0) {
		fprintf(stderr,"Unable to set hw params for playback: %s\n", snd_strerror(err));
		return err;
	}
	return 0;
}

static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
{
	int err;

	/* get the current swparams */
	err = snd_pcm_sw_params_current(handle, swparams);
	if (err < 0) {
		fprintf(stderr,"Unable to determine current swparams for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* start the transfer when the buffer is almost full: */
	/* (buffer_size / avail_min) * avail_min */
	err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size);
	if (err < 0) {
		fprintf(stderr,"Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* allow the transfer when at least period_size samples can be processed */
	/* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
	err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size);
	if (err < 0) {
		fprintf(stderr,"Unable to set avail min for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* enable period events when requested */
	if (period_event) {
		err = snd_pcm_sw_params_set_period_event(handle, swparams, 1);
		if (err < 0) {
			fprintf(stderr,"Unable to set period event: %s\n", snd_strerror(err));
			return err;
		}
	}

	/* write the parameters to the playback device */
	err = snd_pcm_sw_params(handle, swparams);
	if (err < 0) {
		fprintf(stderr,"Unable to set sw params for playback: %s\n", snd_strerror(err));
		return err;
	}
	return 0;
}

static void async_callback(snd_async_handler_t *ahandler)
{
	snd_pcm_t                 *handle  = snd_async_handler_get_pcm(ahandler);
	signed short              *samples = snd_async_handler_get_callback_private(ahandler);
	snd_pcm_sframes_t          avail;
	int                        err, write_count, wc;
	
	avail = snd_pcm_avail_update(handle);
	while (avail >= period_size) {
		err = snd_pcm_readi (handle, samples, period_size);
		if (err < 0) {
			fprintf(stderr,"Write error: %s\n", snd_strerror(err));
			exit(EXIT_FAILURE);
		}
		if (err != period_size) {
			fprintf(stderr,"Write error: written %i expected %li\n", err, period_size);
			exit(EXIT_FAILURE);
		}

		write_count = period_size * channels * byte_per_sample;
		wc = write(1, samples, write_count);
		if(wc != write_count) {
			fprintf(stderr,"file write error. \n");
		}

		avail = snd_pcm_avail_update(handle);
	}
}

static int async_loop(snd_pcm_t *handle, signed short *samples)
{
	snd_async_handler_t       *ahandler;
	int                        err;

	err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, samples);
	if (err < 0) {
		fprintf(stderr,"Unable to register async handler\n");
		exit(EXIT_FAILURE);
	}

	if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED) {
		err = snd_pcm_start(handle);
		if (err < 0) {
			fprintf(stderr,"Start error: %s\n", snd_strerror(err));
			exit(EXIT_FAILURE);
		}
	}

	while (1) {
		sleep(1);
	}
}

int main(int argc, char *argv[])
{
	snd_pcm_t              *handle;
	snd_pcm_hw_params_t    *hwparams;
	snd_pcm_sw_params_t    *swparams;
	signed short           *samples;
	int                     err;

	snd_pcm_hw_params_alloca(&hwparams);
	snd_pcm_sw_params_alloca(&swparams);

	fprintf(stderr,"capture device = %s, rate = %i Hz, %i channels\n", device, rate, channels);

	if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
		fprintf(stderr,"Capture open error: %s\n", snd_strerror(err));
		return 0;
	}
	
	if ((err = set_hwparams(handle, hwparams)) < 0) {
		fprintf(stderr,"Setting of hwparams failed: %s\n", snd_strerror(err));
		exit(EXIT_FAILURE);
	}
	if ((err = set_swparams(handle, swparams)) < 0) {
		fprintf(stderr,"Setting of swparams failed: %s\n", snd_strerror(err));
		exit(EXIT_FAILURE);
	}

	samples = malloc(period_size * channels * byte_per_sample);
	if (samples == NULL) {
		fprintf(stderr,"No enough memory\n");
		exit(EXIT_FAILURE);
	}
	
	err = async_loop(handle, samples);
	if (err < 0) fprintf(stderr,"Transfer failed: %s\n", snd_strerror(err));

	free(samples);
	snd_pcm_close(handle);
	return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *