Codebase list squeezelite / 2f31c9b
add support of unmute of alsa mixer Adrian Smith 9 years ago
4 changed file(s) with 105 addition(s) and 80 deletion(s). Raw diff Collapse all Expand all
132132 Features
133133 - support for closing output device when idle with -C option
134134 - add support for basic IR input using LIRC on Linux
135 - add support for volume adjustment via alsa mixer
135 - add support for volume adjustment or unmuting of alsa mixer
9797 #endif
9898 # if ALSA
9999 " -L \t\t\tList volume controls for output device\n"
100 " -U <control>\t\tUnmute ALSA control and set to full volume\n"
100101 " -V <control>\t\tUse ALSA control for volume adjustment, otherwise use software volume adjustment\n"
101102 #endif
102103 #if LINUX || FREEBSD
210211 #if ALSA
211212 unsigned rt_priority = OUTPUT_RT_PRIORITY;
212213 char *output_mixer = NULL;
214 bool output_mixer_unmute = false;
213215 #endif
214216 #if DSD
215217 bool dop = false;
248250 char *opt = argv[optind] + 1;
249251 if (strstr("oabcCdefmMnNpPrs"
250252 #if ALSA
251 "V"
253 "UV"
252254 #endif
253255 , opt) && optind < argc - 1) {
254256 optarg = argv[optind + 1];
452454 break;
453455 #endif
454456 #if ALSA
457 case 'U':
458 output_mixer_unmute = true;
455459 case 'V':
456460 output_mixer = optarg;
457461 break;
553557 output_init_stdout(log_output, output_buf_size, output_params, rates, rate_delay);
554558 } else {
555559 #if ALSA
556 output_init_alsa(log_output, output_device, output_buf_size, output_params, rates, rate_delay, rt_priority, idle, output_mixer);
560 output_init_alsa(log_output, output_device, output_buf_size, output_params, rates, rate_delay, rt_priority, idle, output_mixer,
561 output_mixer_unmute);
557562 #endif
558563 #if PORTAUDIO
559564 output_init_pa(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle);
142142
143143 #define MINVOL_DB 72 // LMS volume map for SqueezePlay sends values in range ~ -72..0 dB
144144
145 void set_volume(unsigned left, unsigned right) {
145 static void set_mixer(const char *device, const char *mixer, int mixer_index, bool setmax, float ldB, float rdB) {
146146 int err;
147 float ldB, rdB;
148147 long nleft, nright;
149148 long min, max;
150149 snd_mixer_t *handle;
151150 snd_mixer_selem_id_t *sid;
152151 snd_mixer_elem_t* elem;
152
153 if ((err = snd_mixer_open(&handle, 0)) < 0) {
154 LOG_ERROR("open error: %s", snd_strerror(err));
155 return;
156 }
157 if ((err = snd_mixer_attach(handle, device)) < 0) {
158 LOG_ERROR("attach error: %s", snd_strerror(err));
159 snd_mixer_close(handle);
160 return;
161 }
162 if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
163 LOG_ERROR("register error: %s", snd_strerror(err));
164 snd_mixer_close(handle);
165 return;
166 }
167 if ((err = snd_mixer_load(handle)) < 0) {
168 LOG_ERROR("load error: %s", snd_strerror(err));
169 snd_mixer_close(handle);
170 return;
171 }
172
173 snd_mixer_selem_id_alloca(&sid);
174
175 snd_mixer_selem_id_set_index(sid, mixer_index);
176 snd_mixer_selem_id_set_name(sid, mixer);
177
178 if ((elem = snd_mixer_find_selem(handle, sid)) == NULL) {
179 LOG_ERROR("error find selem %s", mixer);
180 snd_mixer_close(handle);
181 return;
182 }
183
184 if (snd_mixer_selem_has_playback_switch(elem)) {
185 snd_mixer_selem_set_playback_switch_all(elem, 1); // unmute
186 }
187
188 err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max);
189
190 if (err < 0 || max - min < 1000 || setmax) {
191 // unable to get db range or range is less than 10dB - ignore and set using raw values
192 if ((err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max)) < 0) {
193 LOG_ERROR("unable to get volume raw range");
194 } else {
195 long lraw, rraw;
196 if (setmax) {
197 lraw = rraw = max;
198 } else {
199 lraw = ((ldB > -MINVOL_DB ? MINVOL_DB + floor(ldB) : 0) / MINVOL_DB * (max-min)) + min;
200 rraw = ((rdB > -MINVOL_DB ? MINVOL_DB + floor(rdB) : 0) / MINVOL_DB * (max-min)) + min;
201 }
202 LOG_DEBUG("setting vol raw [%ld..%ld]", min, max);
203 if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, lraw)) < 0) {
204 LOG_ERROR("error setting left volume: %s", snd_strerror(err));
205 }
206 if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, rraw)) < 0) {
207 LOG_ERROR("error setting right volume: %s", snd_strerror(err));
208 }
209 }
210 } else {
211 // set db directly
212 LOG_DEBUG("setting vol dB [%ld..%ld]", min, max);
213 if ((err = snd_mixer_selem_set_playback_dB(elem, SND_MIXER_SCHN_FRONT_LEFT, 100 * ldB + max, 1)) < 0) {
214 LOG_ERROR("error setting left volume: %s", snd_strerror(err));
215 }
216 if ((err = snd_mixer_selem_set_playback_dB(elem, SND_MIXER_SCHN_FRONT_RIGHT, 100 * rdB + max, 1)) < 0) {
217 LOG_ERROR("error setting right volume: %s", snd_strerror(err));
218 }
219 }
220
221 if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &nleft)) < 0) {
222 LOG_ERROR("error getting left vol: %s", snd_strerror(err));
223 }
224 if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &nright)) < 0) {
225 LOG_ERROR("error getting right vol: %s", snd_strerror(err));
226 }
227
228 LOG_DEBUG("%s left: %3.1fdB -> %ld right: %3.1fdB -> %ld", mixer, ldB, nleft, rdB, nright);
229
230 snd_mixer_close(handle);
231 }
232
233 void set_volume(unsigned left, unsigned right) {
234 float ldB, rdB;
153235
154236 if (!alsa.volume_mixer_name) {
155237 LOG_DEBUG("setting internal gain left: %u right: %u", left, right);
169251 ldB = 20 * log10( left / 65536.0F );
170252 rdB = 20 * log10( right / 65536.0F );
171253
172 if ((err = snd_mixer_open(&handle, 0)) < 0) {
173 LOG_ERROR("open error: %s", snd_strerror(err));
174 return;
175 }
176 if ((err = snd_mixer_attach(handle, output.device)) < 0) {
177 LOG_ERROR("attach error: %s", snd_strerror(err));
178 snd_mixer_close(handle);
179 return;
180 }
181 if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
182 LOG_ERROR("register error: %s", snd_strerror(err));
183 snd_mixer_close(handle);
184 return;
185 }
186 if ((err = snd_mixer_load(handle)) < 0) {
187 LOG_ERROR("load error: %s", snd_strerror(err));
188 snd_mixer_close(handle);
189 return;
190 }
191
192 snd_mixer_selem_id_alloca(&sid);
193
194 snd_mixer_selem_id_set_index(sid, alsa.volume_mixer_index);
195 snd_mixer_selem_id_set_name(sid, alsa.volume_mixer_name);
196
197 if ((elem = snd_mixer_find_selem(handle, sid)) == NULL) {
198 LOG_ERROR("error find selem %s", alsa.volume_mixer_name);
199 snd_mixer_close(handle);
200 return;
201 }
202
203 if (snd_mixer_selem_has_playback_switch(elem)) {
204 snd_mixer_selem_set_playback_switch_all(elem, 1); // unmute
205 }
206
207 if ((err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max)) < 0 || max - min < 1000) {
208 // unable to get db range or range is less than 10dB - ignore and set using raw values
209 if ((err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max)) < 0) {
210 LOG_ERROR("unable to get volume raw range");
211 } else {
212 long lraw = ((ldB > -MINVOL_DB ? MINVOL_DB + floor(ldB) : 0) / MINVOL_DB * (max-min)) + min;
213 long rraw = ((rdB > -MINVOL_DB ? MINVOL_DB + floor(rdB) : 0) / MINVOL_DB * (max-min)) + min;
214 LOG_DEBUG("setting vol raw [%ld..%ld]", min, max);
215 if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, lraw)) < 0) {
216 LOG_ERROR("error setting left volume: %s", snd_strerror(err));
217 }
218 if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, rraw)) < 0) {
219 LOG_ERROR("error setting right volume: %s", snd_strerror(err));
220 }
221 }
222 } else {
223 // set db directly
224 LOG_DEBUG("setting vol dB [%ld..%ld]", min, max);
225 if ((err = snd_mixer_selem_set_playback_dB(elem, SND_MIXER_SCHN_FRONT_LEFT, 100 * ldB + max, 1)) < 0) {
226 LOG_ERROR("error setting left volume: %s", snd_strerror(err));
227 }
228 if ((err = snd_mixer_selem_set_playback_dB(elem, SND_MIXER_SCHN_FRONT_RIGHT, 100 * rdB + max, 1)) < 0) {
229 LOG_ERROR("error setting right volume: %s", snd_strerror(err));
230 }
231 }
232
233 if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &nleft)) < 0) {
234 LOG_ERROR("error getting left vol: %s", snd_strerror(err));
235 }
236 if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &nright)) < 0) {
237 LOG_ERROR("error getting right vol: %s", snd_strerror(err));
238 }
239
240 LOG_DEBUG("%s left: %3.1fdB -> %ld right: %3.1fdB -> %ld", alsa.volume_mixer_name, ldB, nleft, rdB, nright);
241
242 snd_mixer_close(handle);
254 set_mixer(output.device, alsa.volume_mixer_name, alsa.volume_mixer_index, false, ldB, rdB);
243255 }
244256
245257 static void *alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...) {
768780 static pthread_t thread;
769781
770782 void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[],
771 unsigned rate_delay, unsigned rt_priority, unsigned idle, char *volume_mixer) {
783 unsigned rate_delay, unsigned rt_priority, unsigned idle, char *volume_mixer, bool mixer_unmute) {
772784
773785 unsigned alsa_buffer = ALSA_BUFFER_TIME;
774786 unsigned alsa_period = ALSA_PERIOD_COUNT;
801813 alsa.write_buf = NULL;
802814 alsa.format = 0;
803815 alsa.reopen = alsa_reopen;
804 alsa.volume_mixer_name = volume_mixer_name;
805 alsa.volume_mixer_index = volume_mixer_index ? atoi(volume_mixer_index) : 0;
816
817 if (!mixer_unmute) {
818 alsa.volume_mixer_name = volume_mixer_name;
819 alsa.volume_mixer_index = volume_mixer_index ? atoi(volume_mixer_index) : 0;
820 }
821
806822 output.format = 0;
807823 output.buffer = alsa_buffer;
808824 output.period = alsa_period;
823839 snd_lib_error_set_handler((snd_lib_error_handler_t)alsa_error_handler);
824840
825841 output_init_common(level, device, output_buf_size, rates, idle);
842
843 if (mixer_unmute && volume_mixer_name) {
844 set_mixer(output.device, volume_mixer_name, volume_mixer_index ? atoi(volume_mixer_index) : 0, true, 0, 0);
845 }
826846
827847 #if LINUX
828848 // RT linux - aim to avoid pagefaults by locking memory:
594594 void set_volume(unsigned left, unsigned right);
595595 bool test_open(const char *device, unsigned rates[]);
596596 void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[],
597 unsigned rate_delay, unsigned rt_priority, unsigned idle, char *volume_mixer);
597 unsigned rate_delay, unsigned rt_priority, unsigned idle, char *volume_mixer, bool mixer_unmute);
598598 void output_close_alsa(void);
599599 #endif
600600