0,0 → 1,393 |
/* |
* This small demo sends a simple sinusoidal wave to your speakers. |
*/ |
|
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
#include <sched.h> |
#include <errno.h> |
#include <getopt.h> |
#include <alsa/asoundlib.h> |
#include <sys/time.h> |
#include <math.h> |
|
static char *device = "plughw:0,0"; /* playback device */ |
static snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ |
static unsigned int rate = 44100; /* stream rate */ |
static unsigned int channels = 1; /* count of channels */ |
static unsigned int buffer_time = 500000; /* ring buffer length in us */ |
static unsigned int period_time = 100000; /* period time in us */ |
static double freq = 440; /* sinusoidal wave frequency in Hz */ |
static int verbose = 0; /* verbose flag */ |
static int resample = 1; /* enable alsa-lib resampling */ |
static int period_event = 0; /* produce poll event after each period */ |
|
int period=0; |
short sample[1000000]; |
|
static snd_pcm_sframes_t buffer_size; // size of buffer at sound card |
static snd_pcm_sframes_t period_size; //samples per frame |
static snd_output_t *output = NULL; |
|
/*static void generate_sine(const snd_pcm_channel_area_t *areas, |
snd_pcm_uframes_t offset, |
int count, double *_phase) |
{ |
static double max_phase = 2. * M_PI; |
double phase = *_phase; |
double step = max_phase*freq/(double)rate; |
double res; |
unsigned char *samples[channels], *tmp; |
int steps[channels]; |
unsigned int chn, byte; |
union { |
int i; |
unsigned char c[4]; |
} ires; |
unsigned int maxval = (1 << (snd_pcm_format_width(format) - 1)) - 1; |
int bps = snd_pcm_format_width(format) / 8; /* bytes per sample */ |
|
/* verify and prepare the contents of areas |
for (chn = 0; chn < channels; chn++) { |
if ((areas[chn].first % 8) != 0) { |
printf("areas[%i].first == %i, aborting...\n", chn, areas[chn].first); |
exit(EXIT_FAILURE); |
} |
samples[chn] = /*(signed short *)(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8)); |
if ((areas[chn].step % 16) != 0) { |
printf("areas[%i].step == %i, aborting...\n", chn, areas[chn].step); |
exit(EXIT_FAILURE); |
} |
steps[chn] = areas[chn].step / 8; |
samples[chn] += offset * steps[chn]; |
} |
/* fill the channel areas |
while (count-- > 0) { |
res = sin(phase) * maxval; |
ires.i = res; |
tmp = ires.c; |
for (chn = 0; chn < channels; chn++) { |
for (byte = 0; byte < (unsigned int)bps; byte++) |
*(samples[chn] + byte) = tmp[byte]; |
samples[chn] += steps[chn]; |
} |
phase += step; |
if (phase >= max_phase) |
phase -= max_phase; |
} |
*_phase = phase; |
}*/ |
|
static int set_hwparams(snd_pcm_t *handle, |
snd_pcm_hw_params_t *params, |
snd_pcm_access_t access) |
{ |
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) { |
printf("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) { |
printf("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, access); |
if (err < 0) { |
printf("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, format); |
if (err < 0) { |
printf("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) { |
printf("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) { |
printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); |
return err; |
} |
if (rrate != rate) { |
printf("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) { |
printf("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) { |
printf("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) { |
printf("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) { |
printf("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) { |
printf("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) { |
printf("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) { |
printf("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) { |
printf("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) { |
printf("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) { |
printf("Unable to set sw params for playback: %s\n", snd_strerror(err)); |
return err; |
} |
return 0; |
} |
|
struct async_private_data { |
signed short *samples; |
snd_pcm_channel_area_t *areas; |
unsigned int period; |
}; |
|
/*int linear_chirp(int *pole, int delka_pole){ // vygeneruje linearni chirp a vzorky ulozi do pole |
|
static const float f0 = 0.0001; |
static const float k = 0.00001; |
|
int t; |
|
// if((spozdeni+delka) < delka_pole) |
for(t=0;t < delka_pole;t++) pole[t] = round ( 10000*sin(2*M_PI*(t+faze)*(f0+(k/2)*(t+faze))) ); |
faze +=t; |
// else return 0; |
|
} |
|
int linear_windowed_chirp(int *pole, int delka_pole){ // vygeneruje linearni chirp a vzorky ulozi do pole |
|
static const float f0 = 0.0001; |
static const float k = 0.00001; |
|
int t; |
|
// (0.35875 - 0.48829 cos(t) + 0.14128 cos(2t) - 0.01168 cos(3t)) |
for(t=0;t < delka_pole;t++) pole[t] = (short) round ( (0.35875 - 0.48829*cos((t+faze)*0.0001) + 0.14128*cos(.0002*(t+faze)) - 0.01168*cos(.0003*(t+faze)))*30000*sin(2*M_PI*(t+faze)*(f0+(k/2)*(t+faze))) ); |
faze +=t; |
|
|
}*/ |
|
int sine(int *pole, int delka_pole) |
{ |
int t; |
for(t=0;t < delka_pole;t++) pole[t] = (short) round(10000*sin( (double)(t)/10.0)); |
} |
|
|
|
static void async_playback_callback(snd_async_handler_t *ahandler) |
{ |
snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler); |
struct async_private_data *data = snd_async_handler_get_callback_private(ahandler); |
signed short *samples = data->samples; |
snd_pcm_channel_area_t *areas = data->areas; |
snd_pcm_sframes_t avail; |
int err; |
|
avail = snd_pcm_avail_update(handle); |
while (avail >= period_size) { |
// generate_sine(areas, 0, period_size, &data->phase); |
// linear_windowed_chirp(signal,period_size); |
|
err = snd_pcm_writei(handle, sample, period_size); |
if (err < 0) { |
printf("Write error: %s\n", snd_strerror(err)); |
exit(EXIT_FAILURE); |
} |
if (err != period_size) { |
printf("Write error: written %i expected %li\n", err, period_size); |
exit(EXIT_FAILURE); |
} |
avail = snd_pcm_avail_update(handle); |
} |
} |
|
int main(int argc, char *argv[]) |
{ |
snd_pcm_t *playback_handle, *capture_handle; |
int err; |
snd_pcm_hw_params_t *hwparams; |
snd_pcm_sw_params_t *swparams; |
signed short *frame; // pointer to array of samples |
unsigned int chn; |
snd_pcm_channel_area_t *areas; |
|
struct async_private_data data; |
snd_async_handler_t *ahandler; |
int count; |
unsigned int i; |
|
|
snd_pcm_hw_params_alloca(&hwparams); |
snd_pcm_sw_params_alloca(&swparams); |
|
//open adn set playback device |
if ((err = snd_pcm_open(&playback_handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { |
printf("Playback open error: %s\n", snd_strerror(err)); |
return 0; |
} |
|
if ((err = set_hwparams(playback_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { |
printf("Setting of hwparams failed: %s\n", snd_strerror(err)); |
exit(EXIT_FAILURE); |
} |
if ((err = set_swparams(playback_handle, swparams)) < 0) { |
printf("Setting of swparams failed: %s\n", snd_strerror(err)); |
exit(EXIT_FAILURE); |
} |
|
//open and set capture device |
if ((err = snd_pcm_open(&capture_handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) { |
printf("Playback open error: %s\n", snd_strerror(err)); |
return 0; |
} |
|
if ((err = set_hwparams(capture_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { |
printf("Setting of hwparams failed: %s\n", snd_strerror(err)); |
exit(EXIT_FAILURE); |
} |
if ((err = set_swparams(capture_handle, swparams)) < 0) { |
printf("Setting of swparams failed: %s\n", snd_strerror(err)); |
exit(EXIT_FAILURE); |
} |
|
// allocate memory for frame (package of samples) |
frame = malloc((period_size * channels * snd_pcm_format_physical_width(format)) / 8); |
if (frame == NULL) { |
printf("No enough memory\n"); |
exit(EXIT_FAILURE); |
} |
|
//allocate memory for frame structure definition |
areas = calloc(channels, sizeof(snd_pcm_channel_area_t)); |
if (areas == NULL) { |
printf("No enough memory\n"); |
exit(EXIT_FAILURE); |
} |
//fill areas by definition of frame structure |
for (chn = 0; chn < channels; chn++) { |
areas[chn].addr = frame; // frame start adress |
areas[chn].first = chn * snd_pcm_format_physical_width(format); // ofset to first sample (in bits) |
areas[chn].step = channels * snd_pcm_format_physical_width(format); // step between samples |
} |
|
sine(sample,100000); |
|
|
data.samples = frame; |
data.areas = areas; |
data.period = 1; |
|
|
// register playback callback |
err = snd_async_add_pcm_handler(&ahandler, playback_handle, async_playback_callback, &data); |
if (err < 0) { |
printf("Unable to register async handler\n"); |
exit(EXIT_FAILURE); |
} |
for (data.period = 0; data.period < 2; data.period++) { |
|
err = snd_pcm_writei(playback_handle, sample, period_size); |
if (err < 0) { |
printf("Initial write error: %s\n", snd_strerror(err)); |
exit(EXIT_FAILURE); |
} |
if (err != period_size) { |
printf("Initial write error: written %i expected %li\n", err, period_size); |
exit(EXIT_FAILURE); |
} |
} |
|
//start playback |
if (snd_pcm_state(playback_handle) == SND_PCM_STATE_PREPARED) { |
err = snd_pcm_start(playback_handle); |
if (err < 0) { |
printf("Start error: %s\n", snd_strerror(err)); |
exit(EXIT_FAILURE); |
} |
} |
|
/* because all other work is done in the signal handler, |
suspend the process */ |
for(i=0; i<=10;i++) { |
sleep(1); |
} |
|
|
|
free(areas); |
free(frame); |
snd_pcm_close(playback_handle); |
snd_pcm_close(capture_handle); |
return 0; |
} |
|