summaryrefslogtreecommitdiff
path: root/input/alsa.c
blob: 8fcbc2875ec6cec4055744b7101da0e5af09c533 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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(&params);      // 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;
}