26 | 26 |
#include <alsa/asoundlib.h>
|
27 | 27 |
#include <sys/mman.h>
|
28 | 28 |
#include <malloc.h>
|
|
29 |
#include <math.h>
|
29 | 30 |
|
30 | 31 |
#define MAX_DEVICE_LEN 128
|
31 | 32 |
|
|
48 | 49 |
bool mmap;
|
49 | 50 |
bool reopen;
|
50 | 51 |
u8_t *write_buf;
|
|
52 |
const char *volume_mixer;
|
51 | 53 |
} alsa;
|
52 | 54 |
|
53 | 55 |
static snd_pcm_t *pcmp = NULL;
|
|
90 | 92 |
snd_device_name_free_hint(hints);
|
91 | 93 |
}
|
92 | 94 |
printf("\n");
|
|
95 |
}
|
|
96 |
|
|
97 |
void list_mixers(const char *output_device) {
|
|
98 |
int err;
|
|
99 |
snd_mixer_t *handle;
|
|
100 |
snd_mixer_selem_id_t *sid;
|
|
101 |
snd_mixer_elem_t *elem;
|
|
102 |
snd_mixer_selem_id_alloca(&sid);
|
|
103 |
|
|
104 |
LOG_INFO("listing mixers for: %s", output_device);
|
|
105 |
|
|
106 |
if ((err = snd_mixer_open(&handle, 0)) < 0) {
|
|
107 |
LOG_ERROR("open error: %s", snd_strerror(err));
|
|
108 |
return;
|
|
109 |
}
|
|
110 |
if ((err = snd_mixer_attach(handle, output_device)) < 0) {
|
|
111 |
LOG_ERROR("attach error: %s", snd_strerror(err));
|
|
112 |
snd_mixer_close(handle);
|
|
113 |
return;
|
|
114 |
}
|
|
115 |
if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
|
|
116 |
LOG_ERROR("register error: %s", snd_strerror(err));
|
|
117 |
snd_mixer_close(handle);
|
|
118 |
return;
|
|
119 |
}
|
|
120 |
if ((err = snd_mixer_load(handle)) < 0) {
|
|
121 |
LOG_ERROR("load error: %s", snd_strerror(err));
|
|
122 |
snd_mixer_close(handle);
|
|
123 |
return;
|
|
124 |
}
|
|
125 |
|
|
126 |
printf("Volume controls for %s\n", output_device);
|
|
127 |
for (elem = snd_mixer_first_elem(handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
128 |
if (snd_mixer_selem_has_playback_volume(elem)) {
|
|
129 |
snd_mixer_selem_get_id(elem, sid);
|
|
130 |
printf(" %s\n", snd_mixer_selem_id_get_name(sid));//, snd_mixer_selem_id_get_index(sid));
|
|
131 |
}
|
|
132 |
}
|
|
133 |
printf("\n");
|
|
134 |
|
|
135 |
snd_mixer_close(handle);
|
|
136 |
}
|
|
137 |
|
|
138 |
#define MINVOL_DB 72 // LMS volume map for SqueezePlay sends values in range ~ -72..0 dB
|
|
139 |
|
|
140 |
void set_volume(unsigned left, unsigned right) {
|
|
141 |
int err;
|
|
142 |
float ldB, rdB;
|
|
143 |
long nleft, nright;
|
|
144 |
long min, max;
|
|
145 |
snd_mixer_t *handle;
|
|
146 |
snd_mixer_selem_id_t *sid;
|
|
147 |
snd_mixer_elem_t* elem;
|
|
148 |
|
|
149 |
if (!alsa.volume_mixer) {
|
|
150 |
LOG_DEBUG("setting internal gain left: %u right: %u", left, right);
|
|
151 |
LOCK;
|
|
152 |
output.gainL = left;
|
|
153 |
output.gainR = right;
|
|
154 |
UNLOCK;
|
|
155 |
return;
|
|
156 |
} else {
|
|
157 |
LOCK;
|
|
158 |
output.gainL = FIXED_ONE;
|
|
159 |
output.gainR = FIXED_ONE;
|
|
160 |
UNLOCK;
|
|
161 |
}
|
|
162 |
|
|
163 |
// convert 16.16 fixed point to dB
|
|
164 |
ldB = 20 * log10( left / 65536.0F );
|
|
165 |
rdB = 20 * log10( right / 65536.0F );
|
|
166 |
|
|
167 |
if ((err = snd_mixer_open(&handle, 0)) < 0) {
|
|
168 |
LOG_ERROR("open error: %s", snd_strerror(err));
|
|
169 |
return;
|
|
170 |
}
|
|
171 |
if ((err = snd_mixer_attach(handle, output.device)) < 0) {
|
|
172 |
LOG_ERROR("attach error: %s", snd_strerror(err));
|
|
173 |
snd_mixer_close(handle);
|
|
174 |
return;
|
|
175 |
}
|
|
176 |
if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
|
|
177 |
LOG_ERROR("register error: %s", snd_strerror(err));
|
|
178 |
snd_mixer_close(handle);
|
|
179 |
return;
|
|
180 |
}
|
|
181 |
if ((err = snd_mixer_load(handle)) < 0) {
|
|
182 |
LOG_ERROR("load error: %s", snd_strerror(err));
|
|
183 |
snd_mixer_close(handle);
|
|
184 |
return;
|
|
185 |
}
|
|
186 |
|
|
187 |
snd_mixer_selem_id_alloca(&sid);
|
|
188 |
|
|
189 |
snd_mixer_selem_id_set_index(sid, 0);
|
|
190 |
snd_mixer_selem_id_set_name(sid, alsa.volume_mixer);
|
|
191 |
|
|
192 |
if ((elem = snd_mixer_find_selem(handle, sid)) == NULL) {
|
|
193 |
LOG_ERROR("error find selem %s: %s", alsa.volume_mixer, snd_strerror(err));
|
|
194 |
snd_mixer_close(handle);
|
|
195 |
return;
|
|
196 |
}
|
|
197 |
|
|
198 |
if (snd_mixer_selem_has_playback_switch(elem)) {
|
|
199 |
snd_mixer_selem_set_playback_switch_all(elem, 1); // unmute
|
|
200 |
}
|
|
201 |
|
|
202 |
if ((err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max)) < 0) {
|
|
203 |
// unable to get db range - set using raw values
|
|
204 |
if ((err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max)) < 0) {
|
|
205 |
LOG_ERROR("unable to get volume raw", min, max);
|
|
206 |
} else {
|
|
207 |
long lraw = (MINVOL_DB + floor(ldB)) / MINVOL_DB * max;
|
|
208 |
long rraw = (MINVOL_DB + floor(rdB)) / MINVOL_DB * max;
|
|
209 |
LOG_DEBUG("setting vol raw [%ld-%ld]", min, max);
|
|
210 |
if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, lraw)) < 0) {
|
|
211 |
LOG_ERROR("error setting left volume: %s", snd_strerror(err));
|
|
212 |
}
|
|
213 |
if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, rraw)) < 0) {
|
|
214 |
LOG_ERROR("error setting right volume: %s", snd_strerror(err));
|
|
215 |
}
|
|
216 |
}
|
|
217 |
} else {
|
|
218 |
// set db directly
|
|
219 |
LOG_DEBUG("setting vol dB");
|
|
220 |
if ((err = snd_mixer_selem_set_playback_dB(elem, SND_MIXER_SCHN_FRONT_LEFT, 100 * ldB, 1)) < 0) {
|
|
221 |
LOG_ERROR("error setting left volume: %s", snd_strerror(err));
|
|
222 |
}
|
|
223 |
if ((err = snd_mixer_selem_set_playback_dB(elem, SND_MIXER_SCHN_FRONT_RIGHT, 100 * rdB, 1)) < 0) {
|
|
224 |
LOG_ERROR("error setting right volume: %s", snd_strerror(err));
|
|
225 |
}
|
|
226 |
}
|
|
227 |
|
|
228 |
if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &nleft)) < 0) {
|
|
229 |
LOG_ERROR("error getting left vol: %s", snd_strerror(err));
|
|
230 |
}
|
|
231 |
if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &nright)) < 0) {
|
|
232 |
LOG_ERROR("error getting right vol: %s", snd_strerror(err));
|
|
233 |
}
|
|
234 |
|
|
235 |
LOG_DEBUG("%s left: %3.1fdB -> %ld right: %3.1fdB -> %ld", alsa.volume_mixer, ldB, nleft, rdB, nright);
|
|
236 |
|
|
237 |
snd_mixer_close(handle);
|
93 | 238 |
}
|
94 | 239 |
|
95 | 240 |
static void *alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...) {
|
|
618 | 763 |
static pthread_t thread;
|
619 | 764 |
|
620 | 765 |
void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[],
|
621 | |
unsigned rate_delay, unsigned rt_priority, unsigned idle) {
|
|
766 |
unsigned rate_delay, unsigned rt_priority, unsigned idle, const char *volume_mixer) {
|
622 | 767 |
|
623 | 768 |
unsigned alsa_buffer = ALSA_BUFFER_TIME;
|
624 | 769 |
unsigned alsa_period = ALSA_PERIOD_COUNT;
|
|
648 | 793 |
alsa.write_buf = NULL;
|
649 | 794 |
alsa.format = 0;
|
650 | 795 |
alsa.reopen = alsa_reopen;
|
|
796 |
alsa.volume_mixer = volume_mixer;
|
651 | 797 |
output.format = 0;
|
652 | 798 |
output.buffer = alsa_buffer;
|
653 | 799 |
output.period = alsa_period;
|