Final modifications for Radio-Audio API communication
[src/app-framework-binder.git] / plugins / audio / audio-alsa.c
1 /*
2  * Copyright (C) 2015 "IoT.bzh"
3  * Author "Manuel Bachmann"
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "audio-api.h"
20 #include "audio-alsa.h"
21
22 PUBLIC unsigned char _alsa_init (const char *name, audioCtxHandleT *ctx) {
23
24     snd_pcm_t *dev;
25     snd_pcm_hw_params_t *params;
26     snd_mixer_t *mixer;
27     snd_mixer_selem_id_t *mixer_sid;
28     snd_mixer_elem_t *mixer_elm;
29     unsigned int rate = 22050;
30     long vol, vol_min, vol_max;
31     int num;
32
33     if (snd_pcm_open (&dev, name, SND_PCM_STREAM_PLAYBACK, 0) < 0)
34         return 0;
35
36     snd_pcm_hw_params_malloc (&params);
37     snd_pcm_hw_params_any (dev, params);
38     snd_pcm_hw_params_set_access (dev, params, SND_PCM_ACCESS_RW_INTERLEAVED);
39     snd_pcm_hw_params_set_format (dev, params, SND_PCM_FORMAT_S16_LE);
40     snd_pcm_hw_params_set_rate_near (dev, params, &rate, 0);
41     snd_pcm_hw_params_set_channels (dev, params, ctx->channels);
42     if (snd_pcm_hw_params (dev, params) < 0) {
43         snd_pcm_hw_params_free (params);
44         return 0;
45     }
46     snd_pcm_prepare (dev);
47
48     snd_mixer_open (&mixer, 0);
49     if (snd_mixer_attach (mixer, name) < 0) {
50         snd_pcm_hw_params_free (params);
51         return 0;
52     }
53     snd_mixer_selem_register (mixer, NULL, NULL);
54     snd_mixer_load (mixer);
55
56     snd_mixer_selem_id_alloca (&mixer_sid);
57     snd_mixer_selem_id_set_index (mixer_sid, 0);
58     snd_mixer_selem_id_set_name (mixer_sid, "Master");
59
60     mixer_elm = snd_mixer_find_selem (mixer, mixer_sid);
61     if (mixer_elm) {
62         snd_mixer_selem_get_playback_volume_range (mixer_elm, &vol_min, &vol_max);
63         snd_mixer_selem_get_playback_volume (mixer_elm, SND_MIXER_SCHN_FRONT_LEFT, &vol);
64     }
65
66     /* allocate the global array if it hasn't been done */
67     if (!adev_ctx) {
68         adev_ctx = (adev_ctx_T**) malloc (sizeof(adev_ctx_T));
69         adev_ctx[0] = (adev_ctx_T*) malloc (sizeof(adev_ctx_T));
70         adev_ctx[0]->name = NULL;
71         adev_ctx[0]->dev = NULL;
72     }
73
74     /* is a card with similar name already opened ? */
75     for (num = 0; num < (sizeof(adev_ctx)/sizeof(adev_ctx_T)); num++) {
76         if (adev_ctx[num]->name &&
77            !strcmp (adev_ctx[num]->name, name))
78             return 0;
79     }
80     num++;
81
82     /* it's not... let us add it to the global array */
83     adev_ctx = (adev_ctx_T**) realloc (adev_ctx, (num+1)*sizeof(adev_ctx_T));
84     adev_ctx[num] = (adev_ctx_T*) malloc (sizeof(adev_ctx_T));
85     adev_ctx[num]->name = strdup (name);
86     adev_ctx[num]->dev = dev;
87     adev_ctx[num]->params = params;
88     adev_ctx[num]->mixer_elm = mixer_elm;
89     adev_ctx[num]->vol_max = vol_max;
90     adev_ctx[num]->vol = vol;
91
92     /* make the client context aware of current card state */
93     ctx->volume = _alsa_get_volume (num);
94     ctx->mute = _alsa_get_mute (num);
95     ctx->idx = num;
96
97     return 1;
98 }
99
100 PUBLIC void _alsa_free (const char *name) {
101
102     int num;
103
104     for (num = 0; num < (sizeof(adev_ctx)/sizeof(adev_ctx_T)); num++) {
105         if (adev_ctx[num]->name &&
106            !strcmp (adev_ctx[num]->name, name)) {
107             snd_pcm_close (adev_ctx[num]->dev);
108             snd_pcm_hw_params_free (adev_ctx[num]->params);
109             free (adev_ctx[num]->name);
110             adev_ctx[num]->name = NULL;
111             adev_ctx[num]->dev = NULL;
112             free(adev_ctx[num]);
113             return;
114         }
115     }
116 }
117
118 PUBLIC void _alsa_play (unsigned int num, void *buf, int len) {
119
120     if (!adev_ctx || !adev_ctx[num]) {
121         return;
122     }
123     int16_t *cbuf = (int16_t *)buf;
124     int frames = len / 2;
125     int res;
126
127     if ((res = snd_pcm_writei (adev_ctx[num]->dev, cbuf, frames)) != frames) {
128         snd_pcm_recover (adev_ctx[num]->dev, res, 0);
129         snd_pcm_prepare (adev_ctx[num]->dev);
130     }
131     /* snd_pcm_drain (adev_ctx[num]->dev); */
132 }
133
134 PUBLIC unsigned int _alsa_get_volume (unsigned int num) {
135
136     if (!adev_ctx || !adev_ctx[num] || !adev_ctx[num]->mixer_elm)
137         return;
138
139     snd_mixer_selem_get_playback_volume (adev_ctx[num]->mixer_elm, SND_MIXER_SCHN_FRONT_LEFT, &adev_ctx[num]->vol);
140
141     return (unsigned int)(adev_ctx[num]->vol*100)/adev_ctx[num]->vol_max;
142 }
143
144 PUBLIC unsigned int _alsa_set_volume (unsigned int num, unsigned int vol) {
145
146     if (!adev_ctx || !adev_ctx[num] || !adev_ctx[num]->mixer_elm || vol > 100)
147         return;
148
149    snd_mixer_selem_set_playback_volume_all (adev_ctx[num]->mixer_elm, (vol*adev_ctx[num]->vol_max)/100);
150 }
151
152 PUBLIC unsigned char _alsa_get_mute (unsigned int num) {
153
154     int mute = 0;
155
156     if (!adev_ctx || !adev_ctx[num] || !adev_ctx[num]->mixer_elm)
157         return;
158
159     if (snd_mixer_selem_has_playback_switch (adev_ctx[num]->mixer_elm)) {
160         snd_mixer_selem_get_playback_switch (adev_ctx[num]->mixer_elm, SND_MIXER_SCHN_FRONT_LEFT, &mute); 
161         snd_mixer_selem_get_playback_switch (adev_ctx[num]->mixer_elm, SND_MIXER_SCHN_FRONT_RIGHT, &mute); 
162
163     }
164
165     return (unsigned char)!mute;
166 }
167
168 PUBLIC void _alsa_set_mute (unsigned int num, unsigned char mute) {
169
170     if (!adev_ctx || !adev_ctx[num] || !adev_ctx[num]->mixer_elm || 1 < mute < 0)
171         return;
172
173     if (snd_mixer_selem_has_playback_switch (adev_ctx[num]->mixer_elm))
174         snd_mixer_selem_set_playback_switch_all (adev_ctx[num]->mixer_elm, !mute);
175 }
176
177 PUBLIC void _alsa_set_rate (unsigned int num, unsigned int rate) {
178
179     if (!adev_ctx || !adev_ctx[num])
180         return;
181
182     snd_pcm_hw_params_set_rate_near (adev_ctx[num]->dev, adev_ctx[num]->params, &rate, 0);
183 }
184
185 PUBLIC void _alsa_set_channels (unsigned int num, unsigned int channels) {
186
187     if (!adev_ctx || !adev_ctx[num])
188         return;
189
190     snd_pcm_hw_params_set_channels (adev_ctx[num]->dev, adev_ctx[num]->params, channels);
191 }