diff options
author | Lasse Flygenring-Harrsen <Lasse@Flygenring-Harrsen.com> | 2020-07-09 00:42:03 +0200 |
---|---|---|
committer | Lasse Flygenring-Harrsen <Lasse@Flygenring-Harrsen.com> | 2020-07-09 00:42:03 +0200 |
commit | 6d2fa862858dafdea29fc2e4a3dc39c3dbb17f2e (patch) | |
tree | b03c3dc5c5c08129ed7f0642dd3e64d75aa866e0 /input/alsa.c |
Import Upstream version 0.7.1
Diffstat (limited to 'input/alsa.c')
-rw-r--r-- | input/alsa.c | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/input/alsa.c b/input/alsa.c new file mode 100644 index 0000000..8fcbc28 --- /dev/null +++ b/input/alsa.c @@ -0,0 +1,165 @@ +// input: ALSA +#include "input/alsa.h" +#include "debug.h" +#include "input/common.h" + +#include <alloca.h> +#include <alsa/asoundlib.h> +#include <math.h> + +// assuming stereo +#define CHANNELS_COUNT 2 +#define SAMPLE_RATE 44100 + +static void initialize_audio_parameters(snd_pcm_t **handle, struct audio_data *audio, + snd_pcm_uframes_t *frames) { + // alsa: open device to capture audio + int err = snd_pcm_open(handle, audio->source, SND_PCM_STREAM_CAPTURE, 0); + if (err < 0) { + fprintf(stderr, "error opening stream: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } else + debug("open stream successful\n"); + + snd_pcm_hw_params_t *params; + snd_pcm_hw_params_alloca(¶ms); // assembling params + snd_pcm_hw_params_any(*handle, params); // setting defaults or something + // interleaved mode right left right left + snd_pcm_hw_params_set_access(*handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); + // trying to set 16bit + snd_pcm_hw_params_set_format(*handle, params, SND_PCM_FORMAT_S16_LE); + snd_pcm_hw_params_set_channels(*handle, params, CHANNELS_COUNT); + unsigned int sample_rate = SAMPLE_RATE; + // trying our rate + snd_pcm_hw_params_set_rate_near(*handle, params, &sample_rate, NULL); + // number of frames pr read + snd_pcm_hw_params_set_period_size_near(*handle, params, frames, NULL); + err = snd_pcm_hw_params(*handle, params); // attempting to set params + if (err < 0) { + fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + + if ((err = snd_pcm_prepare(*handle)) < 0) { + fprintf(stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + + // getting actual format + snd_pcm_hw_params_get_format(params, (snd_pcm_format_t *)&sample_rate); + // converting result to number of bits + if (sample_rate <= 5) + audio->format = 16; + else if (sample_rate <= 9) + audio->format = 24; + else + audio->format = 32; + snd_pcm_hw_params_get_rate(params, &audio->rate, NULL); + snd_pcm_hw_params_get_period_size(params, frames, NULL); + // snd_pcm_hw_params_get_period_time(params, &sample_rate, &dir); +} + +static int get_certain_frame(signed char *buffer, int buffer_index, int adjustment) { + // using the 10 upper bits this would give me a vert res of 1024, enough... + int temp = buffer[buffer_index + adjustment - 1] << 2; + int lo = buffer[buffer_index + adjustment - 2] >> 6; + if (lo < 0) + lo = abs(lo) + 1; + if (temp >= 0) + temp += lo; + else + temp -= lo; + return temp; +} +/* +static void fill_audio_outs(struct audio_data* audio, signed char* buffer, +const int size) { + int radj = audio->format / 4; // adjustments for interleaved + int ladj = audio->format / 8; + static int audio_out_buffer_index = 0; + // sorting out one channel and only biggest octet + for (int buffer_index = 0; buffer_index < size; buffer_index += ladj * 2) { + // first channel + int tempr = get_certain_frame(buffer, buffer_index, radj); + // second channel + int templ = get_certain_frame(buffer, buffer_index, ladj); + + // mono: adding channels and storing it in the buffer + if (audio->channels == 1) + audio->audio_out_l[audio_out_buffer_index] = (templ + tempr) / 2; + else { // stereo storing channels in buffer + audio->audio_out_l[audio_out_buffer_index] = templ; + audio->audio_out_r[audio_out_buffer_index] = tempr; + } + + ++audio_out_buffer_index; + audio_out_buffer_index %= audio->FFTbufferSize; + } +} +*/ +#define FRAMES_NUMBER 256 + +void *input_alsa(void *data) { + int err; + struct audio_data *audio = (struct audio_data *)data; + snd_pcm_t *handle; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t period_size; + snd_pcm_uframes_t frames = FRAMES_NUMBER; + + initialize_audio_parameters(&handle, audio, &frames); + snd_pcm_get_params(handle, &buffer_size, &period_size); + + int radj = audio->format / 4; // adjustments for interleaved + int ladj = audio->format / 8; + int16_t buf[period_size]; + int32_t buffer32[period_size]; + frames = period_size / ((audio->format / 8) * CHANNELS_COUNT); + // printf("period size: %lu\n", period_size); + // exit(0); + + // frames * bits/8 * channels + // const int size = frames * (audio->format / 8) * CHANNELS_COUNT; + signed char *buffer = malloc(period_size); + + while (!audio->terminate) { + switch (audio->format) { + case 16: + err = snd_pcm_readi(handle, buf, frames); + break; + case 32: + err = snd_pcm_readi(handle, buffer32, frames); + for (uint16_t i = 0; i < frames * 2; i++) { + buf[i] = buffer32[i] / pow(2, 16); + } + break; + default: + err = snd_pcm_readi(handle, buffer, frames); + // sorting out one channel and only biggest octet + for (uint16_t i = 0; i < period_size * 2; i += ladj * 2) { + // first channel + buf[i] = get_certain_frame(buffer, i, ladj); + // second channel + buf[i + 1] = get_certain_frame(buffer, i, radj); + } + // fill_audio_outs(audio, buffer, period_size); + break; + } + + if (err == -EPIPE) { + /* EPIPE means overrun */ + debug("overrun occurred\n"); + snd_pcm_prepare(handle); + } else if (err < 0) { + debug("error from read: %s\n", snd_strerror(err)); + } else if (err != (int)frames) { + debug("short read, read %d %d frames\n", err, (int)frames); + } + + write_to_fftw_input_buffers(buf, frames, data); + } + + free(buffer); + snd_pcm_close(handle); + return NULL; +} |